Merge pull request '[gitea] week 15 cherry pick' (#3091) from algernon/forgejo:wcp/week-15 into forgejo
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/3091 Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
This commit is contained in:
commit
26fc7c3461
117 changed files with 3706 additions and 4312 deletions
|
@ -24,6 +24,7 @@
|
|||
--repo-header-issue-min-height: 41px;
|
||||
--min-height-textarea: 132px; /* padding + 6 lines + border = calc(1.57142em + 6lh + 2px), but lh is not fully supported */
|
||||
--tab-size: 4;
|
||||
--checkbox-size: 16px; /* height and width of checkbox and radio inputs */
|
||||
}
|
||||
|
||||
:root * {
|
||||
|
@ -44,7 +45,7 @@ html, body {
|
|||
}
|
||||
|
||||
body {
|
||||
line-height: 1.4285rem;
|
||||
line-height: 20px;
|
||||
font-family: var(--fonts-regular);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-body);
|
||||
|
@ -316,61 +317,6 @@ a.label,
|
|||
background-color: var(--color-label-bg);
|
||||
}
|
||||
|
||||
/* fix Fomantic's line-height cutting off "g" on Windows Chrome with Segoe UI */
|
||||
.ui.input > input {
|
||||
line-height: var(--line-height-default);
|
||||
text-align: start; /* Override fomantic's `text-align: left` to make RTL work via HTML `dir="auto"` */
|
||||
}
|
||||
|
||||
/* fix Fomantic's line-height causing vertical scrollbars to appear */
|
||||
ul.ui.list li,
|
||||
ol.ui.list li,
|
||||
.ui.list > .item,
|
||||
.ui.list .list > .item {
|
||||
line-height: var(--line-height-default);
|
||||
}
|
||||
|
||||
.ui.input.focus > input,
|
||||
.ui.input > input:focus {
|
||||
border-color: var(--color-primary);
|
||||
}
|
||||
|
||||
.ui.action.input .ui.ui.button {
|
||||
border-color: var(--color-input-border);
|
||||
padding-top: 0; /* the ".action.input" is "flex + stretch", so let the buttons layout themselves */
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
/* currently used for search bar dropdowns in repo search and explore code */
|
||||
.ui.action.input:not([class*="left action"]) > .ui.dropdown.selection {
|
||||
min-width: 10em;
|
||||
}
|
||||
.ui.action.input:not([class*="left action"]) > .ui.dropdown.selection:not(:focus) {
|
||||
border-right: none;
|
||||
}
|
||||
.ui.action.input:not([class*="left action"]) > .ui.dropdown.selection:not(.active):hover {
|
||||
border-color: var(--color-input-border);
|
||||
}
|
||||
.ui.action.input:not([class*="left action"]) .ui.dropdown.selection.upward.visible {
|
||||
border-bottom-left-radius: 0 !important;
|
||||
border-bottom-right-radius: 0 !important;
|
||||
}
|
||||
.ui.action.input:not([class*="left action"]) > input,
|
||||
.ui.action.input:not([class*="left action"]) > input:hover {
|
||||
border-right: none;
|
||||
}
|
||||
.ui.action.input:not([class*="left action"]) > input:focus + .ui.dropdown.selection,
|
||||
.ui.action.input:not([class*="left action"]) > input:focus + .ui.dropdown.selection:hover,
|
||||
.ui.action.input:not([class*="left action"]) > input:focus + .button,
|
||||
.ui.action.input:not([class*="left action"]) > input:focus + .button:hover,
|
||||
.ui.action.input:not([class*="left action"]) > input:focus + .icon + .button,
|
||||
.ui.action.input:not([class*="left action"]) > input:focus + .icon + .button:hover {
|
||||
border-left-color: var(--color-primary);
|
||||
}
|
||||
.ui.action.input:not([class*="left action"]) > input:focus {
|
||||
border-right-color: var(--color-primary);
|
||||
}
|
||||
|
||||
.ui.menu {
|
||||
display: flex;
|
||||
}
|
||||
|
@ -514,21 +460,6 @@ ol.ui.list li,
|
|||
color: var(--color-text-light-2);
|
||||
}
|
||||
|
||||
.ui.list .list > .item .header,
|
||||
.ui.list > .item .header {
|
||||
color: var(--color-text-dark);
|
||||
}
|
||||
|
||||
.ui.list .list > .item > .content,
|
||||
.ui.list > .item > .content {
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.ui.list .list > .item .description,
|
||||
.ui.list > .item .description {
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
/* replace item margin on secondary menu items with gap and remove both the
|
||||
negative margins on the menu as well as margin on the items */
|
||||
.ui.secondary.menu {
|
||||
|
@ -647,10 +578,6 @@ img.ui.avatar,
|
|||
aspect-ratio: 1;
|
||||
}
|
||||
|
||||
.ui.divided.list > .item {
|
||||
border-color: var(--color-secondary);
|
||||
}
|
||||
|
||||
.ui.error.message .header,
|
||||
.ui.warning.message .header {
|
||||
color: inherit;
|
||||
|
@ -1457,11 +1384,6 @@ table th[data-sortt-desc] .svg {
|
|||
vertical-align: -0.15em;
|
||||
}
|
||||
|
||||
/* for the jquery.minicolors plugin */
|
||||
.minicolors-panel {
|
||||
background: var(--color-secondary-dark-1) !important;
|
||||
}
|
||||
|
||||
.ui.tabular.menu {
|
||||
border-color: var(--color-secondary);
|
||||
}
|
||||
|
@ -1625,16 +1547,6 @@ table th[data-sortt-desc] .svg {
|
|||
align-items: stretch;
|
||||
}
|
||||
|
||||
.ui.ui.icon.input .icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.ui.icon.input > i.icon {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.flex-items-block > .item,
|
||||
.flex-text-block {
|
||||
display: flex;
|
||||
|
|
47
web_src/css/features/colorpicker.css
Normal file
47
web_src/css/features/colorpicker.css
Normal file
|
@ -0,0 +1,47 @@
|
|||
.js-color-picker-input {
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.js-color-picker-input input {
|
||||
padding-top: 8px !important;
|
||||
padding-bottom: 8px !important;
|
||||
padding-left: 32px !important;
|
||||
}
|
||||
|
||||
.js-color-picker-input .preview-square {
|
||||
position: absolute;
|
||||
aspect-ratio: 1;
|
||||
height: 16px;
|
||||
left: 10px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
border-radius: 2px;
|
||||
background: repeating-linear-gradient(45deg, #aaa 25%, transparent 25%, transparent 75%, #aaa 75%, #aaa), repeating-linear-gradient(45deg, #aaa 25%, #fff 25%, #fff 75%, #aaa 75%, #aaa); /* stylelint-disable-line scale-unlimited/declaration-strict-value */
|
||||
background-position: 0 0, 4px 4px;
|
||||
background-size: 8px 8px;
|
||||
}
|
||||
|
||||
.js-color-picker-input .preview-square::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: inherit;
|
||||
background-color: currentcolor;
|
||||
}
|
||||
|
||||
hex-color-picker {
|
||||
width: 180px;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
hex-color-picker::part(hue-pointer),
|
||||
hex-color-picker::part(saturation-pointer) {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
hex-color-picker::part(hue) {
|
||||
flex-basis: 16px;
|
||||
}
|
|
@ -102,26 +102,3 @@
|
|||
.card-ghost * {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.color-field .minicolors.minicolors-theme-default {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.color-field .minicolors.minicolors-theme-default .minicolors-input {
|
||||
height: 38px;
|
||||
padding-left: 2rem;
|
||||
}
|
||||
|
||||
.color-field .minicolors.minicolors-theme-default .minicolors-swatch {
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
.edit-project-column-modal .color.picker.column,
|
||||
.new-project-column-modal .color.picker.column {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.edit-project-column-modal .color.picker.column .minicolors,
|
||||
.new-project-column-modal .color.picker.column .minicolors {
|
||||
flex: 1;
|
||||
}
|
||||
|
|
|
@ -32,10 +32,7 @@ textarea,
|
|||
.ui.form input[type="text"],
|
||||
.ui.form input[type="time"],
|
||||
.ui.form input[type="url"],
|
||||
.ui.selection.dropdown,
|
||||
.ui.checkbox label::before,
|
||||
.ui.checkbox input:checked ~ label::before,
|
||||
.ui.checkbox input:not([type="radio"]):indeterminate ~ label::before {
|
||||
.ui.selection.dropdown {
|
||||
background: var(--color-input-background);
|
||||
border-color: var(--color-input-border);
|
||||
color: var(--color-input-text);
|
||||
|
@ -63,12 +60,7 @@ textarea:hover,
|
|||
.ui.form input[type="text"]:hover,
|
||||
.ui.form input[type="time"]:hover,
|
||||
.ui.form input[type="url"]:hover,
|
||||
.ui.selection.dropdown:hover,
|
||||
.ui.checkbox label:hover::before,
|
||||
.ui.checkbox label:active::before,
|
||||
.ui.radio.checkbox label::after,
|
||||
.ui.radio.checkbox input:focus ~ label::before,
|
||||
.ui.radio.checkbox input:checked ~ label::before {
|
||||
.ui.selection.dropdown:hover {
|
||||
background: var(--color-input-background);
|
||||
border-color: var(--color-input-border-hover);
|
||||
color: var(--color-input-text);
|
||||
|
@ -91,11 +83,7 @@ textarea:focus,
|
|||
.ui.form input[type="text"]:focus,
|
||||
.ui.form input[type="time"]:focus,
|
||||
.ui.form input[type="url"]:focus,
|
||||
.ui.selection.dropdown:focus,
|
||||
.ui.checkbox input:focus ~ label::before,
|
||||
.ui.checkbox input:not([type="radio"]):indeterminate:focus ~ label::before,
|
||||
.ui.checkbox input:checked:focus ~ label::before,
|
||||
.ui.radio.checkbox input:focus:checked ~ label::before {
|
||||
.ui.selection.dropdown:focus {
|
||||
background: var(--color-input-background);
|
||||
border-color: var(--color-primary);
|
||||
color: var(--color-input-text);
|
||||
|
@ -106,58 +94,21 @@ textarea:focus,
|
|||
.ui.form .inline.fields .field > label,
|
||||
.ui.form .inline.fields .field > p,
|
||||
.ui.form .inline.field > label,
|
||||
.ui.form .inline.field > p,
|
||||
.ui.checkbox label,
|
||||
.ui.checkbox + label,
|
||||
.ui.checkbox label:hover,
|
||||
.ui.checkbox + label:hover,
|
||||
.ui.checkbox input:focus ~ label,
|
||||
.ui.checkbox input:active ~ label {
|
||||
.ui.form .inline.field > p {
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.ui.form .required.fields:not(.grouped) > .field > label::after,
|
||||
.ui.form .required.fields.grouped > label::after,
|
||||
.ui.form .required.field > label::after,
|
||||
.ui.form .required.fields:not(.grouped) > .field > .checkbox::after,
|
||||
.ui.form .required.field > .checkbox::after,
|
||||
.ui.form label.required::after {
|
||||
color: var(--color-red);
|
||||
}
|
||||
|
||||
.ui.input,
|
||||
.ui.checkbox input:focus ~ label::after,
|
||||
.ui.checkbox input:checked ~ label::after,
|
||||
.ui.checkbox label:active::after,
|
||||
.ui.checkbox input:not([type="radio"]):indeterminate ~ label::after,
|
||||
.ui.checkbox input:not([type="radio"]):indeterminate:focus ~ label::after,
|
||||
.ui.checkbox input:checked:focus ~ label::after,
|
||||
.ui.disabled.checkbox label,
|
||||
.ui.checkbox input[disabled] ~ label {
|
||||
.ui.input {
|
||||
color: var(--color-input-text);
|
||||
}
|
||||
|
||||
.ui.radio.checkbox input:focus ~ label::after,
|
||||
.ui.radio.checkbox input:checked ~ label::after,
|
||||
.ui.radio.checkbox input:focus:checked ~ label::after {
|
||||
background: var(--color-input-text);
|
||||
}
|
||||
|
||||
.ui.toggle.checkbox label::before {
|
||||
background: var(--color-input-toggle-background);
|
||||
}
|
||||
|
||||
.ui.toggle.checkbox label,
|
||||
.ui.toggle.checkbox input:checked ~ label,
|
||||
.ui.toggle.checkbox input:focus:checked ~ label {
|
||||
color: var(--color-text) !important;
|
||||
}
|
||||
|
||||
.ui.toggle.checkbox input:checked ~ label::before,
|
||||
.ui.toggle.checkbox input:focus:checked ~ label::before {
|
||||
background: var(--color-primary) !important;
|
||||
}
|
||||
|
||||
/* match <select> padding to <input> */
|
||||
.ui.form select {
|
||||
padding: 0.67857143em 1em;
|
||||
|
|
|
@ -63,3 +63,20 @@ only use:
|
|||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-size-1 { tab-size: 1 !important; }
|
||||
.tab-size-2 { tab-size: 2 !important; }
|
||||
.tab-size-3 { tab-size: 3 !important; }
|
||||
.tab-size-4 { tab-size: 4 !important; }
|
||||
.tab-size-5 { tab-size: 5 !important; }
|
||||
.tab-size-6 { tab-size: 6 !important; }
|
||||
.tab-size-7 { tab-size: 7 !important; }
|
||||
.tab-size-8 { tab-size: 8 !important; }
|
||||
.tab-size-9 { tab-size: 9 !important; }
|
||||
.tab-size-10 { tab-size: 10 !important; }
|
||||
.tab-size-11 { tab-size: 11 !important; }
|
||||
.tab-size-12 { tab-size: 12 !important; }
|
||||
.tab-size-13 { tab-size: 13 !important; }
|
||||
.tab-size-14 { tab-size: 14 !important; }
|
||||
.tab-size-15 { tab-size: 15 !important; }
|
||||
.tab-size-16 { tab-size: 16 !important; }
|
||||
|
|
|
@ -6,12 +6,15 @@
|
|||
@import "./modules/container.css";
|
||||
@import "./modules/divider.css";
|
||||
@import "./modules/header.css";
|
||||
@import "./modules/input.css";
|
||||
@import "./modules/label.css";
|
||||
@import "./modules/list.css";
|
||||
@import "./modules/segment.css";
|
||||
@import "./modules/grid.css";
|
||||
@import "./modules/message.css";
|
||||
@import "./modules/table.css";
|
||||
@import "./modules/card.css";
|
||||
@import "./modules/checkbox.css";
|
||||
@import "./modules/modal.css";
|
||||
|
||||
@import "./modules/select.css";
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
.is-loading {
|
||||
pointer-events: none !important;
|
||||
position: relative !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
.is-loading > * {
|
||||
|
@ -35,10 +34,14 @@
|
|||
border-radius: var(--border-radius-circle);
|
||||
}
|
||||
|
||||
.is-loading.small-loading-icon::after {
|
||||
.is-loading.loading-icon-2px::after {
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
.is-loading.loading-icon-3px::after {
|
||||
border-width: 3px;
|
||||
}
|
||||
|
||||
/* for single form button, the loading state should be on the button, but not go semi-transparent, just replace the text on the button with the loader. */
|
||||
form.single-button-form.is-loading > * {
|
||||
opacity: 1;
|
||||
|
@ -63,7 +66,7 @@ form.single-button-form.is-loading .button {
|
|||
background: transparent;
|
||||
}
|
||||
|
||||
/* TODO: not needed, use "is-loading small-loading-icon" instead */
|
||||
/* TODO: not needed, use "is-loading loading-icon-2px" instead */
|
||||
code.language-math.is-loading::after {
|
||||
padding: 0;
|
||||
border-width: 2px;
|
||||
|
|
120
web_src/css/modules/checkbox.css
Normal file
120
web_src/css/modules/checkbox.css
Normal file
|
@ -0,0 +1,120 @@
|
|||
/* based on Fomantic UI checkbox module, with just the parts extracted that we use. If you find any
|
||||
unused rules here after refactoring, please remove them. */
|
||||
|
||||
input[type="checkbox"],
|
||||
input[type="radio"] {
|
||||
width: var(--checkbox-size);
|
||||
height: var(--checkbox-size);
|
||||
}
|
||||
|
||||
.ui.checkbox {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
vertical-align: baseline;
|
||||
min-height: var(--checkbox-size);
|
||||
line-height: var(--checkbox-size);
|
||||
min-width: var(--checkbox-size);
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
.ui.checkbox input[type="checkbox"],
|
||||
.ui.checkbox input[type="radio"] {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: var(--checkbox-size);
|
||||
height: var(--checkbox-size);
|
||||
}
|
||||
|
||||
.ui.checkbox input[type="checkbox"]:enabled,
|
||||
.ui.checkbox input[type="radio"]:enabled,
|
||||
.ui.checkbox label:enabled {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ui.checkbox label {
|
||||
cursor: auto;
|
||||
position: relative;
|
||||
display: block;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.ui.checkbox label,
|
||||
.ui.radio.checkbox label {
|
||||
margin-left: 1.85714em;
|
||||
}
|
||||
|
||||
.ui.checkbox + label {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.ui.disabled.checkbox label,
|
||||
.ui.checkbox input[disabled] ~ label {
|
||||
cursor: default !important;
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.ui.radio.checkbox {
|
||||
min-height: var(--checkbox-size);
|
||||
}
|
||||
|
||||
/* "switch" styled checkbox */
|
||||
|
||||
.ui.toggle.checkbox {
|
||||
min-height: 1.5rem;
|
||||
}
|
||||
.ui.toggle.checkbox input {
|
||||
width: 3.5rem;
|
||||
height: 1.5rem;
|
||||
opacity: 0;
|
||||
z-index: 3;
|
||||
}
|
||||
.ui.toggle.checkbox label {
|
||||
min-height: 1.5rem;
|
||||
padding-left: 4.5rem;
|
||||
padding-top: 0.15em;
|
||||
}
|
||||
.ui.toggle.checkbox label::before {
|
||||
display: block;
|
||||
position: absolute;
|
||||
content: "";
|
||||
z-index: 1;
|
||||
top: 0;
|
||||
width: 3.5rem;
|
||||
height: 1.5rem;
|
||||
border-radius: 500rem;
|
||||
left: 0;
|
||||
}
|
||||
.ui.toggle.checkbox label::after {
|
||||
background: var(--color-white);
|
||||
position: absolute;
|
||||
content: "";
|
||||
opacity: 1;
|
||||
z-index: 2;
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border-radius: 500rem;
|
||||
transition: background 0.3s ease, left 0.3s ease;
|
||||
}
|
||||
.ui.toggle.checkbox input ~ label::after {
|
||||
left: -0.05rem;
|
||||
}
|
||||
.ui.toggle.checkbox input:checked ~ label::after {
|
||||
left: 2.15rem;
|
||||
}
|
||||
.ui.toggle.checkbox input:focus ~ label::before,
|
||||
.ui.toggle.checkbox label::before {
|
||||
background: var(--color-input-toggle-background);
|
||||
}
|
||||
.ui.toggle.checkbox label,
|
||||
.ui.toggle.checkbox input:checked ~ label,
|
||||
.ui.toggle.checkbox input:focus:checked ~ label {
|
||||
color: var(--color-text) !important;
|
||||
}
|
||||
.ui.toggle.checkbox input:checked ~ label::before,
|
||||
.ui.toggle.checkbox input:focus:checked ~ label::before {
|
||||
background: var(--color-primary) !important;
|
||||
}
|
|
@ -135,6 +135,12 @@ h4.ui.header .sub.header {
|
|||
font-weight: var(--font-weight-normal);
|
||||
}
|
||||
|
||||
/* open dropdown menus to the left in right-attached headers */
|
||||
.ui.attached.header > .ui.right .ui.dropdown .menu {
|
||||
right: 0;
|
||||
left: auto;
|
||||
}
|
||||
|
||||
/* if a .top.attached.header is followed by a .segment, add some margin */
|
||||
.ui.segments + .ui.top.attached.header,
|
||||
.ui.attached.segment + .ui.top.attached.header {
|
||||
|
|
197
web_src/css/modules/input.css
Normal file
197
web_src/css/modules/input.css
Normal file
|
@ -0,0 +1,197 @@
|
|||
/* based on Fomantic UI input module, with just the parts extracted that we use. If you find any
|
||||
unused rules here after refactoring, please remove them. */
|
||||
|
||||
.ui.input {
|
||||
position: relative;
|
||||
font-weight: var(--font-weight-normal);
|
||||
display: inline-flex;
|
||||
color: var(--color-input-text);
|
||||
}
|
||||
.ui.input > input {
|
||||
margin: 0;
|
||||
max-width: 100%;
|
||||
flex: 1 0 auto;
|
||||
outline: none;
|
||||
font-family: var(--fonts-regular);
|
||||
padding: 0.67857143em 1em;
|
||||
border: 1px solid var(--color-input-border);
|
||||
color: var(--color-input-text);
|
||||
border-radius: 0.28571429rem;
|
||||
line-height: var(--line-height-default);
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
.ui.disabled.input,
|
||||
.ui.input:not(.disabled) input[disabled] {
|
||||
opacity: var(--opacity-disabled);
|
||||
}
|
||||
.ui.disabled.input > input,
|
||||
.ui.input:not(.disabled) input[disabled] {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.ui.input.focus > input,
|
||||
.ui.input > input:focus {
|
||||
border-color: var(--color-primary);
|
||||
}
|
||||
|
||||
.ui.input.error > input {
|
||||
background: var(--color-error-bg);
|
||||
border-color: var(--color-error-border);
|
||||
color: var(--color-error-text);
|
||||
}
|
||||
|
||||
.ui.icon.input > i.icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: default;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
top: 0;
|
||||
right: 0;
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
width: 2.67142857em;
|
||||
opacity: 0.5;
|
||||
border-radius: 0 0.28571429rem 0.28571429rem 0;
|
||||
pointer-events: none;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.ui.icon.input > i.icon.is-loading {
|
||||
position: absolute !important;
|
||||
height: 28px;
|
||||
top: 4px;
|
||||
}
|
||||
|
||||
.ui.icon.input > i.icon.is-loading > * {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.ui.ui.ui.ui.icon.input > textarea,
|
||||
.ui.ui.ui.ui.icon.input > input {
|
||||
padding-right: 2.67142857em;
|
||||
}
|
||||
.ui.icon.input > i.link.icon {
|
||||
cursor: pointer;
|
||||
}
|
||||
.ui.icon.input > i.circular.icon {
|
||||
top: 0.35em;
|
||||
right: 0.5em;
|
||||
}
|
||||
|
||||
.ui[class*="left icon"].input > i.icon {
|
||||
right: auto;
|
||||
left: 1px;
|
||||
border-radius: 0.28571429rem 0 0 0.28571429rem;
|
||||
}
|
||||
.ui[class*="left icon"].input > i.circular.icon {
|
||||
right: auto;
|
||||
left: 0.5em;
|
||||
}
|
||||
.ui.ui.ui.ui[class*="left icon"].input > textarea,
|
||||
.ui.ui.ui.ui[class*="left icon"].input > input {
|
||||
padding-left: 2.67142857em;
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
||||
.ui.icon.input > textarea:focus ~ .icon,
|
||||
.ui.icon.input > input:focus ~ .icon {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.ui.icon.input > textarea ~ i.icon {
|
||||
height: 3em;
|
||||
}
|
||||
|
||||
.ui.form .field.error > .ui.action.input > .ui.button,
|
||||
.ui.action.input.error > .ui.button {
|
||||
border-top: 1px solid var(--color-error-border);
|
||||
border-bottom: 1px solid var(--color-error-border);
|
||||
}
|
||||
|
||||
.ui.action.input > .button,
|
||||
.ui.action.input > .buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.ui.action.input > .button,
|
||||
.ui.action.input > .buttons > .button {
|
||||
padding-top: 0.78571429em;
|
||||
padding-bottom: 0.78571429em;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.ui.action.input:not([class*="left action"]) > input {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-right-color: transparent;
|
||||
}
|
||||
|
||||
.ui.action.input > .dropdown:first-child,
|
||||
.ui.action.input > .button:first-child,
|
||||
.ui.action.input > .buttons:first-child > .button {
|
||||
border-radius: 0.28571429rem 0 0 0.28571429rem;
|
||||
}
|
||||
.ui.action.input > .dropdown:not(:first-child),
|
||||
.ui.action.input > .button:not(:first-child),
|
||||
.ui.action.input > .buttons:not(:first-child) > .button {
|
||||
border-radius: 0;
|
||||
}
|
||||
.ui.action.input > .dropdown:last-child,
|
||||
.ui.action.input > .button:last-child,
|
||||
.ui.action.input > .buttons:last-child > .button {
|
||||
border-radius: 0 0.28571429rem 0.28571429rem 0;
|
||||
}
|
||||
|
||||
.ui.fluid.input {
|
||||
display: flex;
|
||||
}
|
||||
.ui.fluid.input > input {
|
||||
width: 0 !important;
|
||||
}
|
||||
|
||||
.ui.tiny.input {
|
||||
font-size: 0.85714286em;
|
||||
}
|
||||
.ui.small.input {
|
||||
font-size: 0.92857143em;
|
||||
}
|
||||
|
||||
.ui.action.input .ui.ui.button {
|
||||
border-color: var(--color-input-border);
|
||||
padding-top: 0; /* the ".action.input" is "flex + stretch", so let the buttons layout themselves */
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
/* currently used for search bar dropdowns in repo search and explore code */
|
||||
.ui.action.input:not([class*="left action"]) > .ui.dropdown.selection {
|
||||
min-width: 10em;
|
||||
}
|
||||
.ui.action.input:not([class*="left action"]) > .ui.dropdown.selection:not(:focus) {
|
||||
border-right: none;
|
||||
}
|
||||
.ui.action.input:not([class*="left action"]) > .ui.dropdown.selection:not(.active):hover {
|
||||
border-color: var(--color-input-border);
|
||||
}
|
||||
.ui.action.input:not([class*="left action"]) .ui.dropdown.selection.upward.visible {
|
||||
border-bottom-left-radius: 0 !important;
|
||||
border-bottom-right-radius: 0 !important;
|
||||
}
|
||||
.ui.action.input:not([class*="left action"]) > input,
|
||||
.ui.action.input:not([class*="left action"]) > input:hover {
|
||||
border-right: none;
|
||||
}
|
||||
.ui.action.input:not([class*="left action"]) > input:focus + .ui.dropdown.selection,
|
||||
.ui.action.input:not([class*="left action"]) > input:focus + .ui.dropdown.selection:hover,
|
||||
.ui.action.input:not([class*="left action"]) > input:focus + .button,
|
||||
.ui.action.input:not([class*="left action"]) > input:focus + .button:hover,
|
||||
.ui.action.input:not([class*="left action"]) > input:focus + .icon + .button,
|
||||
.ui.action.input:not([class*="left action"]) > input:focus + .icon + .button:hover {
|
||||
border-left-color: var(--color-primary);
|
||||
}
|
||||
.ui.action.input:not([class*="left action"]) > input:focus {
|
||||
border-right-color: var(--color-primary);
|
||||
}
|
187
web_src/css/modules/list.css
Normal file
187
web_src/css/modules/list.css
Normal file
|
@ -0,0 +1,187 @@
|
|||
/* based on Fomantic UI list module, with just the parts extracted that we use. If you find any
|
||||
unused rules here after refactoring, please remove them. */
|
||||
|
||||
.ui.list {
|
||||
list-style-type: none;
|
||||
margin: 1em 0;
|
||||
padding: 0;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.ui.list:first-child {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.ui.list:last-child {
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.ui.list > .item,
|
||||
.ui.list .list > .item {
|
||||
display: list-item;
|
||||
table-layout: fixed;
|
||||
list-style-type: none;
|
||||
list-style-position: outside;
|
||||
}
|
||||
|
||||
.ui.list > .list > .item::after,
|
||||
.ui.list > .item::after {
|
||||
content: "";
|
||||
display: block;
|
||||
height: 0;
|
||||
clear: both;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.ui.list .list:not(.icon) {
|
||||
clear: both;
|
||||
margin: 0;
|
||||
padding: 0.75em 0 0.25em 0.5em;
|
||||
}
|
||||
|
||||
.ui.list .list > .item {
|
||||
padding: 0.14285714em 0;
|
||||
}
|
||||
|
||||
.ui.list .list > .item > i.icon,
|
||||
.ui.list > .item > i.icon {
|
||||
display: table-cell;
|
||||
min-width: 1.55em;
|
||||
padding-top: 0;
|
||||
transition: color 0.1s ease;
|
||||
padding-right: 0.28571429em;
|
||||
vertical-align: top;
|
||||
}
|
||||
.ui.list .list > .item > i.icon:only-child,
|
||||
.ui.list > .item > i.icon:only-child {
|
||||
display: inline-block;
|
||||
min-width: auto;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.ui.list .list > .item > .image,
|
||||
.ui.list > .item > .image {
|
||||
display: table-cell;
|
||||
background-color: transparent;
|
||||
vertical-align: top;
|
||||
}
|
||||
.ui.list .list > .item > .image:not(:only-child):not(img),
|
||||
.ui.list > .item > .image:not(:only-child):not(img) {
|
||||
padding-right: 0.5em;
|
||||
}
|
||||
.ui.list .list > .item > .image img,
|
||||
.ui.list > .item > .image img {
|
||||
vertical-align: top;
|
||||
}
|
||||
.ui.list .list > .item > img.image,
|
||||
.ui.list .list > .item > .image:only-child,
|
||||
.ui.list > .item > img.image,
|
||||
.ui.list > .item > .image:only-child {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.ui.list .list > .item > .content,
|
||||
.ui.list > .item > .content {
|
||||
color: var(--color-text);
|
||||
}
|
||||
.ui.list .list > .item > .image + .content,
|
||||
.ui.list .list > .item > i.icon + .content,
|
||||
.ui.list > .item > .image + .content,
|
||||
.ui.list > .item > i.icon + .content {
|
||||
display: table-cell;
|
||||
width: 100%;
|
||||
padding: 0 0 0 0.5em;
|
||||
vertical-align: top;
|
||||
}
|
||||
.ui.list .list > .item > img.image + .content,
|
||||
.ui.list > .item > img.image + .content {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
}
|
||||
.ui.list .list > .item > .content > .list,
|
||||
.ui.list > .item > .content > .list {
|
||||
margin-left: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.ui.list .list > .item .header,
|
||||
.ui.list > .item .header {
|
||||
display: block;
|
||||
margin: 0;
|
||||
font-family: var(--fonts-regular);
|
||||
font-weight: var(--font-weight-medium);
|
||||
color: var(--color-text-dark);
|
||||
}
|
||||
|
||||
.ui.list .list > .item .description,
|
||||
.ui.list > .item .description {
|
||||
display: block;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.ui.list > .item a,
|
||||
.ui.list .list > .item a {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ui.menu .ui.list > .item,
|
||||
.ui.menu .ui.list .list > .item {
|
||||
display: list-item;
|
||||
table-layout: fixed;
|
||||
background-color: transparent;
|
||||
list-style-type: none;
|
||||
list-style-position: outside;
|
||||
padding: 0.21428571em 0;
|
||||
}
|
||||
.ui.menu .ui.list .list > .item::before,
|
||||
.ui.menu .ui.list > .item::before {
|
||||
border: none;
|
||||
background: none;
|
||||
}
|
||||
.ui.menu .ui.list .list > .item:first-child,
|
||||
.ui.menu .ui.list > .item:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
.ui.menu .ui.list .list > .item:last-child,
|
||||
.ui.menu .ui.list > .item:last-child {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.ui.list .list > .disabled.item,
|
||||
.ui.list > .disabled.item {
|
||||
pointer-events: none;
|
||||
opacity: var(--opacity-disabled);
|
||||
}
|
||||
|
||||
.ui.list .list > a.item:hover > .icons,
|
||||
.ui.list > a.item:hover > .icons,
|
||||
.ui.list .list > a.item:hover > i.icon,
|
||||
.ui.list > a.item:hover > i.icon {
|
||||
color: var(--color-text-dark);
|
||||
}
|
||||
|
||||
.ui.divided.list > .item {
|
||||
border-top: 1px solid var(--color-secondary);
|
||||
}
|
||||
.ui.divided.list .list > .item {
|
||||
border-top: none;
|
||||
}
|
||||
.ui.divided.list .item .list > .item {
|
||||
border-top: none;
|
||||
}
|
||||
.ui.divided.list .list > .item:first-child,
|
||||
.ui.divided.list > .item:first-child {
|
||||
border-top: none;
|
||||
}
|
||||
.ui.divided.list .list > .item:first-child {
|
||||
border-top-width: 1px;
|
||||
}
|
||||
|
||||
.ui.relaxed.list > .item:not(:first-child) {
|
||||
padding-top: 0.42857143em;
|
||||
}
|
||||
.ui.relaxed.list > .item:not(:last-child) {
|
||||
padding-bottom: 0.42857143em;
|
||||
}
|
|
@ -140,3 +140,8 @@
|
|||
.secondary-nav {
|
||||
background: var(--color-secondary-nav-bg) !important; /* important because of .ui.secondary.menu */
|
||||
}
|
||||
|
||||
.issue-navbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,17 @@
|
|||
z-index: 1;
|
||||
}
|
||||
|
||||
/* bare theme, no styling at all, except box-shadow */
|
||||
.tippy-box[data-theme="bare"] {
|
||||
border: none;
|
||||
box-shadow: 0 6px 18px var(--color-shadow);
|
||||
}
|
||||
|
||||
.tippy-box[data-theme="bare"] .tippy-content {
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* tooltip theme for text tooltips */
|
||||
|
||||
.tippy-box[data-theme="tooltip"] {
|
||||
|
|
|
@ -89,10 +89,6 @@
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
.organization.options input {
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
.page-content.organization .org-avatar {
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
|
|
@ -2299,104 +2299,6 @@
|
|||
padding-top: 15px;
|
||||
}
|
||||
|
||||
.edit-label.modal .form .color.picker.column,
|
||||
.new-label.modal .form .color.picker.column {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.edit-label.modal .form .color.picker.column .minicolors,
|
||||
.new-label.modal .form .color.picker.column .minicolors {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.edit-label.modal .form .minicolors-swatch.minicolors-sprite,
|
||||
.new-label.modal .form .minicolors-swatch.minicolors-sprite {
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
}
|
||||
|
||||
.tab-size-1 {
|
||||
tab-size: 1 !important;
|
||||
-moz-tab-size: 1 !important;
|
||||
}
|
||||
|
||||
.tab-size-2 {
|
||||
tab-size: 2 !important;
|
||||
-moz-tab-size: 2 !important;
|
||||
}
|
||||
|
||||
.tab-size-3 {
|
||||
tab-size: 3 !important;
|
||||
-moz-tab-size: 3 !important;
|
||||
}
|
||||
|
||||
.tab-size-4 {
|
||||
tab-size: 4 !important;
|
||||
-moz-tab-size: 4 !important;
|
||||
}
|
||||
|
||||
.tab-size-5 {
|
||||
tab-size: 5 !important;
|
||||
-moz-tab-size: 5 !important;
|
||||
}
|
||||
|
||||
.tab-size-6 {
|
||||
tab-size: 6 !important;
|
||||
-moz-tab-size: 6 !important;
|
||||
}
|
||||
|
||||
.tab-size-7 {
|
||||
tab-size: 7 !important;
|
||||
-moz-tab-size: 7 !important;
|
||||
}
|
||||
|
||||
.tab-size-8 {
|
||||
tab-size: 8 !important;
|
||||
-moz-tab-size: 8 !important;
|
||||
}
|
||||
|
||||
.tab-size-9 {
|
||||
tab-size: 9 !important;
|
||||
-moz-tab-size: 9 !important;
|
||||
}
|
||||
|
||||
.tab-size-10 {
|
||||
tab-size: 10 !important;
|
||||
-moz-tab-size: 10 !important;
|
||||
}
|
||||
|
||||
.tab-size-11 {
|
||||
tab-size: 11 !important;
|
||||
-moz-tab-size: 11 !important;
|
||||
}
|
||||
|
||||
.tab-size-12 {
|
||||
tab-size: 12 !important;
|
||||
-moz-tab-size: 12 !important;
|
||||
}
|
||||
|
||||
.tab-size-13 {
|
||||
tab-size: 13 !important;
|
||||
-moz-tab-size: 13 !important;
|
||||
}
|
||||
|
||||
.tab-size-14 {
|
||||
tab-size: 14 !important;
|
||||
-moz-tab-size: 14 !important;
|
||||
}
|
||||
|
||||
.tab-size-15 {
|
||||
tab-size: 15 !important;
|
||||
-moz-tab-size: 15 !important;
|
||||
}
|
||||
|
||||
.tab-size-16 {
|
||||
tab-size: 16 !important;
|
||||
-moz-tab-size: 16 !important;
|
||||
}
|
||||
|
||||
.stats-table {
|
||||
display: table;
|
||||
width: 100%;
|
||||
|
@ -2573,6 +2475,7 @@ tbody.commit-list {
|
|||
#repo-topics .repo-topic {
|
||||
font-weight: var(--font-weight-normal);
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#new-dependency-drop-list.ui.selection.dropdown {
|
||||
|
@ -2990,6 +2893,7 @@ tbody.commit-list {
|
|||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
.issue-list-toolbar-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.issue-list-toolbar-right .filter.menu {
|
||||
|
|
2430
web_src/fomantic/build/semantic.css
generated
2430
web_src/fomantic/build/semantic.css
generated
File diff suppressed because it is too large
Load diff
877
web_src/fomantic/build/semantic.js
generated
877
web_src/fomantic/build/semantic.js
generated
|
@ -1184,883 +1184,6 @@ $.api.settings = {
|
|||
|
||||
|
||||
|
||||
})( jQuery, window, document );
|
||||
|
||||
/*!
|
||||
* # Fomantic-UI - Checkbox
|
||||
* http://github.com/fomantic/Fomantic-UI/
|
||||
*
|
||||
*
|
||||
* Released under the MIT license
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
*/
|
||||
|
||||
;(function ($, window, document, undefined) {
|
||||
|
||||
'use strict';
|
||||
|
||||
$.isFunction = $.isFunction || function(obj) {
|
||||
return typeof obj === "function" && typeof obj.nodeType !== "number";
|
||||
};
|
||||
|
||||
window = (typeof window != 'undefined' && window.Math == Math)
|
||||
? window
|
||||
: (typeof self != 'undefined' && self.Math == Math)
|
||||
? self
|
||||
: Function('return this')()
|
||||
;
|
||||
|
||||
$.fn.checkbox = function(parameters) {
|
||||
var
|
||||
$allModules = $(this),
|
||||
moduleSelector = $allModules.selector || '',
|
||||
|
||||
time = new Date().getTime(),
|
||||
performance = [],
|
||||
|
||||
query = arguments[0],
|
||||
methodInvoked = (typeof query == 'string'),
|
||||
queryArguments = [].slice.call(arguments, 1),
|
||||
returnedValue
|
||||
;
|
||||
|
||||
$allModules
|
||||
.each(function() {
|
||||
var
|
||||
settings = $.extend(true, {}, $.fn.checkbox.settings, parameters),
|
||||
|
||||
className = settings.className,
|
||||
namespace = settings.namespace,
|
||||
selector = settings.selector,
|
||||
error = settings.error,
|
||||
|
||||
eventNamespace = '.' + namespace,
|
||||
moduleNamespace = 'module-' + namespace,
|
||||
|
||||
$module = $(this),
|
||||
$label = $(this).children(selector.label),
|
||||
$input = $(this).children(selector.input),
|
||||
input = $input[0],
|
||||
|
||||
initialLoad = false,
|
||||
shortcutPressed = false,
|
||||
instance = $module.data(moduleNamespace),
|
||||
|
||||
observer,
|
||||
element = this,
|
||||
module
|
||||
;
|
||||
|
||||
module = {
|
||||
|
||||
initialize: function() {
|
||||
module.verbose('Initializing checkbox', settings);
|
||||
|
||||
module.create.label();
|
||||
module.bind.events();
|
||||
|
||||
module.set.tabbable();
|
||||
module.hide.input();
|
||||
|
||||
module.observeChanges();
|
||||
module.instantiate();
|
||||
module.setup();
|
||||
},
|
||||
|
||||
instantiate: function() {
|
||||
module.verbose('Storing instance of module', module);
|
||||
instance = module;
|
||||
$module
|
||||
.data(moduleNamespace, module)
|
||||
;
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
module.verbose('Destroying module');
|
||||
module.unbind.events();
|
||||
module.show.input();
|
||||
$module.removeData(moduleNamespace);
|
||||
},
|
||||
|
||||
fix: {
|
||||
reference: function() {
|
||||
if( $module.is(selector.input) ) {
|
||||
module.debug('Behavior called on <input> adjusting invoked element');
|
||||
$module = $module.closest(selector.checkbox);
|
||||
module.refresh();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
setup: function() {
|
||||
module.set.initialLoad();
|
||||
if( module.is.indeterminate() ) {
|
||||
module.debug('Initial value is indeterminate');
|
||||
module.indeterminate();
|
||||
}
|
||||
else if( module.is.checked() ) {
|
||||
module.debug('Initial value is checked');
|
||||
module.check();
|
||||
}
|
||||
else {
|
||||
module.debug('Initial value is unchecked');
|
||||
module.uncheck();
|
||||
}
|
||||
module.remove.initialLoad();
|
||||
},
|
||||
|
||||
refresh: function() {
|
||||
$label = $module.children(selector.label);
|
||||
$input = $module.children(selector.input);
|
||||
input = $input[0];
|
||||
},
|
||||
|
||||
hide: {
|
||||
input: function() {
|
||||
module.verbose('Modifying <input> z-index to be unselectable');
|
||||
$input.addClass(className.hidden);
|
||||
}
|
||||
},
|
||||
show: {
|
||||
input: function() {
|
||||
module.verbose('Modifying <input> z-index to be selectable');
|
||||
$input.removeClass(className.hidden);
|
||||
}
|
||||
},
|
||||
|
||||
observeChanges: function() {
|
||||
if('MutationObserver' in window) {
|
||||
observer = new MutationObserver(function(mutations) {
|
||||
module.debug('DOM tree modified, updating selector cache');
|
||||
module.refresh();
|
||||
});
|
||||
observer.observe(element, {
|
||||
childList : true,
|
||||
subtree : true
|
||||
});
|
||||
module.debug('Setting up mutation observer', observer);
|
||||
}
|
||||
},
|
||||
|
||||
attachEvents: function(selector, event) {
|
||||
var
|
||||
$element = $(selector)
|
||||
;
|
||||
event = $.isFunction(module[event])
|
||||
? module[event]
|
||||
: module.toggle
|
||||
;
|
||||
if($element.length > 0) {
|
||||
module.debug('Attaching checkbox events to element', selector, event);
|
||||
$element
|
||||
.on('click' + eventNamespace, event)
|
||||
;
|
||||
}
|
||||
else {
|
||||
module.error(error.notFound);
|
||||
}
|
||||
},
|
||||
|
||||
preventDefaultOnInputTarget: function() {
|
||||
if(typeof event !== 'undefined' && event !== null && $(event.target).is(selector.input)) {
|
||||
module.verbose('Preventing default check action after manual check action');
|
||||
event.preventDefault();
|
||||
}
|
||||
},
|
||||
|
||||
event: {
|
||||
change: function(event) {
|
||||
if( !module.should.ignoreCallbacks() ) {
|
||||
settings.onChange.call(input);
|
||||
}
|
||||
},
|
||||
click: function(event) {
|
||||
var
|
||||
$target = $(event.target)
|
||||
;
|
||||
if( $target.is(selector.input) ) {
|
||||
module.verbose('Using default check action on initialized checkbox');
|
||||
return;
|
||||
}
|
||||
if( $target.is(selector.link) ) {
|
||||
module.debug('Clicking link inside checkbox, skipping toggle');
|
||||
return;
|
||||
}
|
||||
module.toggle();
|
||||
$input.focus();
|
||||
event.preventDefault();
|
||||
},
|
||||
keydown: function(event) {
|
||||
var
|
||||
key = event.which,
|
||||
keyCode = {
|
||||
enter : 13,
|
||||
space : 32,
|
||||
escape : 27,
|
||||
left : 37,
|
||||
up : 38,
|
||||
right : 39,
|
||||
down : 40
|
||||
}
|
||||
;
|
||||
|
||||
var r = module.get.radios(),
|
||||
rIndex = r.index($module),
|
||||
rLen = r.length,
|
||||
checkIndex = false;
|
||||
|
||||
if(key == keyCode.left || key == keyCode.up) {
|
||||
checkIndex = (rIndex === 0 ? rLen : rIndex) - 1;
|
||||
} else if(key == keyCode.right || key == keyCode.down) {
|
||||
checkIndex = rIndex === rLen-1 ? 0 : rIndex+1;
|
||||
}
|
||||
|
||||
if (!module.should.ignoreCallbacks() && checkIndex !== false) {
|
||||
if(settings.beforeUnchecked.apply(input)===false) {
|
||||
module.verbose('Option not allowed to be unchecked, cancelling key navigation');
|
||||
return false;
|
||||
}
|
||||
if (settings.beforeChecked.apply($(r[checkIndex]).children(selector.input)[0])===false) {
|
||||
module.verbose('Next option should not allow check, cancelling key navigation');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(key == keyCode.escape) {
|
||||
module.verbose('Escape key pressed blurring field');
|
||||
$input.blur();
|
||||
shortcutPressed = true;
|
||||
}
|
||||
else if(!event.ctrlKey && ( key == keyCode.space || (key == keyCode.enter && settings.enableEnterKey)) ) {
|
||||
module.verbose('Enter/space key pressed, toggling checkbox');
|
||||
module.toggle();
|
||||
shortcutPressed = true;
|
||||
}
|
||||
else {
|
||||
shortcutPressed = false;
|
||||
}
|
||||
},
|
||||
keyup: function(event) {
|
||||
if(shortcutPressed) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
check: function() {
|
||||
if( !module.should.allowCheck() ) {
|
||||
return;
|
||||
}
|
||||
module.debug('Checking checkbox', $input);
|
||||
module.set.checked();
|
||||
if( !module.should.ignoreCallbacks() ) {
|
||||
settings.onChecked.call(input);
|
||||
module.trigger.change();
|
||||
}
|
||||
module.preventDefaultOnInputTarget();
|
||||
},
|
||||
|
||||
uncheck: function() {
|
||||
if( !module.should.allowUncheck() ) {
|
||||
return;
|
||||
}
|
||||
module.debug('Unchecking checkbox');
|
||||
module.set.unchecked();
|
||||
if( !module.should.ignoreCallbacks() ) {
|
||||
settings.onUnchecked.call(input);
|
||||
module.trigger.change();
|
||||
}
|
||||
module.preventDefaultOnInputTarget();
|
||||
},
|
||||
|
||||
indeterminate: function() {
|
||||
if( module.should.allowIndeterminate() ) {
|
||||
module.debug('Checkbox is already indeterminate');
|
||||
return;
|
||||
}
|
||||
module.debug('Making checkbox indeterminate');
|
||||
module.set.indeterminate();
|
||||
if( !module.should.ignoreCallbacks() ) {
|
||||
settings.onIndeterminate.call(input);
|
||||
module.trigger.change();
|
||||
}
|
||||
},
|
||||
|
||||
determinate: function() {
|
||||
if( module.should.allowDeterminate() ) {
|
||||
module.debug('Checkbox is already determinate');
|
||||
return;
|
||||
}
|
||||
module.debug('Making checkbox determinate');
|
||||
module.set.determinate();
|
||||
if( !module.should.ignoreCallbacks() ) {
|
||||
settings.onDeterminate.call(input);
|
||||
module.trigger.change();
|
||||
}
|
||||
},
|
||||
|
||||
enable: function() {
|
||||
if( module.is.enabled() ) {
|
||||
module.debug('Checkbox is already enabled');
|
||||
return;
|
||||
}
|
||||
module.debug('Enabling checkbox');
|
||||
module.set.enabled();
|
||||
if( !module.should.ignoreCallbacks() ) {
|
||||
settings.onEnable.call(input);
|
||||
// preserve legacy callbacks
|
||||
settings.onEnabled.call(input);
|
||||
module.trigger.change();
|
||||
}
|
||||
},
|
||||
|
||||
disable: function() {
|
||||
if( module.is.disabled() ) {
|
||||
module.debug('Checkbox is already disabled');
|
||||
return;
|
||||
}
|
||||
module.debug('Disabling checkbox');
|
||||
module.set.disabled();
|
||||
if( !module.should.ignoreCallbacks() ) {
|
||||
settings.onDisable.call(input);
|
||||
// preserve legacy callbacks
|
||||
settings.onDisabled.call(input);
|
||||
module.trigger.change();
|
||||
}
|
||||
},
|
||||
|
||||
get: {
|
||||
radios: function() {
|
||||
var
|
||||
name = module.get.name()
|
||||
;
|
||||
return $('input[name="' + name + '"]').closest(selector.checkbox);
|
||||
},
|
||||
otherRadios: function() {
|
||||
return module.get.radios().not($module);
|
||||
},
|
||||
name: function() {
|
||||
return $input.attr('name');
|
||||
}
|
||||
},
|
||||
|
||||
is: {
|
||||
initialLoad: function() {
|
||||
return initialLoad;
|
||||
},
|
||||
radio: function() {
|
||||
return ($input.hasClass(className.radio) || $input.attr('type') == 'radio');
|
||||
},
|
||||
indeterminate: function() {
|
||||
return $input.prop('indeterminate') !== undefined && $input.prop('indeterminate');
|
||||
},
|
||||
checked: function() {
|
||||
return $input.prop('checked') !== undefined && $input.prop('checked');
|
||||
},
|
||||
disabled: function() {
|
||||
return $input.prop('disabled') !== undefined && $input.prop('disabled');
|
||||
},
|
||||
enabled: function() {
|
||||
return !module.is.disabled();
|
||||
},
|
||||
determinate: function() {
|
||||
return !module.is.indeterminate();
|
||||
},
|
||||
unchecked: function() {
|
||||
return !module.is.checked();
|
||||
}
|
||||
},
|
||||
|
||||
should: {
|
||||
allowCheck: function() {
|
||||
if(module.is.determinate() && module.is.checked() && !module.is.initialLoad() ) {
|
||||
module.debug('Should not allow check, checkbox is already checked');
|
||||
return false;
|
||||
}
|
||||
if(!module.should.ignoreCallbacks() && settings.beforeChecked.apply(input) === false) {
|
||||
module.debug('Should not allow check, beforeChecked cancelled');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
allowUncheck: function() {
|
||||
if(module.is.determinate() && module.is.unchecked() && !module.is.initialLoad() ) {
|
||||
module.debug('Should not allow uncheck, checkbox is already unchecked');
|
||||
return false;
|
||||
}
|
||||
if(!module.should.ignoreCallbacks() && settings.beforeUnchecked.apply(input) === false) {
|
||||
module.debug('Should not allow uncheck, beforeUnchecked cancelled');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
allowIndeterminate: function() {
|
||||
if(module.is.indeterminate() && !module.is.initialLoad() ) {
|
||||
module.debug('Should not allow indeterminate, checkbox is already indeterminate');
|
||||
return false;
|
||||
}
|
||||
if(!module.should.ignoreCallbacks() && settings.beforeIndeterminate.apply(input) === false) {
|
||||
module.debug('Should not allow indeterminate, beforeIndeterminate cancelled');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
allowDeterminate: function() {
|
||||
if(module.is.determinate() && !module.is.initialLoad() ) {
|
||||
module.debug('Should not allow determinate, checkbox is already determinate');
|
||||
return false;
|
||||
}
|
||||
if(!module.should.ignoreCallbacks() && settings.beforeDeterminate.apply(input) === false) {
|
||||
module.debug('Should not allow determinate, beforeDeterminate cancelled');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
ignoreCallbacks: function() {
|
||||
return (initialLoad && !settings.fireOnInit);
|
||||
}
|
||||
},
|
||||
|
||||
can: {
|
||||
change: function() {
|
||||
return !( $module.hasClass(className.disabled) || $module.hasClass(className.readOnly) || $input.prop('disabled') || $input.prop('readonly') );
|
||||
},
|
||||
uncheck: function() {
|
||||
return (typeof settings.uncheckable === 'boolean')
|
||||
? settings.uncheckable
|
||||
: !module.is.radio()
|
||||
;
|
||||
}
|
||||
},
|
||||
|
||||
set: {
|
||||
initialLoad: function() {
|
||||
initialLoad = true;
|
||||
},
|
||||
checked: function() {
|
||||
module.verbose('Setting class to checked');
|
||||
$module
|
||||
.removeClass(className.indeterminate)
|
||||
.addClass(className.checked)
|
||||
;
|
||||
if( module.is.radio() ) {
|
||||
module.uncheckOthers();
|
||||
}
|
||||
if(!module.is.indeterminate() && module.is.checked()) {
|
||||
module.debug('Input is already checked, skipping input property change');
|
||||
return;
|
||||
}
|
||||
module.verbose('Setting state to checked', input);
|
||||
$input
|
||||
.prop('indeterminate', false)
|
||||
.prop('checked', true)
|
||||
;
|
||||
},
|
||||
unchecked: function() {
|
||||
module.verbose('Removing checked class');
|
||||
$module
|
||||
.removeClass(className.indeterminate)
|
||||
.removeClass(className.checked)
|
||||
;
|
||||
if(!module.is.indeterminate() && module.is.unchecked() ) {
|
||||
module.debug('Input is already unchecked');
|
||||
return;
|
||||
}
|
||||
module.debug('Setting state to unchecked');
|
||||
$input
|
||||
.prop('indeterminate', false)
|
||||
.prop('checked', false)
|
||||
;
|
||||
},
|
||||
indeterminate: function() {
|
||||
module.verbose('Setting class to indeterminate');
|
||||
$module
|
||||
.addClass(className.indeterminate)
|
||||
;
|
||||
if( module.is.indeterminate() ) {
|
||||
module.debug('Input is already indeterminate, skipping input property change');
|
||||
return;
|
||||
}
|
||||
module.debug('Setting state to indeterminate');
|
||||
$input
|
||||
.prop('indeterminate', true)
|
||||
;
|
||||
},
|
||||
determinate: function() {
|
||||
module.verbose('Removing indeterminate class');
|
||||
$module
|
||||
.removeClass(className.indeterminate)
|
||||
;
|
||||
if( module.is.determinate() ) {
|
||||
module.debug('Input is already determinate, skipping input property change');
|
||||
return;
|
||||
}
|
||||
module.debug('Setting state to determinate');
|
||||
$input
|
||||
.prop('indeterminate', false)
|
||||
;
|
||||
},
|
||||
disabled: function() {
|
||||
module.verbose('Setting class to disabled');
|
||||
$module
|
||||
.addClass(className.disabled)
|
||||
;
|
||||
if( module.is.disabled() ) {
|
||||
module.debug('Input is already disabled, skipping input property change');
|
||||
return;
|
||||
}
|
||||
module.debug('Setting state to disabled');
|
||||
$input
|
||||
.prop('disabled', 'disabled')
|
||||
;
|
||||
},
|
||||
enabled: function() {
|
||||
module.verbose('Removing disabled class');
|
||||
$module.removeClass(className.disabled);
|
||||
if( module.is.enabled() ) {
|
||||
module.debug('Input is already enabled, skipping input property change');
|
||||
return;
|
||||
}
|
||||
module.debug('Setting state to enabled');
|
||||
$input
|
||||
.prop('disabled', false)
|
||||
;
|
||||
},
|
||||
tabbable: function() {
|
||||
module.verbose('Adding tabindex to checkbox');
|
||||
if( $input.attr('tabindex') === undefined) {
|
||||
$input.attr('tabindex', 0);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
remove: {
|
||||
initialLoad: function() {
|
||||
initialLoad = false;
|
||||
}
|
||||
},
|
||||
|
||||
trigger: {
|
||||
change: function() {
|
||||
var
|
||||
inputElement = $input[0]
|
||||
;
|
||||
if(inputElement) {
|
||||
var events = document.createEvent('HTMLEvents');
|
||||
module.verbose('Triggering native change event');
|
||||
events.initEvent('change', true, false);
|
||||
inputElement.dispatchEvent(events);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
create: {
|
||||
label: function() {
|
||||
if($input.prevAll(selector.label).length > 0) {
|
||||
$input.prev(selector.label).detach().insertAfter($input);
|
||||
module.debug('Moving existing label', $label);
|
||||
}
|
||||
else if( !module.has.label() ) {
|
||||
$label = $('<label>').insertAfter($input);
|
||||
module.debug('Creating label', $label);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
has: {
|
||||
label: function() {
|
||||
return ($label.length > 0);
|
||||
}
|
||||
},
|
||||
|
||||
bind: {
|
||||
events: function() {
|
||||
module.verbose('Attaching checkbox events');
|
||||
$module
|
||||
.on('click' + eventNamespace, module.event.click)
|
||||
.on('change' + eventNamespace, module.event.change)
|
||||
.on('keydown' + eventNamespace, selector.input, module.event.keydown)
|
||||
.on('keyup' + eventNamespace, selector.input, module.event.keyup)
|
||||
;
|
||||
}
|
||||
},
|
||||
|
||||
unbind: {
|
||||
events: function() {
|
||||
module.debug('Removing events');
|
||||
$module
|
||||
.off(eventNamespace)
|
||||
;
|
||||
}
|
||||
},
|
||||
|
||||
uncheckOthers: function() {
|
||||
var
|
||||
$radios = module.get.otherRadios()
|
||||
;
|
||||
module.debug('Unchecking other radios', $radios);
|
||||
$radios.removeClass(className.checked);
|
||||
},
|
||||
|
||||
toggle: function() {
|
||||
if( !module.can.change() ) {
|
||||
if(!module.is.radio()) {
|
||||
module.debug('Checkbox is read-only or disabled, ignoring toggle');
|
||||
}
|
||||
return;
|
||||
}
|
||||
if( module.is.indeterminate() || module.is.unchecked() ) {
|
||||
module.debug('Currently unchecked');
|
||||
module.check();
|
||||
}
|
||||
else if( module.is.checked() && module.can.uncheck() ) {
|
||||
module.debug('Currently checked');
|
||||
module.uncheck();
|
||||
}
|
||||
},
|
||||
setting: function(name, value) {
|
||||
module.debug('Changing setting', name, value);
|
||||
if( $.isPlainObject(name) ) {
|
||||
$.extend(true, settings, name);
|
||||
}
|
||||
else if(value !== undefined) {
|
||||
if($.isPlainObject(settings[name])) {
|
||||
$.extend(true, settings[name], value);
|
||||
}
|
||||
else {
|
||||
settings[name] = value;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return settings[name];
|
||||
}
|
||||
},
|
||||
internal: function(name, value) {
|
||||
if( $.isPlainObject(name) ) {
|
||||
$.extend(true, module, name);
|
||||
}
|
||||
else if(value !== undefined) {
|
||||
module[name] = value;
|
||||
}
|
||||
else {
|
||||
return module[name];
|
||||
}
|
||||
},
|
||||
debug: function() {
|
||||
if(!settings.silent && settings.debug) {
|
||||
if(settings.performance) {
|
||||
module.performance.log(arguments);
|
||||
}
|
||||
else {
|
||||
module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
|
||||
module.debug.apply(console, arguments);
|
||||
}
|
||||
}
|
||||
},
|
||||
verbose: function() {
|
||||
if(!settings.silent && settings.verbose && settings.debug) {
|
||||
if(settings.performance) {
|
||||
module.performance.log(arguments);
|
||||
}
|
||||
else {
|
||||
module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
|
||||
module.verbose.apply(console, arguments);
|
||||
}
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
if(!settings.silent) {
|
||||
module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
|
||||
module.error.apply(console, arguments);
|
||||
}
|
||||
},
|
||||
performance: {
|
||||
log: function(message) {
|
||||
var
|
||||
currentTime,
|
||||
executionTime,
|
||||
previousTime
|
||||
;
|
||||
if(settings.performance) {
|
||||
currentTime = new Date().getTime();
|
||||
previousTime = time || currentTime;
|
||||
executionTime = currentTime - previousTime;
|
||||
time = currentTime;
|
||||
performance.push({
|
||||
'Name' : message[0],
|
||||
'Arguments' : [].slice.call(message, 1) || '',
|
||||
'Element' : element,
|
||||
'Execution Time' : executionTime
|
||||
});
|
||||
}
|
||||
clearTimeout(module.performance.timer);
|
||||
module.performance.timer = setTimeout(module.performance.display, 500);
|
||||
},
|
||||
display: function() {
|
||||
var
|
||||
title = settings.name + ':',
|
||||
totalTime = 0
|
||||
;
|
||||
time = false;
|
||||
clearTimeout(module.performance.timer);
|
||||
$.each(performance, function(index, data) {
|
||||
totalTime += data['Execution Time'];
|
||||
});
|
||||
title += ' ' + totalTime + 'ms';
|
||||
if(moduleSelector) {
|
||||
title += ' \'' + moduleSelector + '\'';
|
||||
}
|
||||
if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
|
||||
console.groupCollapsed(title);
|
||||
if(console.table) {
|
||||
console.table(performance);
|
||||
}
|
||||
else {
|
||||
$.each(performance, function(index, data) {
|
||||
console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
|
||||
});
|
||||
}
|
||||
console.groupEnd();
|
||||
}
|
||||
performance = [];
|
||||
}
|
||||
},
|
||||
invoke: function(query, passedArguments, context) {
|
||||
var
|
||||
object = instance,
|
||||
maxDepth,
|
||||
found,
|
||||
response
|
||||
;
|
||||
passedArguments = passedArguments || queryArguments;
|
||||
context = element || context;
|
||||
if(typeof query == 'string' && object !== undefined) {
|
||||
query = query.split(/[\. ]/);
|
||||
maxDepth = query.length - 1;
|
||||
$.each(query, function(depth, value) {
|
||||
var camelCaseValue = (depth != maxDepth)
|
||||
? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
|
||||
: query
|
||||
;
|
||||
if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
|
||||
object = object[camelCaseValue];
|
||||
}
|
||||
else if( object[camelCaseValue] !== undefined ) {
|
||||
found = object[camelCaseValue];
|
||||
return false;
|
||||
}
|
||||
else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
|
||||
object = object[value];
|
||||
}
|
||||
else if( object[value] !== undefined ) {
|
||||
found = object[value];
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
module.error(error.method, query);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
if ( $.isFunction( found ) ) {
|
||||
response = found.apply(context, passedArguments);
|
||||
}
|
||||
else if(found !== undefined) {
|
||||
response = found;
|
||||
}
|
||||
if(Array.isArray(returnedValue)) {
|
||||
returnedValue.push(response);
|
||||
}
|
||||
else if(returnedValue !== undefined) {
|
||||
returnedValue = [returnedValue, response];
|
||||
}
|
||||
else if(response !== undefined) {
|
||||
returnedValue = response;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
};
|
||||
|
||||
if(methodInvoked) {
|
||||
if(instance === undefined) {
|
||||
module.initialize();
|
||||
}
|
||||
module.invoke(query);
|
||||
}
|
||||
else {
|
||||
if(instance !== undefined) {
|
||||
instance.invoke('destroy');
|
||||
}
|
||||
module.initialize();
|
||||
}
|
||||
})
|
||||
;
|
||||
|
||||
return (returnedValue !== undefined)
|
||||
? returnedValue
|
||||
: this
|
||||
;
|
||||
};
|
||||
|
||||
$.fn.checkbox.settings = {
|
||||
|
||||
name : 'Checkbox',
|
||||
namespace : 'checkbox',
|
||||
|
||||
silent : false,
|
||||
debug : false,
|
||||
verbose : true,
|
||||
performance : true,
|
||||
|
||||
// delegated event context
|
||||
uncheckable : 'auto',
|
||||
fireOnInit : false,
|
||||
enableEnterKey : true,
|
||||
|
||||
onChange : function(){},
|
||||
|
||||
beforeChecked : function(){},
|
||||
beforeUnchecked : function(){},
|
||||
beforeDeterminate : function(){},
|
||||
beforeIndeterminate : function(){},
|
||||
|
||||
onChecked : function(){},
|
||||
onUnchecked : function(){},
|
||||
|
||||
onDeterminate : function() {},
|
||||
onIndeterminate : function() {},
|
||||
|
||||
onEnable : function(){},
|
||||
onDisable : function(){},
|
||||
|
||||
// preserve misspelled callbacks (will be removed in 3.0)
|
||||
onEnabled : function(){},
|
||||
onDisabled : function(){},
|
||||
|
||||
className : {
|
||||
checked : 'checked',
|
||||
indeterminate : 'indeterminate',
|
||||
disabled : 'disabled',
|
||||
hidden : 'hidden',
|
||||
radio : 'radio',
|
||||
readOnly : 'read-only'
|
||||
},
|
||||
|
||||
error : {
|
||||
method : 'The method you called is not defined'
|
||||
},
|
||||
|
||||
selector : {
|
||||
checkbox : '.ui.checkbox',
|
||||
label : 'label, .box',
|
||||
input : 'input[type="checkbox"], input[type="radio"]',
|
||||
link : 'a[href]'
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
})( jQuery, window, document );
|
||||
|
||||
/*!
|
||||
|
|
|
@ -23,12 +23,9 @@
|
|||
"components": [
|
||||
"api",
|
||||
"button",
|
||||
"checkbox",
|
||||
"dimmer",
|
||||
"dropdown",
|
||||
"form",
|
||||
"input",
|
||||
"list",
|
||||
"menu",
|
||||
"modal",
|
||||
"search",
|
||||
|
|
|
@ -350,10 +350,10 @@ export default sfc; // activate the IDE's Vue plugin
|
|||
<span class="ui grey label tw-ml-2">{{ reposTotalCount }}</span>
|
||||
</div>
|
||||
</h4>
|
||||
<div class="ui top attached segment repos-search gt-rounded-top">
|
||||
<div class="ui fluid action left icon input" :class="{loading: isLoading}">
|
||||
<div class="ui attached segment repos-search">
|
||||
<div class="ui small fluid action left icon input">
|
||||
<input type="search" spellcheck="false" maxlength="255" @input="changeReposFilter(reposFilter)" v-model="searchQuery" ref="search" @keydown="reposFilterKeyControl" :placeholder="textSearchRepos">
|
||||
<i class="icon"><svg-icon name="octicon-search" :size="16"/></i>
|
||||
<i class="icon loading-icon-3px" :class="{'is-loading': isLoading}"><svg-icon name="octicon-search" :size="16"/></i>
|
||||
<div class="ui dropdown icon button" :title="textFilter">
|
||||
<svg-icon name="octicon-filter" :size="16"/>
|
||||
<div class="menu">
|
||||
|
|
|
@ -218,17 +218,24 @@ export function initAdminCommon() {
|
|||
});
|
||||
|
||||
// Select actions
|
||||
const $checkboxes = $('.select.table .ui.checkbox');
|
||||
const checkboxes = document.querySelectorAll('.select.table .ui.checkbox input');
|
||||
|
||||
$('.select.action').on('click', function () {
|
||||
switch ($(this).data('action')) {
|
||||
case 'select-all':
|
||||
$checkboxes.checkbox('check');
|
||||
for (const checkbox of checkboxes) {
|
||||
checkbox.checked = true;
|
||||
}
|
||||
break;
|
||||
case 'deselect-all':
|
||||
$checkboxes.checkbox('uncheck');
|
||||
for (const checkbox of checkboxes) {
|
||||
checkbox.checked = false;
|
||||
}
|
||||
break;
|
||||
case 'inverse':
|
||||
$checkboxes.checkbox('toggle');
|
||||
for (const checkbox of checkboxes) {
|
||||
checkbox.checked = !checkbox.checked;
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
@ -236,11 +243,11 @@ export function initAdminCommon() {
|
|||
e.preventDefault();
|
||||
this.classList.add('is-loading', 'disabled');
|
||||
const data = new FormData();
|
||||
$checkboxes.each(function () {
|
||||
if ($(this).checkbox('is checked')) {
|
||||
data.append('ids[]', this.getAttribute('data-id'));
|
||||
for (const checkbox of checkboxes) {
|
||||
if (checkbox.checked) {
|
||||
data.append('ids[]', checkbox.closest('.ui.checkbox').getAttribute('data-id'));
|
||||
}
|
||||
});
|
||||
}
|
||||
await POST(this.getAttribute('data-link'), {data});
|
||||
window.location.href = this.getAttribute('data-redirect');
|
||||
});
|
||||
|
|
|
@ -1,12 +1,66 @@
|
|||
import $ from 'jquery';
|
||||
import {createTippy} from '../modules/tippy.js';
|
||||
|
||||
export async function createColorPicker(els) {
|
||||
export async function initColorPickers() {
|
||||
const els = document.getElementsByClassName('js-color-picker-input');
|
||||
if (!els.length) return;
|
||||
|
||||
await Promise.all([
|
||||
import(/* webpackChunkName: "minicolors" */'@claviska/jquery-minicolors'),
|
||||
import(/* webpackChunkName: "minicolors" */'@claviska/jquery-minicolors/jquery.minicolors.css'),
|
||||
import(/* webpackChunkName: "colorpicker" */'vanilla-colorful/hex-color-picker.js'),
|
||||
import(/* webpackChunkName: "colorpicker" */'../../css/features/colorpicker.css'),
|
||||
]);
|
||||
|
||||
return $(els).minicolors();
|
||||
for (const el of els) {
|
||||
initPicker(el);
|
||||
}
|
||||
}
|
||||
|
||||
function updateSquare(el, newValue) {
|
||||
el.style.color = /#[0-9a-f]{6}/i.test(newValue) ? newValue : 'transparent';
|
||||
}
|
||||
|
||||
function updatePicker(el, newValue) {
|
||||
el.setAttribute('color', newValue);
|
||||
}
|
||||
|
||||
function initPicker(el) {
|
||||
const input = el.querySelector('input');
|
||||
|
||||
const square = document.createElement('div');
|
||||
square.classList.add('preview-square');
|
||||
updateSquare(square, input.value);
|
||||
el.append(square);
|
||||
|
||||
const picker = document.createElement('hex-color-picker');
|
||||
picker.addEventListener('color-changed', (e) => {
|
||||
input.value = e.detail.value;
|
||||
input.focus();
|
||||
updateSquare(square, e.detail.value);
|
||||
});
|
||||
|
||||
input.addEventListener('input', (e) => {
|
||||
updateSquare(square, e.target.value);
|
||||
updatePicker(picker, e.target.value);
|
||||
});
|
||||
|
||||
createTippy(input, {
|
||||
trigger: 'focus click',
|
||||
theme: 'bare',
|
||||
hideOnClick: true,
|
||||
content: picker,
|
||||
placement: 'bottom-start',
|
||||
interactive: true,
|
||||
onShow() {
|
||||
updatePicker(picker, input.value);
|
||||
},
|
||||
});
|
||||
|
||||
// init precolors
|
||||
for (const colorEl of el.querySelectorAll('.precolors .color')) {
|
||||
colorEl.addEventListener('click', (e) => {
|
||||
const newValue = e.target.getAttribute('data-color-hex');
|
||||
input.value = newValue;
|
||||
input.dispatchEvent(new Event('input', {bubbles: true}));
|
||||
updateSquare(square, newValue);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ import $ from 'jquery';
|
|||
import '../vendor/jquery.are-you-sure.js';
|
||||
import {clippie} from 'clippie';
|
||||
import {createDropzone} from './dropzone.js';
|
||||
import {initCompColorPicker} from './comp/ColorPicker.js';
|
||||
import {showGlobalErrorMessage} from '../bootstrap.js';
|
||||
import {handleGlobalEnterQuickSubmit} from './comp/QuickSubmit.js';
|
||||
import {svg} from '../svg.js';
|
||||
|
@ -110,7 +109,7 @@ async function fetchActionDoRequest(actionElem, url, opt) {
|
|||
showErrorToast(`${i18n.network_error} ${e}`);
|
||||
}
|
||||
}
|
||||
actionElem.classList.remove('is-loading', 'small-loading-icon');
|
||||
actionElem.classList.remove('is-loading', 'loading-icon-2px');
|
||||
}
|
||||
|
||||
async function formFetchAction(e) {
|
||||
|
@ -122,7 +121,7 @@ async function formFetchAction(e) {
|
|||
|
||||
formEl.classList.add('is-loading');
|
||||
if (formEl.clientHeight < 50) {
|
||||
formEl.classList.add('small-loading-icon');
|
||||
formEl.classList.add('loading-icon-2px');
|
||||
}
|
||||
|
||||
const formMethod = formEl.getAttribute('method') || 'get';
|
||||
|
@ -196,8 +195,6 @@ export function initGlobalCommon() {
|
|||
$uiDropdowns.filter('.upward').dropdown('setting', 'direction', 'upward');
|
||||
$uiDropdowns.filter('.downward').dropdown('setting', 'direction', 'downward');
|
||||
|
||||
$('.ui.checkbox').checkbox();
|
||||
|
||||
$('.tabular.menu .item').tab();
|
||||
|
||||
initSubmitEventPolyfill();
|
||||
|
@ -379,10 +376,7 @@ function initGlobalShowModal() {
|
|||
$attrTarget.text(attrib.value); // FIXME: it should be more strict here, only handle div/span/p
|
||||
}
|
||||
}
|
||||
const $colorPickers = $modal.find('.color-picker');
|
||||
if ($colorPickers.length > 0) {
|
||||
initCompColorPicker(); // FIXME: this might cause duplicate init
|
||||
}
|
||||
|
||||
$modal.modal('setting', {
|
||||
onApprove: () => {
|
||||
// "form-fetch-action" can handle network errors gracefully,
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
import $ from 'jquery';
|
||||
import {createColorPicker} from '../colorpicker.js';
|
||||
|
||||
export function initCompColorPicker() {
|
||||
(async () => {
|
||||
await createColorPicker(document.querySelectorAll('.color-picker'));
|
||||
|
||||
for (const el of document.querySelectorAll('.precolors .color')) {
|
||||
el.addEventListener('click', (e) => {
|
||||
const color = e.target.getAttribute('data-color-hex');
|
||||
const parent = e.target.closest('.color.picker');
|
||||
$(parent.querySelector('.color-picker')).minicolors('value', color);
|
||||
});
|
||||
}
|
||||
})();
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
import $ from 'jquery';
|
||||
import {initCompColorPicker} from './ColorPicker.js';
|
||||
|
||||
function isExclusiveScopeName(name) {
|
||||
return /.*[^/]\/[^/].*/.test(name);
|
||||
|
@ -28,13 +27,17 @@ function updateExclusiveLabelEdit(form) {
|
|||
|
||||
export function initCompLabelEdit(selector) {
|
||||
if (!$(selector).length) return;
|
||||
initCompColorPicker();
|
||||
|
||||
// Create label
|
||||
$('.new-label.button').on('click', () => {
|
||||
updateExclusiveLabelEdit('.new-label');
|
||||
$('.new-label.modal').modal({
|
||||
onApprove() {
|
||||
const form = document.querySelector('.new-label.form');
|
||||
if (!form.checkValidity()) {
|
||||
form.reportValidity();
|
||||
return false;
|
||||
}
|
||||
$('.new-label.form').trigger('submit');
|
||||
},
|
||||
}).modal('show');
|
||||
|
@ -60,10 +63,18 @@ export function initCompLabelEdit(selector) {
|
|||
updateExclusiveLabelEdit('.edit-label');
|
||||
|
||||
$('.edit-label .label-desc-input').val(this.getAttribute('data-description'));
|
||||
$('.edit-label .color-picker').minicolors('value', this.getAttribute('data-color'));
|
||||
|
||||
const colorInput = document.querySelector('.edit-label .js-color-picker-input input');
|
||||
colorInput.value = this.getAttribute('data-color');
|
||||
colorInput.dispatchEvent(new Event('input', {bubbles: true}));
|
||||
|
||||
$('.edit-label.modal').modal({
|
||||
onApprove() {
|
||||
const form = document.querySelector('.edit-label.form');
|
||||
if (!form.checkValidity()) {
|
||||
form.reportValidity();
|
||||
return false;
|
||||
}
|
||||
$('.edit-label.form').trigger('submit');
|
||||
},
|
||||
}).modal('show');
|
||||
|
|
|
@ -19,7 +19,7 @@ export function initCopyContent() {
|
|||
// the text to copy is not in the DOM or it is an image which should be
|
||||
// fetched to copy in full resolution
|
||||
if (link) {
|
||||
btn.classList.add('is-loading', 'small-loading-icon');
|
||||
btn.classList.add('is-loading', 'loading-icon-2px');
|
||||
try {
|
||||
const res = await GET(link, {credentials: 'include', redirect: 'follow'});
|
||||
const contentType = res.headers.get('content-type');
|
||||
|
@ -33,7 +33,7 @@ export function initCopyContent() {
|
|||
} catch {
|
||||
return showTemporaryTooltip(btn, i18n.copy_error);
|
||||
} finally {
|
||||
btn.classList.remove('is-loading', 'small-loading-icon');
|
||||
btn.classList.remove('is-loading', 'loading-icon-2px');
|
||||
}
|
||||
} else { // text, read from DOM
|
||||
const lineEls = document.querySelectorAll('.file-view .lines-code');
|
||||
|
|
|
@ -110,15 +110,15 @@ export function initImageDiff() {
|
|||
const $imagesAfter = imageInfos[0].$images;
|
||||
const $imagesBefore = imageInfos[1].$images;
|
||||
|
||||
initSideBySide(createContext($imagesAfter[0], $imagesBefore[0]));
|
||||
initSideBySide(this, createContext($imagesAfter[0], $imagesBefore[0]));
|
||||
if ($imagesAfter.length > 0 && $imagesBefore.length > 0) {
|
||||
initSwipe(createContext($imagesAfter[1], $imagesBefore[1]));
|
||||
initOverlay(createContext($imagesAfter[2], $imagesBefore[2]));
|
||||
}
|
||||
|
||||
$container.find('> .image-diff-tabs').removeClass('is-loading');
|
||||
this.querySelector(':scope > .image-diff-tabs')?.classList.remove('is-loading');
|
||||
|
||||
function initSideBySide(sizes) {
|
||||
function initSideBySide(container, sizes) {
|
||||
let factor = 1;
|
||||
if (sizes.max.width > (diffContainerWidth - 24) / 2) {
|
||||
factor = (diffContainerWidth - 24) / 2 / sizes.max.width;
|
||||
|
@ -126,13 +126,24 @@ export function initImageDiff() {
|
|||
|
||||
const widthChanged = sizes.$image1.length !== 0 && sizes.$image2.length !== 0 && sizes.$image1[0].naturalWidth !== sizes.$image2[0].naturalWidth;
|
||||
const heightChanged = sizes.$image1.length !== 0 && sizes.$image2.length !== 0 && sizes.$image1[0].naturalHeight !== sizes.$image2[0].naturalHeight;
|
||||
if (sizes.$image1.length !== 0) {
|
||||
$container.find('.bounds-info-after .bounds-info-width').text(`${sizes.$image1[0].naturalWidth}px`).addClass(widthChanged ? 'green' : '');
|
||||
$container.find('.bounds-info-after .bounds-info-height').text(`${sizes.$image1[0].naturalHeight}px`).addClass(heightChanged ? 'green' : '');
|
||||
if (sizes.$image1?.length) {
|
||||
const boundsInfoAfterWidth = container.querySelector('.bounds-info-after .bounds-info-width');
|
||||
boundsInfoAfterWidth.textContent = `${sizes.$image1[0].naturalWidth}px`;
|
||||
if (widthChanged) boundsInfoAfterWidth.classList.add('green');
|
||||
|
||||
const boundsInfoAfterHeight = container.querySelector('.bounds-info-after .bounds-info-height');
|
||||
boundsInfoAfterHeight.textContent = `${sizes.$image1[0].naturalHeight}px`;
|
||||
if (heightChanged) boundsInfoAfterHeight.classList.add('green');
|
||||
}
|
||||
if (sizes.$image2.length !== 0) {
|
||||
$container.find('.bounds-info-before .bounds-info-width').text(`${sizes.$image2[0].naturalWidth}px`).addClass(widthChanged ? 'red' : '');
|
||||
$container.find('.bounds-info-before .bounds-info-height').text(`${sizes.$image2[0].naturalHeight}px`).addClass(heightChanged ? 'red' : '');
|
||||
|
||||
if (sizes.$image2?.length) {
|
||||
const boundsInfoBeforeWidth = container.querySelector('.bounds-info-before .bounds-info-width');
|
||||
boundsInfoBeforeWidth.textContent = `${sizes.$image2[0].naturalWidth}px`;
|
||||
if (widthChanged) boundsInfoBeforeWidth.classList.add('red');
|
||||
|
||||
const boundsInfoBeforeHeight = container.querySelector('.bounds-info-before .bounds-info-height');
|
||||
boundsInfoBeforeHeight.textContent = `${sizes.$image2[0].naturalHeight}px`;
|
||||
if (heightChanged) boundsInfoBeforeHeight.classList.add('red');
|
||||
}
|
||||
|
||||
const image1 = sizes.$image1[0];
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import $ from 'jquery';
|
||||
import {GET} from '../modules/fetch.js';
|
||||
import {toggleElem} from '../utils/dom.js';
|
||||
|
||||
const {appSubUrl, notificationSettings, assetVersionEncoded} = window.config;
|
||||
let notificationSequenceNumber = 0;
|
||||
|
@ -177,14 +178,11 @@ async function updateNotificationCount() {
|
|||
|
||||
const data = await response.json();
|
||||
|
||||
const $notificationCount = $('.notification_count');
|
||||
if (data.new === 0) {
|
||||
$notificationCount.addClass('tw-hidden');
|
||||
} else {
|
||||
$notificationCount.removeClass('tw-hidden');
|
||||
}
|
||||
toggleElem('.notification_count', data.new !== 0);
|
||||
|
||||
$notificationCount.text(`${data.new}`);
|
||||
for (const el of document.getElementsByClassName('notification_count')) {
|
||||
el.textContent = `${data.new}`;
|
||||
}
|
||||
|
||||
return `${data.new}`;
|
||||
} catch (error) {
|
||||
|
|
|
@ -25,7 +25,9 @@ function getLineEls() {
|
|||
}
|
||||
|
||||
function selectRange($linesEls, $selectionEndEl, $selectionStartEls) {
|
||||
$linesEls.closest('tr').removeClass('active');
|
||||
for (const el of $linesEls) {
|
||||
el.closest('tr').classList.remove('active');
|
||||
}
|
||||
|
||||
// add hashchange to permalink
|
||||
const refInNewIssue = document.querySelector('a.ref-in-new-issue');
|
||||
|
@ -72,7 +74,7 @@ function selectRange($linesEls, $selectionEndEl, $selectionStartEls) {
|
|||
classes.push(`[rel=L${i}]`);
|
||||
}
|
||||
$linesEls.filter(classes.join(',')).each(function () {
|
||||
$(this).closest('tr').addClass('active');
|
||||
this.closest('tr').classList.add('active');
|
||||
});
|
||||
changeHash(`#L${a}-L${b}`);
|
||||
|
||||
|
@ -82,7 +84,7 @@ function selectRange($linesEls, $selectionEndEl, $selectionStartEls) {
|
|||
return;
|
||||
}
|
||||
}
|
||||
$selectionEndEl.closest('tr').addClass('active');
|
||||
$selectionEndEl[0].closest('tr').classList.add('active');
|
||||
changeHash(`#${$selectionEndEl[0].getAttribute('rel')}`);
|
||||
|
||||
updateIssueHref($selectionEndEl[0].getAttribute('rel'));
|
||||
|
|
|
@ -7,7 +7,7 @@ import {validateTextareaNonEmpty} from './comp/ComboMarkdownEditor.js';
|
|||
import {initViewedCheckboxListenerFor, countAndUpdateViewedFiles, initExpandAndCollapseFilesButton} from './pull-view-file.js';
|
||||
import {initImageDiff} from './imagediff.js';
|
||||
import {showErrorToast} from '../modules/toast.js';
|
||||
import {submitEventSubmitter} from '../utils/dom.js';
|
||||
import {submitEventSubmitter, queryElemSiblings, hideElem, showElem} from '../utils/dom.js';
|
||||
import {POST, GET} from '../modules/fetch.js';
|
||||
|
||||
const {pageData, i18n} = window.config;
|
||||
|
@ -16,7 +16,6 @@ function initRepoDiffReviewButton() {
|
|||
const reviewBox = document.getElementById('review-box');
|
||||
if (!reviewBox) return;
|
||||
|
||||
const $reviewBox = $(reviewBox);
|
||||
const counter = reviewBox.querySelector('.review-comments-counter');
|
||||
if (!counter) return;
|
||||
|
||||
|
@ -27,23 +26,27 @@ function initRepoDiffReviewButton() {
|
|||
const num = parseInt(counter.getAttribute('data-pending-comment-number')) + 1 || 1;
|
||||
counter.setAttribute('data-pending-comment-number', num);
|
||||
counter.textContent = num;
|
||||
// Force the browser to reflow the DOM. This is to ensure that the browser replay the animation
|
||||
$reviewBox.removeClass('pulse');
|
||||
$reviewBox.width();
|
||||
$reviewBox.addClass('pulse');
|
||||
|
||||
reviewBox.classList.remove('pulse');
|
||||
requestAnimationFrame(() => {
|
||||
reviewBox.classList.add('pulse');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function initRepoDiffFileViewToggle() {
|
||||
$('.file-view-toggle').on('click', function () {
|
||||
const $this = $(this);
|
||||
$this.parent().children().removeClass('active');
|
||||
$this.addClass('active');
|
||||
for (const el of queryElemSiblings(this)) {
|
||||
el.classList.remove('active');
|
||||
}
|
||||
this.classList.add('active');
|
||||
|
||||
const $target = $($this.data('toggle-selector'));
|
||||
$target.parent().children().addClass('tw-hidden');
|
||||
$target.removeClass('tw-hidden');
|
||||
const target = document.querySelector(this.getAttribute('data-toggle-selector'));
|
||||
if (!target) return;
|
||||
|
||||
hideElem(queryElemSiblings(target));
|
||||
showElem(target);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -57,9 +60,9 @@ function initRepoDiffConversationForm() {
|
|||
return;
|
||||
}
|
||||
|
||||
if ($form.hasClass('is-loading')) return;
|
||||
if (e.target.classList.contains('is-loading')) return;
|
||||
try {
|
||||
$form.addClass('is-loading');
|
||||
e.target.classList.add('is-loading');
|
||||
const formData = new FormData($form[0]);
|
||||
|
||||
// If the form is submitted by a button, append the button's name and value to the form data.
|
||||
|
@ -76,10 +79,14 @@ function initRepoDiffConversationForm() {
|
|||
const {path, side, idx} = $newConversationHolder.data();
|
||||
|
||||
$form.closest('.conversation-holder').replaceWith($newConversationHolder);
|
||||
let selector;
|
||||
if ($form.closest('tr').data('line-type') === 'same') {
|
||||
$(`[data-path="${path}"] .add-code-comment[data-idx="${idx}"]`).addClass('tw-invisible');
|
||||
selector = `[data-path="${path}"] .add-code-comment[data-idx="${idx}"]`;
|
||||
} else {
|
||||
$(`[data-path="${path}"] .add-code-comment[data-side="${side}"][data-idx="${idx}"]`).addClass('tw-invisible');
|
||||
selector = `[data-path="${path}"] .add-code-comment[data-side="${side}"][data-idx="${idx}"]`;
|
||||
}
|
||||
for (const el of document.querySelectorAll(selector)) {
|
||||
el.classList.add('tw-invisible');
|
||||
}
|
||||
$newConversationHolder.find('.dropdown').dropdown();
|
||||
initCompReactionSelector($newConversationHolder);
|
||||
|
@ -87,7 +94,7 @@ function initRepoDiffConversationForm() {
|
|||
console.error('error when submitting conversation', e);
|
||||
showErrorToast(i18n.network_error);
|
||||
} finally {
|
||||
$form.removeClass('is-loading');
|
||||
e.target.classList.remove('is-loading');
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -147,13 +154,13 @@ function onShowMoreFiles() {
|
|||
}
|
||||
|
||||
export async function loadMoreFiles(url) {
|
||||
const $target = $('a#diff-show-more-files');
|
||||
if ($target.hasClass('disabled') || pageData.diffFileInfo.isLoadingNewData) {
|
||||
const target = document.querySelector('a#diff-show-more-files');
|
||||
if (target?.classList.contains('disabled') || pageData.diffFileInfo.isLoadingNewData) {
|
||||
return;
|
||||
}
|
||||
|
||||
pageData.diffFileInfo.isLoadingNewData = true;
|
||||
$target.addClass('disabled');
|
||||
target?.classList.add('disabled');
|
||||
|
||||
try {
|
||||
const response = await GET(url);
|
||||
|
@ -170,7 +177,7 @@ export async function loadMoreFiles(url) {
|
|||
console.error('Error:', error);
|
||||
showErrorToast('An error occurred while loading more files.');
|
||||
} finally {
|
||||
$target.removeClass('disabled');
|
||||
target?.classList.remove('disabled');
|
||||
pageData.diffFileInfo.isLoadingNewData = false;
|
||||
}
|
||||
}
|
||||
|
@ -187,11 +194,11 @@ function initRepoDiffShowMore() {
|
|||
e.preventDefault();
|
||||
const $target = $(e.target);
|
||||
|
||||
if ($target.hasClass('disabled')) {
|
||||
if (e.target.classList.contains('disabled')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$target.addClass('disabled');
|
||||
e.target.classList.add('disabled');
|
||||
|
||||
const url = $target.data('href');
|
||||
|
||||
|
@ -207,7 +214,7 @@ function initRepoDiffShowMore() {
|
|||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
} finally {
|
||||
$target.removeClass('disabled');
|
||||
e.target.classList.remove('disabled');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -147,8 +147,8 @@ export function initRepoEditor() {
|
|||
silent: true,
|
||||
dirtyClass: dirtyFileClass,
|
||||
fieldSelector: ':input:not(.commit-form-wrapper :input)',
|
||||
change() {
|
||||
const dirty = $(this).hasClass(dirtyFileClass);
|
||||
change($form) {
|
||||
const dirty = $form[0]?.classList.contains(dirtyFileClass);
|
||||
commitButton.disabled = !dirty;
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,55 +1,53 @@
|
|||
import $ from 'jquery';
|
||||
import {stripTags} from '../utils.js';
|
||||
import {hideElem, showElem} from '../utils/dom.js';
|
||||
import {hideElem, queryElemChildren, showElem} from '../utils/dom.js';
|
||||
import {POST} from '../modules/fetch.js';
|
||||
import {showErrorToast} from '../modules/toast.js';
|
||||
|
||||
const {appSubUrl} = window.config;
|
||||
|
||||
export function initRepoTopicBar() {
|
||||
const mgrBtn = document.getElementById('manage_topic');
|
||||
if (!mgrBtn) return;
|
||||
|
||||
const editDiv = document.getElementById('topic_edit');
|
||||
const viewDiv = document.getElementById('repo-topics');
|
||||
const saveBtn = document.getElementById('save_topic');
|
||||
const topicDropdown = editDiv.querySelector('.dropdown');
|
||||
const $topicDropdown = $(topicDropdown);
|
||||
const $topicForm = $(editDiv);
|
||||
const $topicDropdownSearch = $topicDropdown.find('input.search');
|
||||
const topicPrompts = {
|
||||
countPrompt: topicDropdown.getAttribute('data-text-count-prompt') ?? undefined,
|
||||
formatPrompt: topicDropdown.getAttribute('data-text-format-prompt') ?? undefined,
|
||||
};
|
||||
const topicDropdown = editDiv.querySelector('.ui.dropdown');
|
||||
let lastErrorToast;
|
||||
|
||||
mgrBtn.addEventListener('click', () => {
|
||||
hideElem(viewDiv);
|
||||
showElem(editDiv);
|
||||
$topicDropdownSearch.trigger('focus');
|
||||
topicDropdown.querySelector('input.search').focus();
|
||||
});
|
||||
|
||||
$('#cancel_topic_edit').on('click', () => {
|
||||
document.querySelector('#cancel_topic_edit').addEventListener('click', () => {
|
||||
lastErrorToast?.hideToast();
|
||||
hideElem(editDiv);
|
||||
showElem(viewDiv);
|
||||
mgrBtn.focus();
|
||||
});
|
||||
|
||||
saveBtn.addEventListener('click', async () => {
|
||||
const topics = $('input[name=topics]').val();
|
||||
document.getElementById('save_topic').addEventListener('click', async (e) => {
|
||||
lastErrorToast?.hideToast();
|
||||
const topics = editDiv.querySelector('input[name=topics]').value;
|
||||
|
||||
const data = new FormData();
|
||||
data.append('topics', topics);
|
||||
|
||||
const response = await POST(saveBtn.getAttribute('data-link'), {data});
|
||||
const response = await POST(e.target.getAttribute('data-link'), {data});
|
||||
|
||||
if (response.ok) {
|
||||
const responseData = await response.json();
|
||||
if (responseData.status === 'ok') {
|
||||
$(viewDiv).children('.topic').remove();
|
||||
queryElemChildren(viewDiv, '.repo-topic', (el) => el.remove());
|
||||
if (topics.length) {
|
||||
const topicArray = topics.split(',');
|
||||
topicArray.sort();
|
||||
for (const topic of topicArray) {
|
||||
// it should match the code in repo/home.tmpl
|
||||
const link = document.createElement('a');
|
||||
link.classList.add('ui', 'repo-topic', 'large', 'label', 'topic', 'tw-m-0');
|
||||
link.classList.add('repo-topic', 'ui', 'large', 'label');
|
||||
link.href = `${appSubUrl}/explore/repos?q=${encodeURIComponent(topic)}&topic=1`;
|
||||
link.textContent = topic;
|
||||
mgrBtn.parentNode.insertBefore(link, mgrBtn); // insert all new topics before manage button
|
||||
|
@ -59,27 +57,23 @@ export function initRepoTopicBar() {
|
|||
showElem(viewDiv);
|
||||
}
|
||||
} else if (response.status === 422) {
|
||||
// how to test: input topic like " invalid topic " (with spaces), and select it from the list, then "Save"
|
||||
const responseData = await response.json();
|
||||
lastErrorToast = showErrorToast(responseData.message, {duration: 5000});
|
||||
if (responseData.invalidTopics.length > 0) {
|
||||
topicPrompts.formatPrompt = responseData.message;
|
||||
|
||||
const {invalidTopics} = responseData;
|
||||
const $topicLabels = $topicDropdown.children('a.ui.label');
|
||||
const topicLabels = queryElemChildren(topicDropdown, 'a.ui.label');
|
||||
for (const [index, value] of topics.split(',').entries()) {
|
||||
if (invalidTopics.includes(value)) {
|
||||
$topicLabels.eq(index).removeClass('green').addClass('red');
|
||||
topicLabels[index].classList.remove('green');
|
||||
topicLabels[index].classList.add('red');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
topicPrompts.countPrompt = responseData.message;
|
||||
}
|
||||
}
|
||||
|
||||
// Always validate the form
|
||||
$topicForm.form('validate form');
|
||||
});
|
||||
|
||||
$topicDropdown.dropdown({
|
||||
$(topicDropdown).dropdown({
|
||||
allowAdditions: true,
|
||||
forceSelection: false,
|
||||
fullTextSearch: 'exact',
|
||||
|
@ -102,9 +96,9 @@ export function initRepoTopicBar() {
|
|||
const query = stripTags(this.urlData.query.trim());
|
||||
let found_query = false;
|
||||
const current_topics = [];
|
||||
$topicDropdown.find('a.label.visible').each((_, el) => {
|
||||
for (const el of queryElemChildren(topicDropdown, 'a.ui.label.visible')) {
|
||||
current_topics.push(el.getAttribute('data-value'));
|
||||
});
|
||||
}
|
||||
|
||||
if (res.topics) {
|
||||
let found = false;
|
||||
|
@ -146,38 +140,8 @@ export function initRepoTopicBar() {
|
|||
},
|
||||
onAdd(addedValue, _addedText, $addedChoice) {
|
||||
addedValue = addedValue.toLowerCase().trim();
|
||||
$($addedChoice)[0].setAttribute('data-value', addedValue);
|
||||
$($addedChoice)[0].setAttribute('data-text', addedValue);
|
||||
},
|
||||
});
|
||||
|
||||
$.fn.form.settings.rules.validateTopic = function (_values, regExp) {
|
||||
const $topics = $topicDropdown.children('a.ui.label');
|
||||
const status = !$topics.length || $topics.last()[0].getAttribute('data-value').match(regExp);
|
||||
if (!status) {
|
||||
$topics.last().removeClass('green').addClass('red');
|
||||
}
|
||||
return status && !$topicDropdown.children('a.ui.label.red').length;
|
||||
};
|
||||
|
||||
$topicForm.form({
|
||||
on: 'change',
|
||||
inline: true,
|
||||
fields: {
|
||||
topics: {
|
||||
identifier: 'topics',
|
||||
rules: [
|
||||
{
|
||||
type: 'validateTopic',
|
||||
value: /^\s*[a-z0-9][-.a-z0-9]{0,35}\s*$/,
|
||||
prompt: topicPrompts.formatPrompt,
|
||||
},
|
||||
{
|
||||
type: 'maxCount[25]',
|
||||
prompt: topicPrompts.countPrompt,
|
||||
},
|
||||
],
|
||||
},
|
||||
$addedChoice[0].setAttribute('data-value', addedValue);
|
||||
$addedChoice[0].setAttribute('data-text', addedValue);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import $ from 'jquery';
|
|||
import {svg} from '../svg.js';
|
||||
import {showErrorToast} from '../modules/toast.js';
|
||||
import {GET, POST} from '../modules/fetch.js';
|
||||
import {showElem} from '../utils/dom.js';
|
||||
|
||||
const {appSubUrl} = window.config;
|
||||
let i18nTextEdited;
|
||||
|
@ -73,10 +74,12 @@ function showContentHistoryDetail(issueBaseUrl, commentId, historyId, itemTitleH
|
|||
const response = await GET(url);
|
||||
const resp = await response.json();
|
||||
|
||||
$dialog.find('.comment-diff-data').removeClass('is-loading').html(resp.diffHtml);
|
||||
const commentDiffData = $dialog.find('.comment-diff-data')[0];
|
||||
commentDiffData?.classList.remove('is-loading');
|
||||
commentDiffData.innerHTML = resp.diffHtml;
|
||||
// there is only one option "item[data-option-item=delete]", so the dropdown can be entirely shown/hidden.
|
||||
if (resp.canSoftDelete) {
|
||||
$dialog.find('.dialog-header-options').removeClass('tw-hidden');
|
||||
showElem($dialog.find('.dialog-header-options'));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
|
|
|
@ -6,6 +6,7 @@ import {confirmModal} from './comp/ConfirmModal.js';
|
|||
import {showErrorToast} from '../modules/toast.js';
|
||||
import {createSortable} from '../modules/sortable.js';
|
||||
import {DELETE, POST} from '../modules/fetch.js';
|
||||
import {parseDom} from '../utils.js';
|
||||
|
||||
function initRepoIssueListCheckboxes() {
|
||||
const issueSelectAll = document.querySelector('.issue-checkbox-all');
|
||||
|
@ -129,22 +130,29 @@ function initRepoIssueListAuthorDropdown() {
|
|||
const dropdownTemplates = $searchDropdown.dropdown('setting', 'templates');
|
||||
$searchDropdown.dropdown('internal', 'setup', dropdownSetup);
|
||||
dropdownSetup.menu = function (values) {
|
||||
const $menu = $searchDropdown.find('> .menu');
|
||||
$menu.find('> .dynamic-item').remove(); // remove old dynamic items
|
||||
const menu = $searchDropdown.find('> .menu')[0];
|
||||
// remove old dynamic items
|
||||
for (const el of menu.querySelectorAll(':scope > .dynamic-item')) {
|
||||
el.remove();
|
||||
}
|
||||
|
||||
const newMenuHtml = dropdownTemplates.menu(values, $searchDropdown.dropdown('setting', 'fields'), true /* html */, $searchDropdown.dropdown('setting', 'className'));
|
||||
if (newMenuHtml) {
|
||||
const $newMenuItems = $(newMenuHtml);
|
||||
$newMenuItems.addClass('dynamic-item');
|
||||
const newMenuItems = parseDom(newMenuHtml, 'text/html').querySelectorAll('body > div');
|
||||
for (const newMenuItem of newMenuItems) {
|
||||
newMenuItem.classList.add('dynamic-item');
|
||||
}
|
||||
const div = document.createElement('div');
|
||||
div.classList.add('divider', 'dynamic-item');
|
||||
$menu[0].append(div, ...$newMenuItems);
|
||||
menu.append(div, ...newMenuItems);
|
||||
}
|
||||
$searchDropdown.dropdown('refresh');
|
||||
// defer our selection to the next tick, because dropdown will set the selection item after this `menu` function
|
||||
setTimeout(() => {
|
||||
$menu.find('.item.active, .item.selected').removeClass('active selected');
|
||||
$menu.find(`.item[data-value="${selectedUserId}"]`).addClass('selected');
|
||||
for (const el of menu.querySelectorAll('.item.active, .item.selected')) {
|
||||
el.classList.remove('active', 'selected');
|
||||
}
|
||||
menu.querySelector(`.item[data-value="${selectedUserId}"]`)?.classList.add('selected');
|
||||
}, 0);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -158,17 +158,22 @@ export function initRepoIssueSidebarList() {
|
|||
|
||||
export function initRepoIssueCommentDelete() {
|
||||
// Delete comment
|
||||
$(document).on('click', '.delete-comment', async function () {
|
||||
const $this = $(this);
|
||||
if (window.confirm($this.data('locale'))) {
|
||||
document.addEventListener('click', async (e) => {
|
||||
if (!e.target.matches('.delete-comment')) return;
|
||||
e.preventDefault();
|
||||
|
||||
const deleteButton = e.target;
|
||||
if (window.confirm(deleteButton.getAttribute('data-locale'))) {
|
||||
try {
|
||||
const response = await POST($this.data('url'));
|
||||
const response = await POST(deleteButton.getAttribute('data-url'));
|
||||
if (!response.ok) throw new Error('Failed to delete comment');
|
||||
const $conversationHolder = $this.closest('.conversation-holder');
|
||||
const $parentTimelineItem = $this.closest('.timeline-item');
|
||||
const $parentTimelineGroup = $this.closest('.timeline-item-group');
|
||||
|
||||
const conversationHolder = deleteButton.closest('.conversation-holder');
|
||||
const parentTimelineItem = deleteButton.closest('.timeline-item');
|
||||
const parentTimelineGroup = deleteButton.closest('.timeline-item-group');
|
||||
|
||||
// Check if this was a pending comment.
|
||||
if ($conversationHolder.find('.pending-label').length) {
|
||||
if (conversationHolder?.querySelector('.pending-label')) {
|
||||
const counter = document.querySelector('#review-box .review-comments-counter');
|
||||
let num = parseInt(counter?.getAttribute('data-pending-comment-number')) - 1 || 0;
|
||||
num = Math.max(num, 0);
|
||||
|
@ -176,29 +181,32 @@ export function initRepoIssueCommentDelete() {
|
|||
counter.textContent = String(num);
|
||||
}
|
||||
|
||||
$(`#${$this.data('comment-id')}`).remove();
|
||||
if ($conversationHolder.length && !$conversationHolder.find('.comment').length) {
|
||||
const path = $conversationHolder.data('path');
|
||||
const side = $conversationHolder.data('side');
|
||||
const idx = $conversationHolder.data('idx');
|
||||
const lineType = $conversationHolder.closest('tr').data('line-type');
|
||||
document.getElementById(deleteButton.getAttribute('data-comment-id'))?.remove();
|
||||
|
||||
if (conversationHolder && !conversationHolder.querySelector('.comment')) {
|
||||
const path = conversationHolder.getAttribute('data-path');
|
||||
const side = conversationHolder.getAttribute('data-side');
|
||||
const idx = conversationHolder.getAttribute('data-idx');
|
||||
const lineType = conversationHolder.closest('tr').getAttribute('data-line-type');
|
||||
|
||||
if (lineType === 'same') {
|
||||
$(`[data-path="${path}"] .add-code-comment[data-idx="${idx}"]`).removeClass('tw-invisible');
|
||||
document.querySelector(`[data-path="${path}"] .add-code-comment[data-idx="${idx}"]`).classList.remove('tw-invisible');
|
||||
} else {
|
||||
$(`[data-path="${path}"] .add-code-comment[data-side="${side}"][data-idx="${idx}"]`).removeClass('tw-invisible');
|
||||
document.querySelector(`[data-path="${path}"] .add-code-comment[data-side="${side}"][data-idx="${idx}"]`).classList.remove('tw-invisible');
|
||||
}
|
||||
$conversationHolder.remove();
|
||||
|
||||
conversationHolder.remove();
|
||||
}
|
||||
|
||||
// Check if there is no review content, move the time avatar upward to avoid overlapping the content below.
|
||||
if (!$parentTimelineGroup.find('.timeline-item.comment').length && !$parentTimelineItem.find('.conversation-holder').length) {
|
||||
const $timelineAvatar = $parentTimelineGroup.find('.timeline-avatar');
|
||||
$timelineAvatar.removeClass('timeline-avatar-offset');
|
||||
if (!parentTimelineGroup?.querySelector('.timeline-item.comment') && !parentTimelineItem?.querySelector('.conversation-holder')) {
|
||||
const timelineAvatar = parentTimelineGroup?.querySelector('.timeline-avatar');
|
||||
timelineAvatar?.classList.remove('timeline-avatar-offset');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -222,32 +230,35 @@ export function initRepoIssueDependencyDelete() {
|
|||
|
||||
export function initRepoIssueCodeCommentCancel() {
|
||||
// Cancel inline code comment
|
||||
$(document).on('click', '.cancel-code-comment', (e) => {
|
||||
const $form = $(e.currentTarget).closest('form');
|
||||
if ($form.length > 0 && $form.hasClass('comment-form')) {
|
||||
$form.addClass('tw-hidden');
|
||||
showElem($form.closest('.comment-code-cloud').find('button.comment-form-reply'));
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!e.target.matches('.cancel-code-comment')) return;
|
||||
|
||||
const form = e.target.closest('form');
|
||||
if (form?.classList.contains('comment-form')) {
|
||||
hideElem(form);
|
||||
showElem(form.closest('.comment-code-cloud')?.querySelectorAll('button.comment-form-reply'));
|
||||
} else {
|
||||
$form.closest('.comment-code-cloud').remove();
|
||||
form.closest('.comment-code-cloud')?.remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function initRepoPullRequestUpdate() {
|
||||
// Pull Request update button
|
||||
const $pullUpdateButton = $('.update-button > button');
|
||||
$pullUpdateButton.on('click', async function (e) {
|
||||
const pullUpdateButton = document.querySelector('.update-button > button');
|
||||
if (!pullUpdateButton) return;
|
||||
|
||||
pullUpdateButton.addEventListener('click', async function (e) {
|
||||
e.preventDefault();
|
||||
const $this = $(this);
|
||||
const redirect = $this.data('redirect');
|
||||
$this.addClass('is-loading');
|
||||
const redirect = this.getAttribute('data-redirect');
|
||||
this.classList.add('is-loading');
|
||||
let response;
|
||||
try {
|
||||
response = await POST($this.data('do'));
|
||||
response = await POST(this.getAttribute('data-do'));
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} finally {
|
||||
$this.removeClass('is-loading');
|
||||
this.classList.remove('is-loading');
|
||||
}
|
||||
let data;
|
||||
try {
|
||||
|
@ -266,10 +277,13 @@ export function initRepoPullRequestUpdate() {
|
|||
|
||||
$('.update-button > .dropdown').dropdown({
|
||||
onChange(_text, _value, $choice) {
|
||||
const $url = $choice.data('do');
|
||||
if ($url) {
|
||||
$pullUpdateButton.find('.button-text').text($choice.text());
|
||||
$pullUpdateButton.data('do', $url);
|
||||
const url = $choice[0].getAttribute('data-do');
|
||||
if (url) {
|
||||
const buttonText = pullUpdateButton.querySelector('.button-text');
|
||||
if (buttonText) {
|
||||
buttonText.textContent = $choice.text();
|
||||
}
|
||||
pullUpdateButton.setAttribute('data-do', url);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
@ -282,32 +296,26 @@ export function initRepoPullRequestMergeInstruction() {
|
|||
}
|
||||
|
||||
export function initRepoPullRequestAllowMaintainerEdit() {
|
||||
const checkbox = document.getElementById('allow-edits-from-maintainers');
|
||||
if (!checkbox) return;
|
||||
const wrapper = document.getElementById('allow-edits-from-maintainers');
|
||||
if (!wrapper) return;
|
||||
|
||||
const $checkbox = $(checkbox);
|
||||
|
||||
const promptError = checkbox.getAttribute('data-prompt-error');
|
||||
$checkbox.checkbox({
|
||||
'onChange': async () => {
|
||||
const checked = $checkbox.checkbox('is checked');
|
||||
let url = checkbox.getAttribute('data-url');
|
||||
url += '/set_allow_maintainer_edit';
|
||||
$checkbox.checkbox('set disabled');
|
||||
try {
|
||||
const response = await POST(url, {
|
||||
data: {allow_maintainer_edit: checked},
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to update maintainer edit permission');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
showTemporaryTooltip(checkbox, promptError);
|
||||
} finally {
|
||||
$checkbox.checkbox('set enabled');
|
||||
wrapper.querySelector('input[type="checkbox"]')?.addEventListener('change', async (e) => {
|
||||
const checked = e.target.checked;
|
||||
const url = `${wrapper.getAttribute('data-url')}/set_allow_maintainer_edit`;
|
||||
wrapper.classList.add('is-loading');
|
||||
e.target.disabled = true;
|
||||
try {
|
||||
const response = await POST(url, {data: {allow_maintainer_edit: checked}});
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to update maintainer edit permission');
|
||||
}
|
||||
},
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
showTemporaryTooltip(wrapper, wrapper.getAttribute('data-prompt-error'));
|
||||
} finally {
|
||||
wrapper.classList.remove('is-loading');
|
||||
e.target.disabled = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -373,10 +381,10 @@ export function initRepoIssueComments() {
|
|||
|
||||
$('.re-request-review').on('click', async function (e) {
|
||||
e.preventDefault();
|
||||
const url = $(this).data('update-url');
|
||||
const issueId = $(this).data('issue-id');
|
||||
const id = $(this).data('id');
|
||||
const isChecked = $(this).hasClass('checked');
|
||||
const url = this.getAttribute('data-update-url');
|
||||
const issueId = this.getAttribute('data-issue-id');
|
||||
const id = this.getAttribute('data-id');
|
||||
const isChecked = this.classList.contains('checked');
|
||||
|
||||
await updateIssuesMeta(url, isChecked ? 'detach' : 'attach', issueId, id);
|
||||
window.location.reload();
|
||||
|
@ -403,7 +411,7 @@ export function initRepoIssueComments() {
|
|||
export async function handleReply($el) {
|
||||
hideElem($el);
|
||||
const $form = $el.closest('.comment-code-cloud').find('.comment-form');
|
||||
$form.removeClass('tw-hidden');
|
||||
showElem($form);
|
||||
|
||||
const $textarea = $form.find('textarea');
|
||||
let editor = getComboMarkdownEditor($textarea);
|
||||
|
@ -460,20 +468,20 @@ export function initRepoPullRequestReview() {
|
|||
|
||||
$(document).on('click', '.show-outdated', function (e) {
|
||||
e.preventDefault();
|
||||
const id = $(this).data('comment');
|
||||
$(this).addClass('tw-hidden');
|
||||
$(`#code-comments-${id}`).removeClass('tw-hidden');
|
||||
$(`#code-preview-${id}`).removeClass('tw-hidden');
|
||||
$(`#hide-outdated-${id}`).removeClass('tw-hidden');
|
||||
const id = this.getAttribute('data-comment');
|
||||
hideElem(this);
|
||||
showElem(`#code-comments-${id}`);
|
||||
showElem(`#code-preview-${id}`);
|
||||
showElem(`#hide-outdated-${id}`);
|
||||
});
|
||||
|
||||
$(document).on('click', '.hide-outdated', function (e) {
|
||||
e.preventDefault();
|
||||
const id = $(this).data('comment');
|
||||
$(this).addClass('tw-hidden');
|
||||
$(`#code-comments-${id}`).addClass('tw-hidden');
|
||||
$(`#code-preview-${id}`).addClass('tw-hidden');
|
||||
$(`#show-outdated-${id}`).removeClass('tw-hidden');
|
||||
const id = this.getAttribute('data-comment');
|
||||
hideElem(this);
|
||||
hideElem(`#code-comments-${id}`);
|
||||
hideElem(`#code-preview-${id}`);
|
||||
showElem(`#show-outdated-${id}`);
|
||||
});
|
||||
|
||||
$(document).on('click', 'button.comment-form-reply', async function (e) {
|
||||
|
@ -510,18 +518,19 @@ export function initRepoPullRequestReview() {
|
|||
}
|
||||
|
||||
$(document).on('click', '.add-code-comment', async function (e) {
|
||||
if ($(e.target).hasClass('btn-add-single')) return; // https://github.com/go-gitea/gitea/issues/4745
|
||||
if (e.target.classList.contains('btn-add-single')) return; // https://github.com/go-gitea/gitea/issues/4745
|
||||
e.preventDefault();
|
||||
|
||||
const isSplit = $(this).closest('.code-diff').hasClass('code-diff-split');
|
||||
const side = $(this).data('side');
|
||||
const idx = $(this).data('idx');
|
||||
const path = $(this).closest('[data-path]').data('path');
|
||||
const $tr = $(this).closest('tr');
|
||||
const lineType = $tr.data('line-type');
|
||||
const isSplit = this.closest('.code-diff')?.classList.contains('code-diff-split');
|
||||
const side = this.getAttribute('data-side');
|
||||
const idx = this.getAttribute('data-idx');
|
||||
const path = this.closest('[data-path]')?.getAttribute('data-path');
|
||||
const tr = this.closest('tr');
|
||||
const lineType = tr.getAttribute('data-line-type');
|
||||
|
||||
let $ntr = $tr.next();
|
||||
if (!$ntr.hasClass('add-comment')) {
|
||||
const ntr = tr.nextElementSibling;
|
||||
let $ntr = $(ntr);
|
||||
if (!ntr?.classList.contains('add-comment')) {
|
||||
$ntr = $(`
|
||||
<tr class="add-comment" data-line-type="${lineType}">
|
||||
${isSplit ? `
|
||||
|
@ -531,7 +540,7 @@ export function initRepoPullRequestReview() {
|
|||
<td class="add-comment-left add-comment-right" colspan="5"></td>
|
||||
`}
|
||||
</tr>`);
|
||||
$tr.after($ntr);
|
||||
$(tr).after($ntr);
|
||||
}
|
||||
|
||||
const $td = $ntr.find(`.add-comment-${side}`);
|
||||
|
@ -617,13 +626,13 @@ export function initRepoIssueTitleEdit() {
|
|||
|
||||
const editTitleToggle = function () {
|
||||
toggleElem($issueTitle);
|
||||
toggleElem($('.not-in-edit'));
|
||||
toggleElem($('#edit-title-input'));
|
||||
toggleElem($('#pull-desc'));
|
||||
toggleElem($('#pull-desc-edit'));
|
||||
toggleElem($('.in-edit'));
|
||||
toggleElem($('.new-issue-button'));
|
||||
$('#issue-title-wrapper').toggleClass('edit-active');
|
||||
toggleElem('.not-in-edit');
|
||||
toggleElem('#edit-title-input');
|
||||
toggleElem('#pull-desc');
|
||||
toggleElem('#pull-desc-edit');
|
||||
toggleElem('.in-edit');
|
||||
toggleElem('.new-issue-button');
|
||||
document.getElementById('issue-title-wrapper')?.classList.toggle('edit-active');
|
||||
$editInput[0].focus();
|
||||
$editInput[0].select();
|
||||
return false;
|
||||
|
|
|
@ -94,47 +94,46 @@ async function initRepoProjectSortable() {
|
|||
}
|
||||
|
||||
export function initRepoProject() {
|
||||
if (!$('.repository.projects').length) {
|
||||
if (!document.querySelector('.repository.projects')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const _promise = initRepoProjectSortable();
|
||||
|
||||
$('.edit-project-column-modal').each(function () {
|
||||
const $projectHeader = $(this).closest('.project-column-header');
|
||||
const $projectTitleLabel = $projectHeader.find('.project-column-title');
|
||||
const $projectTitleInput = $(this).find('.project-column-title-input');
|
||||
const $projectColorInput = $(this).find('#new_project_column_color');
|
||||
const $boardColumn = $(this).closest('.project-column');
|
||||
for (const modal of document.getElementsByClassName('edit-project-column-modal')) {
|
||||
const projectHeader = modal.closest('.project-column-header');
|
||||
const projectTitleLabel = projectHeader?.querySelector('.project-column-title');
|
||||
const projectTitleInput = modal.querySelector('.project-column-title-input');
|
||||
const projectColorInput = modal.querySelector('#new_project_column_color');
|
||||
const boardColumn = modal.closest('.project-column');
|
||||
const bgColor = boardColumn?.style.backgroundColor;
|
||||
|
||||
const bgColor = $boardColumn[0].style.backgroundColor;
|
||||
if (bgColor) {
|
||||
setLabelColor($projectHeader, rgbToHex(bgColor));
|
||||
setLabelColor(projectHeader, rgbToHex(bgColor));
|
||||
}
|
||||
|
||||
$(this).find('.edit-project-column-button').on('click', async function (e) {
|
||||
modal.querySelector('.edit-project-column-button')?.addEventListener('click', async function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
try {
|
||||
await PUT($(this).data('url'), {
|
||||
await PUT(this.getAttribute('data-url'), {
|
||||
data: {
|
||||
title: $projectTitleInput.val(),
|
||||
color: $projectColorInput.val(),
|
||||
title: projectTitleInput?.value,
|
||||
color: projectColorInput?.value,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} finally {
|
||||
$projectTitleLabel.text($projectTitleInput.val());
|
||||
$projectTitleInput.closest('form').removeClass('dirty');
|
||||
if ($projectColorInput.val()) {
|
||||
setLabelColor($projectHeader, $projectColorInput.val());
|
||||
projectTitleLabel.textContent = projectTitleInput?.value;
|
||||
projectTitleInput.closest('form')?.classList.remove('dirty');
|
||||
if (projectColorInput?.value) {
|
||||
setLabelColor(projectHeader, projectColorInput.value);
|
||||
}
|
||||
$boardColumn[0].style = `background: ${$projectColorInput.val()} !important`;
|
||||
boardColumn.style = `background: ${projectColorInput.value} !important`;
|
||||
$('.ui.modal').modal('hide');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$('.default-project-column-modal').each(function () {
|
||||
const $boardColumn = $(this).closest('.project-column');
|
||||
|
@ -187,9 +186,11 @@ export function initRepoProject() {
|
|||
function setLabelColor(label, color) {
|
||||
const {r, g, b} = tinycolor(color).toRgb();
|
||||
if (useLightTextOnBackground(r, g, b)) {
|
||||
label.removeClass('dark-label').addClass('light-label');
|
||||
label.classList.remove('dark-label');
|
||||
label.classList.add('light-label');
|
||||
} else {
|
||||
label.removeClass('light-label').addClass('dark-label');
|
||||
label.classList.remove('light-label');
|
||||
label.classList.add('dark-label');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -77,18 +77,24 @@ export function initRepoSettingGitHook() {
|
|||
}
|
||||
|
||||
export function initRepoSettingBranches() {
|
||||
if (!$('.repository.settings.branches').length) return;
|
||||
$('.toggle-target-enabled').on('change', function () {
|
||||
const $target = $(this.getAttribute('data-target'));
|
||||
$target.toggleClass('disabled', !this.checked);
|
||||
});
|
||||
$('.toggle-target-disabled').on('change', function () {
|
||||
const $target = $(this.getAttribute('data-target'));
|
||||
if (this.checked) $target.addClass('disabled'); // only disable, do not auto enable
|
||||
});
|
||||
$('#dismiss_stale_approvals').on('change', function () {
|
||||
const $target = $('#ignore_stale_approvals_box');
|
||||
$target.toggleClass('disabled', this.checked);
|
||||
if (!document.querySelector('.repository.settings.branches')) return;
|
||||
|
||||
for (const el of document.getElementsByClassName('toggle-target-enabled')) {
|
||||
el.addEventListener('change', function () {
|
||||
const target = document.querySelector(this.getAttribute('data-target'));
|
||||
target?.classList.toggle('disabled', !this.checked);
|
||||
});
|
||||
}
|
||||
|
||||
for (const el of document.getElementsByClassName('toggle-target-disabled')) {
|
||||
el.addEventListener('change', function () {
|
||||
const target = document.querySelector(this.getAttribute('data-target'));
|
||||
if (this.checked) target?.classList.add('disabled'); // only disable, do not auto enable
|
||||
});
|
||||
}
|
||||
|
||||
document.getElementById('dismiss_stale_approvals')?.addEventListener('change', function () {
|
||||
document.getElementById('ignore_stale_approvals_box')?.classList.toggle('disabled', this.checked);
|
||||
});
|
||||
|
||||
// show the `Matched` mark for the status checks that match the pattern
|
||||
|
@ -106,7 +112,6 @@ export function initRepoSettingBranches() {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
toggleElem(el, matched);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -86,6 +86,7 @@ import {initRepoRecentCommits} from './features/recent-commits.js';
|
|||
import {initRepoDiffCommitBranchesAndTags} from './features/repo-diff-commit.js';
|
||||
import {initDirAuto} from './modules/dirauto.js';
|
||||
import {initRepositorySearch} from './features/repo-search.js';
|
||||
import {initColorPickers} from './features/colorpicker.js';
|
||||
|
||||
// Init Gitea's Fomantic settings
|
||||
initGiteaFomantic();
|
||||
|
@ -188,4 +189,5 @@ onDomReady(() => {
|
|||
initRepoDiffView();
|
||||
initPdfViewer();
|
||||
initScopedAccessTokenCategories();
|
||||
initColorPickers();
|
||||
});
|
||||
|
|
|
@ -11,8 +11,6 @@ export const fomanticMobileScreen = window.matchMedia('only screen and (max-widt
|
|||
export function initGiteaFomantic() {
|
||||
// Silence fomantic's error logging when tabs are used without a target content element
|
||||
$.fn.tab.settings.silent = true;
|
||||
// Disable the behavior of fomantic to toggle the checkbox when you press enter on a checkbox element.
|
||||
$.fn.checkbox.settings.enableEnterKey = false;
|
||||
|
||||
// By default, use "exact match" for full text search
|
||||
$.fn.dropdown.settings.fullTextSearch = 'exact';
|
||||
|
|
|
@ -41,24 +41,19 @@ The ideal checkboxes should be:
|
|||
<label><input type="checkbox"> ... </label>
|
||||
```
|
||||
|
||||
However, related CSS styles aren't supported (not implemented) yet, so at the moment,
|
||||
almost all the checkboxes are still using Fomantic UI checkbox.
|
||||
|
||||
## Fomantic UI Checkbox
|
||||
However, the templates still have the Fomantic-style HTML layout:
|
||||
|
||||
```html
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox"> <!-- class "hidden" will be added by $.checkbox() -->
|
||||
<input type="checkbox">
|
||||
<label>...</label>
|
||||
</div>
|
||||
```
|
||||
|
||||
Then the JS `$.checkbox()` should be called to make it work with keyboard and label-clicking,
|
||||
then it works like the ideal checkboxes.
|
||||
|
||||
There is still a problem: Fomantic UI checkbox is not friendly to screen readers,
|
||||
so we add IDs to all the Fomantic UI checkboxes automatically by JS.
|
||||
If the `label` part is empty, then the checkbox needs to get the `aria-label` attribute manually.
|
||||
We call `initAriaCheckboxPatch` to link the `input` and `label` which makes clicking the
|
||||
label etc. work. There is still a problem: These checkboxes are not friendly to screen readers,
|
||||
so we add IDs to all the Fomantic UI checkboxes automatically by JS. If the `label` part is empty,
|
||||
then the checkbox needs to get the `aria-label` attribute manually.
|
||||
|
||||
# Fomantic Dropdown
|
||||
|
||||
|
|
|
@ -1,38 +1,24 @@
|
|||
import $ from 'jquery';
|
||||
import {generateAriaId} from './base.js';
|
||||
|
||||
const ariaPatchKey = '_giteaAriaPatchCheckbox';
|
||||
const fomanticCheckboxFn = $.fn.checkbox;
|
||||
|
||||
// use our own `$.fn.checkbox` to patch Fomantic's checkbox module
|
||||
export function initAriaCheckboxPatch() {
|
||||
if ($.fn.checkbox === ariaCheckboxFn) throw new Error('initAriaCheckboxPatch could only be called once');
|
||||
$.fn.checkbox = ariaCheckboxFn;
|
||||
ariaCheckboxFn.settings = fomanticCheckboxFn.settings;
|
||||
}
|
||||
// link the label and the input element so it's clickable and accessible
|
||||
for (const el of document.querySelectorAll('.ui.checkbox')) {
|
||||
if (el.hasAttribute('data-checkbox-patched')) continue;
|
||||
const label = el.querySelector('label');
|
||||
const input = el.querySelector('input');
|
||||
if (!label || !input) continue;
|
||||
const inputId = input.getAttribute('id');
|
||||
const labelFor = label.getAttribute('for');
|
||||
|
||||
// the patched `$.fn.checkbox` checkbox function
|
||||
// * it does the one-time attaching on the first call
|
||||
function ariaCheckboxFn(...args) {
|
||||
const ret = fomanticCheckboxFn.apply(this, args);
|
||||
for (const el of this) {
|
||||
if (el[ariaPatchKey]) continue;
|
||||
attachInit(el);
|
||||
if (inputId && !labelFor) { // missing "for"
|
||||
label.setAttribute('for', inputId);
|
||||
} else if (!inputId && !labelFor) { // missing both "id" and "for"
|
||||
const id = generateAriaId();
|
||||
input.setAttribute('id', id);
|
||||
label.setAttribute('for', id);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
el.setAttribute('data-checkbox-patched', 'true');
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
function attachInit(el) {
|
||||
// Fomantic UI checkbox needs to be something like: <div class="ui checkbox"><label /><input /></div>
|
||||
// It doesn't work well with <label><input />...</label>
|
||||
// To make it work with aria, the "id"/"for" attributes are necessary, so add them automatically if missing.
|
||||
// In the future, refactor to use native checkbox directly, then this patch could be removed.
|
||||
el[ariaPatchKey] = {}; // record that this element has been patched
|
||||
const label = el.querySelector('label');
|
||||
const input = el.querySelector('input');
|
||||
if (!label || !input || input.getAttribute('id')) return;
|
||||
|
||||
const id = generateAriaId();
|
||||
input.setAttribute('id', id);
|
||||
label.setAttribute('for', id);
|
||||
}
|
||||
|
|
|
@ -207,7 +207,7 @@ function attachDomEvents(dropdown, focusable, menu) {
|
|||
if (!$item) $item = $(menu).find('> .item.selected'); // when dropdown filters items by input, there is no "value", so query the "selected" item
|
||||
// if the selected item is clickable, then trigger the click event.
|
||||
// we can not click any item without check, because Fomantic code might also handle the Enter event. that would result in double click.
|
||||
if ($item && ($item[0].matches('a') || $item.hasClass('js-aria-clickable'))) $item[0].click();
|
||||
if ($item?.[0]?.matches('a, .js-aria-clickable')) $item[0].click();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -3,11 +3,12 @@ import {isDocumentFragmentOrElementNode} from '../utils/dom.js';
|
|||
import {formatDatetime} from '../utils/time.js';
|
||||
|
||||
const visibleInstances = new Set();
|
||||
const arrowSvg = `<svg width="16" height="7"><path d="m0 7 8-7 8 7Z" class="tippy-svg-arrow-outer"/><path d="m0 8 8-7 8 7Z" class="tippy-svg-arrow-inner"/></svg>`;
|
||||
|
||||
export function createTippy(target, opts = {}) {
|
||||
// the callback functions should be destructured from opts,
|
||||
// because we should use our own wrapper functions to handle them, do not let the user override them
|
||||
const {onHide, onShow, onDestroy, role, theme, ...other} = opts;
|
||||
const {onHide, onShow, onDestroy, role, theme, arrow, ...other} = opts;
|
||||
|
||||
const instance = tippy(target, {
|
||||
appendTo: document.body,
|
||||
|
@ -35,9 +36,9 @@ export function createTippy(target, opts = {}) {
|
|||
visibleInstances.add(instance);
|
||||
return onShow?.(instance);
|
||||
},
|
||||
arrow: `<svg width="16" height="7"><path d="m0 7 8-7 8 7Z" class="tippy-svg-arrow-outer"/><path d="m0 8 8-7 8 7Z" class="tippy-svg-arrow-inner"/></svg>`,
|
||||
arrow: arrow || (theme === 'bare' ? false : arrowSvg),
|
||||
role: role || 'menu', // HTML role attribute
|
||||
theme: theme || role || 'menu', // CSS theme, either "tooltip", "menu" or "box-with-header"
|
||||
theme: theme || role || 'menu', // CSS theme, either "tooltip", "menu", "box-with-header" or "bare"
|
||||
plugins: [followCursor],
|
||||
...other,
|
||||
});
|
||||
|
|
|
@ -39,6 +39,7 @@ function showToast(message, level, {gravity, position, duration, useHtmlBody, ..
|
|||
|
||||
toast.showToast();
|
||||
toast.toastElement.querySelector('.toast-close').addEventListener('click', () => toast.hideToast());
|
||||
return toast;
|
||||
}
|
||||
|
||||
export function showInfoToast(message, opts) {
|
||||
|
|
|
@ -51,8 +51,22 @@ export function isElemHidden(el) {
|
|||
return res[0];
|
||||
}
|
||||
|
||||
export function queryElemSiblings(el, selector) {
|
||||
return Array.from(el.parentNode.children).filter((child) => child !== el && child.matches(selector));
|
||||
function applyElemsCallback(elems, fn) {
|
||||
if (fn) {
|
||||
for (const el of elems) {
|
||||
fn(el);
|
||||
}
|
||||
}
|
||||
return elems;
|
||||
}
|
||||
|
||||
export function queryElemSiblings(el, selector = '*', fn) {
|
||||
return applyElemsCallback(Array.from(el.parentNode.children).filter((child) => child !== el && child.matches(selector)), fn);
|
||||
}
|
||||
|
||||
// it works like jQuery.children: only the direct children are selected
|
||||
export function queryElemChildren(parent, selector = '*', fn) {
|
||||
return applyElemsCallback(parent.querySelectorAll(`:scope > ${selector}`), fn);
|
||||
}
|
||||
|
||||
export function onDomReady(cb) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue