@stackoverflow/stacks 2.0.6 → 2.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/modal/modal.d.ts +2 -2
- package/dist/components/table/table.d.ts +2 -2
- package/dist/components/uploader/uploader.d.ts +3 -3
- package/dist/css/stacks.css +30 -30
- package/dist/css/stacks.min.css +1 -1
- package/dist/js/stacks.js +4 -1
- package/dist/js/stacks.min.js +1 -1
- package/lib/atomic/__snapshots__/color.less.test.ts.snap +1312 -1312
- package/lib/atomic/misc.less +3 -3
- package/lib/atomic/v1/__snapshots__/color.less.test.ts.snap +2354 -2354
- package/lib/base/internal.less +1 -1
- package/lib/components/badge/badge.less +3 -3
- package/lib/components/banner/banner.less +6 -6
- package/lib/components/checkbox_radio/checkbox_radio.less +4 -4
- package/lib/components/code-block/code-block.a11y.test.ts +5 -6
- package/lib/components/modal/modal.test.ts +155 -0
- package/lib/components/modal/modal.ts +7 -3
- package/lib/components/prose/prose.less +1 -1
- package/lib/components/table/table.ts +2 -2
- package/lib/components/tag/tag.less +2 -2
- package/lib/components/toggle-switch/toggle-switch.less +2 -2
- package/lib/components/uploader/uploader.less +2 -2
- package/lib/components/uploader/uploader.ts +4 -3
- package/lib/exports/__snapshots__/color-mixins.less.test.ts.snap +96 -96
- package/package.json +24 -24
package/lib/base/internal.less
CHANGED
|
@@ -215,6 +215,6 @@
|
|
|
215
215
|
#bullet-arrow(@col) {
|
|
216
216
|
@image: '<?xml version="1.0" encoding="UTF-8"?><svg version="1.1" viewBox="0 0 7 10" xmlns="http://www.w3.org/2000/svg"><path d="m0.72153 0.68446 4.1336 4.3077-4.1336 4.3077" fill="none" stroke="@{col}" stroke-width="2"/></svg>';
|
|
217
217
|
@encoded-image: escape(@image);
|
|
218
|
-
background-image: url("data:image/svg+xml
|
|
218
|
+
background-image: url("data:image/svg+xml;,@{encoded-image}");
|
|
219
219
|
}
|
|
220
220
|
}
|
|
@@ -125,14 +125,14 @@
|
|
|
125
125
|
--_ba-g: calc(var(--su-static4) - var(--su-static1)); // 3px
|
|
126
126
|
// :before icon
|
|
127
127
|
--_ba-before-h: calc(var(--su-static16) - var(--su-static2)); // 14px
|
|
128
|
-
--_ba-before-icon: url("data:image/svg+xml
|
|
128
|
+
--_ba-before-icon: url("data:image/svg+xml;,%3Csvg width='12' height='14' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M5.528.746c.257-.329.675-.327.93 0l4.42 5.66c.258.329.257.864 0 1.192l-4.42 5.66c-.256.328-.674.327-.93 0l-4.42-5.66c-.257-.329-.256-.865 0-1.192l4.42-5.66z' fill='%23fff'/%3E%3C/svg%3E");
|
|
129
129
|
--_ba-before-mt: calc(var(--su-static1) * -1); // -1px
|
|
130
130
|
--_ba-before-w: var(--su-static12);
|
|
131
131
|
|
|
132
132
|
// Sizes
|
|
133
133
|
&.s-badge__xs {
|
|
134
134
|
--_ba-before-h: calc(var(--su-static8) + var(--su-static1)); // 9px
|
|
135
|
-
--_ba-before-icon: url("data:image/svg+xml
|
|
135
|
+
--_ba-before-icon: url("data:image/svg+xml;,%3Csvg width='7' height='9' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M3 .246c.3-.329.701-.327 1 0L6.776 4c.3.329.298.672 0 1L4 8.75c-.299.329-.702.327-1 0L.224 5c-.284-.324-.285-.675 0-1L3 .246z' fill='%23fff'/%3E%3C/svg%3E");
|
|
136
136
|
--_ba-before-mt: 0;
|
|
137
137
|
--_ba-before-w: calc(var(--su-static8) - var(--su-static1)); // 7px
|
|
138
138
|
}
|
|
@@ -140,7 +140,7 @@
|
|
|
140
140
|
&.s-badge__sm {
|
|
141
141
|
--_ba-g: var(--su-static2);
|
|
142
142
|
--_ba-before-h: calc(var(--su-static12) - var(--su-static1)); // 11px
|
|
143
|
-
--_ba-before-icon: url("data:image/svg+xml
|
|
143
|
+
--_ba-before-icon: url("data:image/svg+xml;,%3Csvg width='9' height='11' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M3.55.246c.257-.329.647-.327.903 0l3.36 4.66c.256.329.256.864 0 1.192L4.45 10.75c-.257.329-.644.327-.9 0L.192 6.098c-.256-.329-.256-.865 0-1.192L3.55.246z' fill='%23fff'/%3E%3C/svg%3E");
|
|
144
144
|
--_ba-before-mt: 0;
|
|
145
145
|
--_ba-before-w: calc(var(--su-static8) + var(--su-static1)); // 9px
|
|
146
146
|
}
|
|
@@ -2,22 +2,23 @@
|
|
|
2
2
|
// TODO deprecate .s-banner (by turning it into a modifier on .s-notice)
|
|
3
3
|
// This would reduce the amount of CSS we ship to the client and simplify our codebase
|
|
4
4
|
.s-banner {
|
|
5
|
-
--_no-
|
|
5
|
+
--_no-ty-offset: 0;
|
|
6
|
+
--_no-ty: var(--theme-topbar-height, calc(var(--su-static48) + var(--su-static8)));
|
|
6
7
|
.construct-notice-component(s-banner);
|
|
7
8
|
|
|
8
9
|
&[aria-hidden="true"] { // If you want to hide and reveal the banner
|
|
9
|
-
--_no-
|
|
10
|
+
--_no-ty-offset: -1;
|
|
10
11
|
opacity: 0;
|
|
11
12
|
visibility: hidden;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
&[aria-hidden="false"] {
|
|
15
|
-
--_no-
|
|
16
|
+
--_no-ty-offset: 1;
|
|
16
17
|
opacity: 1;
|
|
17
18
|
visibility: visible;
|
|
18
19
|
|
|
19
20
|
&.is-pinned { // If you want to put the banner above the topbar
|
|
20
|
-
--_no-
|
|
21
|
+
--_no-ty-offset: 0;
|
|
21
22
|
z-index: calc(var(--zi-navigation-fixed) + 1);
|
|
22
23
|
}
|
|
23
24
|
}
|
|
@@ -37,8 +38,7 @@
|
|
|
37
38
|
inset: 0 0 auto 0;
|
|
38
39
|
padding: var(--su12);
|
|
39
40
|
position: fixed;
|
|
40
|
-
|
|
41
|
-
transform: translate3d(0, calc(var(--theme-topbar-height, calc(var(--su-static48) + var(--su-static8))) * var(--_no-x-offset)), 0);
|
|
41
|
+
transform: translate3d(0, calc(var(--_no-ty) * var(--_no-ty-offset)), 0);
|
|
42
42
|
width: 100%;
|
|
43
43
|
z-index: calc(var(--zi-navigation-fixed) - 1); // Tuck below topbar
|
|
44
44
|
}
|
|
@@ -54,10 +54,10 @@
|
|
|
54
54
|
// CONTEXTUAL STYLES
|
|
55
55
|
.dark-mode({
|
|
56
56
|
&:checked {
|
|
57
|
-
--_ch-bg-image: url("data:image/svg+xml
|
|
57
|
+
--_ch-bg-image: url("data:image/svg+xml;,%3Csvg width='11' height='11' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M10 3.41L8.59 2 4 6.59 2.41 5 1 6.41l3 3z' fill='@{ch-bg-image-fill-dark}'/%3E%3C/svg%3E");
|
|
58
58
|
}
|
|
59
59
|
&:indeterminate {
|
|
60
|
-
--_ch-bg-image: url("data:image/svg+xml
|
|
60
|
+
--_ch-bg-image: url("data:image/svg+xml;,%3Csvg width='11' height='11' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M2 4.5 h7 v2 h-7 z' fill='@{ch-bg-image-fill-dark}'/%3E%3C/svg%3E");
|
|
61
61
|
}
|
|
62
62
|
});
|
|
63
63
|
|
|
@@ -80,11 +80,11 @@
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
&:checked {
|
|
83
|
-
--_ch-bg-image: url("data:image/svg+xml
|
|
83
|
+
--_ch-bg-image: url("data:image/svg+xml;,%3Csvg width='11' height='11' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M10 3.41L8.59 2 4 6.59 2.41 5 1 6.41l3 3z' fill='@{ch-bg-image-fill}'/%3E%3C/svg%3E");
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
&:indeterminate {
|
|
87
|
-
--_ch-bg-image: url("data:image/svg+xml
|
|
87
|
+
--_ch-bg-image: url("data:image/svg+xml;,%3Csvg width='11' height='11' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M2 4.5 h7 v2 h-7 z' fill='@{ch-bg-image-fill}'/%3E%3C/svg%3E");
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
// INTERACTION
|
|
@@ -15,14 +15,13 @@ describe("code block", () => {
|
|
|
15
15
|
...defaultOptions,
|
|
16
16
|
includeNullModifier: false,
|
|
17
17
|
},
|
|
18
|
+
attributes: {
|
|
19
|
+
tabindex: "0",
|
|
20
|
+
},
|
|
18
21
|
// TODO revisit these skipped test ids
|
|
19
22
|
skippedTestids: [
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"s-code-block-language-html-highcontrast-dark",
|
|
23
|
-
"s-code-block-language-html-dark",
|
|
24
|
-
"s-code-block-language-css-dark",
|
|
25
|
-
"s-code-block-language-javascript-dark",
|
|
23
|
+
/s-code-block-language-(html|css|javascript)-dark/,
|
|
24
|
+
/s-code-block-language-html-highcontrast-(light|dark)/,
|
|
26
25
|
"s-code-block-language-javascript-highcontrast-light",
|
|
27
26
|
],
|
|
28
27
|
});
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { html, fixture, expect } from "@open-wc/testing";
|
|
2
|
+
import { screen, waitFor } from "@testing-library/dom";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import "../../index";
|
|
5
|
+
|
|
6
|
+
const user = userEvent.setup();
|
|
7
|
+
|
|
8
|
+
const createModal = ({
|
|
9
|
+
hidden = true,
|
|
10
|
+
initialFocusEl,
|
|
11
|
+
}: { hidden?: boolean; initialFocusEl?: ReturnType<typeof html> } = {}) => html`
|
|
12
|
+
<div data-controller="s-modal">
|
|
13
|
+
<button
|
|
14
|
+
class="s-btn"
|
|
15
|
+
data-action="s-modal#show"
|
|
16
|
+
data-testid="trigger">
|
|
17
|
+
Show Modal
|
|
18
|
+
</button>
|
|
19
|
+
|
|
20
|
+
<aside
|
|
21
|
+
class="s-modal"
|
|
22
|
+
id="modal-base"
|
|
23
|
+
tabindex="-1"
|
|
24
|
+
role="dialog"
|
|
25
|
+
aria-labelledby="modal-base-title"
|
|
26
|
+
aria-describedby="modal-base-description"
|
|
27
|
+
aria-hidden="${hidden}"
|
|
28
|
+
data-s-modal-target="modal"
|
|
29
|
+
data-testid="modal">
|
|
30
|
+
<div class="s-modal--dialog" role="document">
|
|
31
|
+
<h1 class="s-modal--header" id="modal-base-title">Title</h1>
|
|
32
|
+
|
|
33
|
+
<p class="s-modal--body">
|
|
34
|
+
<span id="modal-base-description">Description</span>
|
|
35
|
+
<form>
|
|
36
|
+
<input type="text" data-testid="first-focusable-element" />
|
|
37
|
+
${initialFocusEl}
|
|
38
|
+
</form>
|
|
39
|
+
</p>
|
|
40
|
+
|
|
41
|
+
<div class="d-flex gx8 s-modal--footer">
|
|
42
|
+
<button class="flex--item s-btn s-btn__primary" type="button">Save changes</button>
|
|
43
|
+
<button class="flex--item s-btn" type="button" data-action="s-modal#hide">Cancel</button>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<button
|
|
47
|
+
class="s-btn s-btn__muted s-modal--close"
|
|
48
|
+
type="button"
|
|
49
|
+
aria-label="Close"
|
|
50
|
+
data-action="s-modal#hide"
|
|
51
|
+
data-testid="close-btn">
|
|
52
|
+
Close
|
|
53
|
+
</button>
|
|
54
|
+
</div>
|
|
55
|
+
</aside>
|
|
56
|
+
</div>
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
describe("modal", () => {
|
|
60
|
+
it("should make the modal visible when toggle button is clicked", async () => {
|
|
61
|
+
await fixture(createModal());
|
|
62
|
+
|
|
63
|
+
const modal = await screen.findByTestId("modal");
|
|
64
|
+
const trigger = await screen.findByTestId("trigger");
|
|
65
|
+
|
|
66
|
+
expect(modal).not.to.be.visible;
|
|
67
|
+
|
|
68
|
+
await user.click(trigger);
|
|
69
|
+
|
|
70
|
+
expect(modal).to.be.visible;
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("should hide the modal when the close button is clicked", async () => {
|
|
74
|
+
await fixture(createModal({ hidden: false }));
|
|
75
|
+
|
|
76
|
+
const modal = await screen.findByTestId("modal");
|
|
77
|
+
const closeBtn = await screen.findByTestId("close-btn");
|
|
78
|
+
|
|
79
|
+
expect(modal).to.be.visible;
|
|
80
|
+
|
|
81
|
+
await user.click(closeBtn);
|
|
82
|
+
|
|
83
|
+
await waitFor(() => expect(modal).not.to.be.visible);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should focus on the first element with `data-s-modal-target"initialFocus"` when modal is shown', async () => {
|
|
87
|
+
await fixture(
|
|
88
|
+
createModal({
|
|
89
|
+
initialFocusEl: html`<input
|
|
90
|
+
type="text"
|
|
91
|
+
data-testid="initialFocus"
|
|
92
|
+
data-s-modal-target="initialFocus"
|
|
93
|
+
/>`,
|
|
94
|
+
})
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
const modal = await screen.findByTestId("modal");
|
|
98
|
+
const trigger = await screen.findByTestId("trigger");
|
|
99
|
+
const initialFocusEl = await screen.findByTestId("initialFocus");
|
|
100
|
+
|
|
101
|
+
expect(modal).not.to.be.visible;
|
|
102
|
+
|
|
103
|
+
await user.click(trigger);
|
|
104
|
+
expect(modal).to.be.visible;
|
|
105
|
+
|
|
106
|
+
await waitFor(() => expect(initialFocusEl).to.have.focus);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("should focus on the first focusable element when modal is shown and no initialFocus is specified", async () => {
|
|
110
|
+
await fixture(createModal());
|
|
111
|
+
|
|
112
|
+
const modal = await screen.findByTestId("modal");
|
|
113
|
+
const trigger = await screen.findByTestId("trigger");
|
|
114
|
+
const focusableEl = await screen.findByTestId(
|
|
115
|
+
"first-focusable-element"
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
expect(modal).not.to.be.visible;
|
|
119
|
+
expect(focusableEl).not.to.have.focus;
|
|
120
|
+
|
|
121
|
+
await user.click(trigger);
|
|
122
|
+
expect(modal).to.be.visible;
|
|
123
|
+
|
|
124
|
+
await waitFor(() => expect(focusableEl).to.have.focus);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("should not change set focus when an element within the modal is already focused", async () => {
|
|
128
|
+
await fixture(createModal());
|
|
129
|
+
|
|
130
|
+
const modal = await screen.findByTestId("modal");
|
|
131
|
+
const trigger = await screen.findByTestId("trigger");
|
|
132
|
+
const firstFocusableEl = await screen.findByTestId(
|
|
133
|
+
"first-focusable-element"
|
|
134
|
+
);
|
|
135
|
+
const closeButton = await screen.findByTestId("close-btn");
|
|
136
|
+
|
|
137
|
+
expect(modal).not.to.be.visible;
|
|
138
|
+
expect(firstFocusableEl).not.to.have.focus;
|
|
139
|
+
|
|
140
|
+
await user.click(trigger);
|
|
141
|
+
expect(modal).to.be.visible;
|
|
142
|
+
|
|
143
|
+
// manually focus on an element within the modal
|
|
144
|
+
closeButton.focus();
|
|
145
|
+
|
|
146
|
+
// wait for s-modal:shown css transition to complete
|
|
147
|
+
await new Promise((resolve) =>
|
|
148
|
+
modal.addEventListener("s-modal:shown", resolve)
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
// check that focus stayed on the manually focused element and
|
|
152
|
+
// has not changed to the first focusable element
|
|
153
|
+
expect(closeButton).to.have.focus;
|
|
154
|
+
});
|
|
155
|
+
});
|
|
@@ -3,8 +3,8 @@ import * as Stacks from "../../stacks";
|
|
|
3
3
|
export class ModalController extends Stacks.StacksController {
|
|
4
4
|
static targets = ["modal", "initialFocus"];
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
declare readonly modalTarget: HTMLElement;
|
|
7
|
+
declare readonly initialFocusTargets: HTMLElement[];
|
|
8
8
|
|
|
9
9
|
private _boundClickFn!: (event: MouseEvent) => void;
|
|
10
10
|
private _boundKeypressFn!: (event: KeyboardEvent) => void;
|
|
@@ -228,7 +228,11 @@ export class ModalController extends Stacks.StacksController {
|
|
|
228
228
|
const initialFocus =
|
|
229
229
|
this.firstVisible(this.initialFocusTargets) ??
|
|
230
230
|
this.firstVisible(this.getAllTabbables());
|
|
231
|
-
|
|
231
|
+
|
|
232
|
+
// Only set focus if focus is not already set on an element within the modal
|
|
233
|
+
if (!this.modalTarget.contains(document.activeElement)) {
|
|
234
|
+
initialFocus?.focus();
|
|
235
|
+
}
|
|
232
236
|
},
|
|
233
237
|
{ once: true }
|
|
234
238
|
);
|
|
@@ -382,7 +382,7 @@
|
|
|
382
382
|
top: var(--_pr-spoiler-after-t);
|
|
383
383
|
transition: @pr-spoiler-transition;
|
|
384
384
|
|
|
385
|
-
background-image: url("data:image/svg+xml
|
|
385
|
+
background-image: url("data:image/svg+xml;,%3Csvg xmlns='http://www.w3.org/2000/svg' aria-hidden='true' fill='rgb(132, 141, 149)' width='18' height='18' viewBox='0 0 18 18'%3E%3Cpath d='M9 17A8 8 0 119 1a8 8 0 010 16zM8 4v6h2V4H8zm0 8v2h2v-2H8z'%3E%3C/path%3E%3C/svg%3E");
|
|
386
386
|
background-position: center right;
|
|
387
387
|
background-repeat: no-repeat;
|
|
388
388
|
color: var(--black-400);
|
|
@@ -12,8 +12,8 @@ export enum SortOrder {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export class TableController extends Stacks.StacksController {
|
|
15
|
-
declare columnTarget: HTMLTableCellElement;
|
|
16
|
-
declare columnTargets: HTMLTableCellElement[];
|
|
15
|
+
declare readonly columnTarget: HTMLTableCellElement;
|
|
16
|
+
declare readonly columnTargets: HTMLTableCellElement[];
|
|
17
17
|
|
|
18
18
|
static targets = ["column"];
|
|
19
19
|
|
|
@@ -100,7 +100,7 @@
|
|
|
100
100
|
|
|
101
101
|
&__ignored, // TODO: remove all single `&` ignored styles once core no longer requires them
|
|
102
102
|
&&__ignored {
|
|
103
|
-
--_ta-before-icon: url("data:image/svg+xml
|
|
103
|
+
--_ta-before-icon: url("data:image/svg+xml;,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 14 14'%3E%3Cpath d='M3.52 7.38 1.58 9.26A12.38 12.38 0 0 1 0 7s2.63-5.14 7.05-5.14c.66 0 1.28.12 1.86.32L7.44 3.6a3.48 3.48 0 0 0-3.92 3.78ZM5.3 9.99c.5.28 1.1.44 1.71.44 1.94 0 3.5-1.53 3.5-3.43 0-.62-.17-1.21-.47-1.72L8.7 6.6a1.73 1.73 0 0 1-2.08 2.07L5.29 10Zm6.23-6.19A12.7 12.7 0 0 1 14 7s-2.63 5.14-6.95 5.14A6.1 6.1 0 0 1 4 11.3L2.27 13l-1.4-1.36L11.9 1l1.23 1.2-1.6 1.6Z'/%3E%3C/svg%3E");
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
// moderator overrides other muted and required, required overrides muted
|
|
@@ -151,7 +151,7 @@
|
|
|
151
151
|
}
|
|
152
152
|
&__watched, // TODO: remove all single `&` watched styles once core no longer requires them
|
|
153
153
|
&&__watched {
|
|
154
|
-
--_ta-before-icon: url("data:image/svg+xml
|
|
154
|
+
--_ta-before-icon: url("data:image/svg+xml;,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 14 14'%3E%3Cpath d='M7.05 1C2.63 1 0 6.5 0 6.5S2.63 12 7.05 12C11.38 12 14 6.5 14 6.5S11.37 1 7.05 1ZM7 10.17A3.59 3.59 0 0 1 3.5 6.5 3.6 3.6 0 0 1 7 2.83c1.94 0 3.5 1.65 3.5 3.67A3.57 3.57 0 0 1 7 10.17Zm0-1.84c.97 0 1.75-.81 1.75-1.83S7.97 4.67 7 4.67s-1.75.81-1.75 1.83S6.03 8.33 7 8.33Z'/%3E%3C/svg%3E");
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
// CHILD ELEMENTS
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
@ts-bg-image-fill: .set-white()[default];
|
|
3
3
|
@ts-bg-image-fill-dark: .set-white-dark()[default];
|
|
4
4
|
--_ts-bg: var(--black-350);
|
|
5
|
-
--_ts-bg-image: url("data:image/svg+xml
|
|
5
|
+
--_ts-bg-image: url("data:image/svg+xml;,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='@{ts-bg-image-fill}'/%3e%3c/svg%3e");
|
|
6
6
|
--_ts-bg-ps: left center;
|
|
7
7
|
--_ts-bs-color: transparent;
|
|
8
8
|
--_ts-multiple-bg: unset;
|
|
9
9
|
--_ts-multiple-fc: var(--black-400);
|
|
10
10
|
|
|
11
11
|
.dark-mode({
|
|
12
|
-
--_ts-bg-image: url("data:image/svg+xml
|
|
12
|
+
--_ts-bg-image: url("data:image/svg+xml;,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='@{ts-bg-image-fill-dark}'/%3e%3c/svg%3e");
|
|
13
13
|
});
|
|
14
14
|
|
|
15
15
|
fieldset[disabled] &,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
.s-uploader {
|
|
2
2
|
// COMPONENT-SPECIFIC CONSTANTS
|
|
3
|
-
--_up-bg-b-image: url("data:image/svg+xml
|
|
3
|
+
--_up-bg-b-image: url("data:image/svg+xml;,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' rx='5' ry='5' stroke='%23000000' stroke-width='8' stroke-dasharray='7%2c 22' stroke-dashoffset='0' stroke-linecap='square'/%3e%3c/svg%3e"); // Keeping this a custom property to save a few bytes
|
|
4
4
|
// COMPONENT-SPECIFIC CUSTOM PROPERTIES
|
|
5
5
|
--_up-bg: var(--black-100);
|
|
6
6
|
--_up-bg-focus: var(--black-150);
|
|
@@ -161,7 +161,7 @@
|
|
|
161
161
|
object-fit: cover;
|
|
162
162
|
}
|
|
163
163
|
&:not(img) {
|
|
164
|
-
background-image: url("data:image/svg+xml
|
|
164
|
+
background-image: url("data:image/svg+xml;,%3Csvg xmlns='http://www.w3.org/2000/svg' aria-hidden='true' fill='%23535A60' width='18' height='18' viewBox='0 0 18 18'%3E%3Cpath d='M3 3a2 2 0 012-2h6l4 4v10a2 2 0 01-2 2H5a2 2 0 01-2-2V3zm7-1.5V6h4.5L10 1.5z'%3E%3C/path%3E%3C/svg%3E");
|
|
165
165
|
background-position: center;
|
|
166
166
|
background-repeat: no-repeat;
|
|
167
167
|
}
|
|
@@ -8,9 +8,10 @@ interface FilePreview {
|
|
|
8
8
|
|
|
9
9
|
export class UploaderController extends Stacks.StacksController {
|
|
10
10
|
static targets = ["input", "previews", "uploader"];
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
|
|
12
|
+
declare readonly inputTarget: HTMLInputElement;
|
|
13
|
+
declare readonly previewsTarget: HTMLElement;
|
|
14
|
+
declare readonly uploaderTarget: HTMLElement;
|
|
14
15
|
|
|
15
16
|
private boundDragEnter!: () => void;
|
|
16
17
|
private boundDragLeave!: () => void;
|