@repobit/dex-system-design 1.0.1 → 2.0.1
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/CHANGELOG.md +89 -0
- package/README.md +43 -12
- package/package.json +6 -4
- package/schemas/component-manifest.json +207 -49
- package/schemas/manifest-omissions.json +2 -0
- package/src/components/accordion/accordion.css.js +12 -1
- package/src/components/agent-endpoints/agent-endpoints.js +1 -1
- package/src/components/alert/alert.css.js +22 -1
- package/src/components/anchor/anchor-nav.css.js +6 -1
- package/src/components/annotation/annotation.css.js +6 -1
- package/src/components/architecture.stories.js +6 -4
- package/src/components/avatar/avatar.js +37 -11
- package/src/components/avatar/avatar.stories.js +10 -6
- package/src/components/awards/awards-flex.css.js +31 -129
- package/src/components/awards/awards-flex.js +66 -116
- package/src/components/awards/awards-flex.stories.js +8 -6
- package/src/components/back/back.css.js +6 -1
- package/src/components/back/back.js +24 -12
- package/src/components/back/back.stories.js +3 -3
- package/src/components/badge/badge.js +6 -3
- package/src/components/badge/badge.stories.js +13 -9
- package/src/components/bento-grid/bento-grid.js +1 -1
- package/src/components/blockquote/blockquote.css.js +12 -3
- package/src/components/blockquote/blockquote.js +12 -5
- package/src/components/blockquote/blockquote.stories.js +6 -4
- package/src/components/button/button.css.js +46 -25
- package/src/components/button/button.js +44 -43
- package/src/components/button/button.stories.js +2 -2
- package/src/components/caption/caption.css.js +10 -0
- package/src/components/caption/caption.stories.js +4 -4
- package/src/components/carousel/carousel.css.js +19 -116
- package/src/components/carousel/carousel.js +70 -74
- package/src/components/carousel/carousel.stories.js +9 -8
- package/src/components/checkbox/checkbox.css.js +6 -1
- package/src/components/code/code.css.js +6 -1
- package/src/components/code/code.js +48 -7
- package/src/components/code/code.stories.js +7 -4
- package/src/components/divider/divider-horizontal.js +11 -3
- package/src/components/divider/divider-vertical.js +12 -5
- package/src/components/divider/divider.css.js +4 -1
- package/src/components/divider/divider.stories.js +6 -6
- package/src/components/dropdown/dropdown.js +67 -13
- package/src/components/dropdown/dropdown.stories.js +15 -14
- package/src/components/fieldset/fieldset.stories.js +41 -16
- package/src/components/footer/footer-nav-menu.css.js +6 -1
- package/src/components/footer/footer.css.js +6 -1
- package/src/components/header/header-code.js +1 -1
- package/src/components/header/header.css.js +9 -4
- package/src/components/heading/heading.stories.js +64 -31
- package/src/components/highlight/highlight.js +74 -34
- package/src/components/highlight/highlight.stories.js +5 -4
- package/src/components/icons/icon-element.js +49 -6
- package/src/components/icons/icons.stories.js +7 -0
- package/src/components/image/image.css.js +9 -98
- package/src/components/image/image.js +7 -2
- package/src/components/input/custom-form.stories.js +14 -19
- package/src/components/input/input-clipboard.css.js +6 -1
- package/src/components/input/input-clipboard.js +56 -8
- package/src/components/input/input.js +19 -2
- package/src/components/label/label.js +87 -1
- package/src/components/label/label.stories.js +21 -12
- package/src/components/link/link.css.js +52 -3
- package/src/components/link/link.js +52 -17
- package/src/components/link/link.stories.js +114 -3
- package/src/components/list/list-item/list-item.css.js +4 -6
- package/src/components/list/list-item/list-item.js +53 -5
- package/src/components/list/list-item/list-item.stories.js +22 -0
- package/src/components/list/list.js +27 -0
- package/src/components/modal/modal.css.js +6 -1
- package/src/components/pagination-dots/pagination-dots.css.js +161 -0
- package/src/components/pagination-dots/pagination-dots.js +202 -0
- package/src/components/pagination-dots/pagination-dots.stories.js +171 -0
- package/src/components/paragraph/paragraph.css.js +37 -1
- package/src/components/paragraph/paragraph.js +19 -2
- package/src/components/paragraph/paragraph.stories.js +40 -6
- package/src/components/picture/picture.css.js +8 -67
- package/src/components/picture/picture.js +15 -8
- package/src/components/pricing-cards/pricing-card.css.js +12 -2
- package/src/components/pricing-cards/pricing-card.js +4 -4
- package/src/components/progress/progress.js +24 -4
- package/src/components/progress/progress.stories.js +8 -6
- package/src/components/radio/radio.css.js +6 -1
- package/src/components/radio/radio.js +48 -3
- package/src/components/radio/radio.stories.js +1 -1
- package/src/components/shared/form-field.css.js +30 -4
- package/src/components/shared/token-scales.js +16 -0
- package/src/components/spinner/spinner.css.js +31 -4
- package/src/components/spinner/spinner.js +26 -4
- package/src/components/spinner/spinner.stories.js +10 -7
- package/src/components/switch/switch.css.js +21 -3
- package/src/components/switch/switch.js +8 -1
- package/src/components/switch/switch.stories.js +2 -1
- package/src/components/tab-list/tab-list.css.js +6 -1
- package/src/components/terminal/terminal-cursor.js +11 -0
- package/src/components/terminal/terminal-message.js +1 -1
- package/src/components/termsOfUse/terms.css.js +6 -1
- package/src/components/textarea/textarea.stories.js +23 -0
- package/src/components/tooltip/tooltip.css.js +24 -1
- package/src/components/tooltip/tooltip.js +135 -5
- package/src/components/tooltip/tooltip.stories.js +20 -14
- package/src/components/trust-panel/trust-panel.js +2 -2
- package/src/components/video/video.js +38 -3
- package/src/components/video/video.stories.js +10 -5
- package/src/components/video-player/video-player.css.js +6 -1
- package/src/tokens/elevation.stories.js +2 -3
- package/src/tokens/focus.stories.js +81 -15
- package/src/tokens/motion.stories.js +0 -6
- package/src/tokens/tokens.css +174 -97
- package/src/tokens/tokens.js +174 -97
- package/src/tokens/vertical-rhythm.stories.js +1 -1
- package/src/stories/Button.js +0 -20
- package/src/stories/Button.stories.js +0 -46
- package/src/stories/Configure.mdx +0 -364
- package/src/stories/Header.js +0 -45
- package/src/stories/Header.stories.js +0 -24
- package/src/stories/Page.js +0 -62
- package/src/stories/Page.stories.js +0 -20
- package/src/stories/assets/accessibility.png +0 -0
- package/src/stories/assets/accessibility.svg +0 -1
- package/src/stories/assets/addon-library.png +0 -0
- package/src/stories/assets/assets.png +0 -0
- package/src/stories/assets/avif-test-image.avif +0 -0
- package/src/stories/assets/context.png +0 -0
- package/src/stories/assets/discord.svg +0 -1
- package/src/stories/assets/docs.png +0 -0
- package/src/stories/assets/figma-plugin.png +0 -0
- package/src/stories/assets/github.svg +0 -1
- package/src/stories/assets/share.png +0 -0
- package/src/stories/assets/styling.png +0 -0
- package/src/stories/assets/testing.png +0 -0
- package/src/stories/assets/theming.png +0 -0
- package/src/stories/assets/tutorials.svg +0 -1
- package/src/stories/assets/youtube.svg +0 -1
- package/src/stories/button.css +0 -30
- package/src/stories/header.css +0 -32
- package/src/stories/page.css +0 -68
|
@@ -12,7 +12,10 @@ export default css`
|
|
|
12
12
|
display: block;
|
|
13
13
|
align-items: center;
|
|
14
14
|
position: relative;
|
|
15
|
-
|
|
15
|
+
/* 100px used to stand here, room for two arrows positioned absolutely
|
|
16
|
+
65px below the track. The dots are in the flow now, so the space below
|
|
17
|
+
the track is their margin, their 24px and this: 96. */
|
|
18
|
+
margin-bottom: var(--spacing-48);
|
|
16
19
|
max-width: 100%;
|
|
17
20
|
flex-direction: column;
|
|
18
21
|
}
|
|
@@ -27,17 +30,16 @@ export default css`
|
|
|
27
30
|
hand while leaving its line-height unset, which is how the title came to
|
|
28
31
|
render at the browser's default leading and with the browser's default h2
|
|
29
32
|
margins. LightCarousel and CardFeatures declare no such rule either. */
|
|
30
|
-
div.bd-arrow {
|
|
31
|
-
padding: var(--spacing-2);
|
|
32
|
-
visibility: visible;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
33
|
.bd-box:focus-visible,
|
|
36
|
-
.bd-arrow:focus-visible,
|
|
37
34
|
.bd-plus-button:focus-visible,
|
|
38
35
|
.bd-modal-content button:focus-visible {
|
|
39
36
|
/* the one focus ring — see Foundations/Focus */
|
|
40
|
-
|
|
37
|
+
/* forced-colors mode does not paint box-shadow, so the ring
|
|
38
|
+
vanishes there; it does paint an outline, in a system colour. This one
|
|
39
|
+
is transparent everywhere else, and the offset lands it on the ring's
|
|
40
|
+
outer band. */
|
|
41
|
+
outline: var(--border-width-2) solid transparent;
|
|
42
|
+
outline-offset: var(--focus-outline-offset);
|
|
41
43
|
box-shadow: var(--focus-outline-primary);
|
|
42
44
|
}
|
|
43
45
|
|
|
@@ -59,90 +61,16 @@ export default css`
|
|
|
59
61
|
gap: 1rem;
|
|
60
62
|
padding: 1rem;
|
|
61
63
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
/* The dots: bd-pagination-dots, the package's one set of slide controls.
|
|
65
|
+
Two <img role="button"> arrows stood here, painted by eight data-URI SVGs
|
|
66
|
+
with the blues and the disabled grey baked in as hex -- invisible to every
|
|
67
|
+
colour check -- and positioned absolutely 65px under the track. Where they
|
|
68
|
+
sat, the dots sit now: under the track, at its end. */
|
|
69
|
+
.bd-carousel-controls {
|
|
66
70
|
display: flex;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
box-shadow: var(--spacing-0) var(--spacing-2) var(--spacing-4) rgba(0, 0, 0, 0.1);
|
|
71
|
-
position: absolute;
|
|
72
|
-
bottom: -65px;
|
|
73
|
-
background: var(--color-surface-brand);
|
|
74
|
-
color: var(--color-text-inverse);
|
|
75
|
-
/* no role: .bd-arrow is an <img> -- left-arrow.svg / right-arrow.svg in
|
|
76
|
-
carousel.js. It renders no text, so this styles nothing but the alt
|
|
77
|
-
text a browser shows when the SVG fails to load. Fallback typography
|
|
78
|
-
is not a role decision. */
|
|
79
|
-
font-size: var(--typography-fontSize-xl);
|
|
80
|
-
/* no role: .bd-arrow is an <img> -- left-arrow.svg / right-arrow.svg in
|
|
81
|
-
carousel.js. It renders no text, so this styles nothing but the alt
|
|
82
|
-
text a browser shows when the SVG fails to load. Fallback typography
|
|
83
|
-
is not a role decision. */
|
|
84
|
-
line-height: var(--typography-lineHeight-none);
|
|
85
|
-
text-align: center;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
.bd-left-arrow {
|
|
89
|
-
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><circle cx="20" cy="20" r="20" transform="matrix(-1 0 0 1 40 0)" fill="%23006DFF"/><path d="M23.3431 14.4142L21.9289 13L14.8579 20.071L16.2721 21.4853L23.3431 14.4142Z" fill="white"/><path d="M16.2721 18.6568L14.8579 20.071L21.9289 27.1421L23.3431 25.7279L16.2721 18.6568Z" fill="white"/></svg>');
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
.bd-left-arrow:hover {
|
|
94
|
-
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><circle cx="20" cy="20" r="20" transform="matrix(-1 0 0 1 40 0)" fill="%230059D6"/><path d="M23.3431 14.4142L21.9289 13L14.8579 20.071L16.2721 21.4853L23.3431 14.4142Z" fill="white"/><path d="M16.2721 18.6568L14.8579 20.071L21.9289 27.1421L23.3431 25.7279L16.2721 18.6568Z" fill="white"/></svg>');
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
.bd-left-arrow:active {
|
|
98
|
-
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><circle cx="20" cy="20" r="20" transform="matrix(-1 0 0 1 40 0)" fill="%230045AD"/><path d="M23.3431 14.4142L21.9289 13L14.8579 20.071L16.2721 21.4853L23.3431 14.4142Z" fill="white"/><path d="M16.2721 18.6568L14.8579 20.071L21.9289 27.1421L23.3431 25.7279L16.2721 18.6568Z" fill="white"/></svg>');
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
.bd-left-arrow:disabled {
|
|
103
|
-
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><circle cx="20" cy="20" r="20" fill="%23F1F2F3"/><path d="M23.3431 14.4142L21.9289 13L14.8579 20.071L16.2721 21.4853L23.3431 14.4142Z" fill="%23A6ADB4"/><path d="M16.2721 18.6568L14.8579 20.071L21.9289 27.1421L23.3431 25.7279L16.2721 18.6568Z" fill="%23A6ADB4"/></svg>');
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
.bd-right-arrow {
|
|
107
|
-
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><circle cx="20" cy="20" r="20" fill="%23006DFF"/><path d="M16.6569 14.4142L18.0711 13L25.1421 20.071L23.7279 21.4853L16.6569 14.4142Z" fill="white"/><path d="M23.7279 18.6568L25.1421 20.071L18.0711 27.1421L16.6569 25.7279L23.7279 18.6568Z" fill="white"/></svg>');
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
.bd-right-arrow:hover {
|
|
112
|
-
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><circle cx="20" cy="20" r="20" fill="%230059D6"/><path d="M16.6569 14.4142L18.0711 13L25.1421 20.071L23.7279 21.4853L16.6569 14.4142Z" fill="white"/><path d="M23.7279 18.6568L25.1421 20.071L18.0711 27.1421L16.6569 25.7279L23.7279 18.6568Z" fill="white"/></svg>');
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
.bd-right-arrow:active {
|
|
117
|
-
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><circle cx="20" cy="20" r="20" fill="%230045AD"/><path d="M16.6569 14.4142L18.0711 13L25.1421 20.071L23.7279 21.4853L16.6569 14.4142Z" fill="white"/><path d="M23.7279 18.6568L25.1421 20.071L18.0711 27.1421L16.6569 25.7279L23.7279 18.6568Z" fill="white"/></svg>');
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
.bd-right-arrow:disabled {
|
|
122
|
-
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><circle cx="20" cy="20" r="20" fill="%23F1F2F3"/><path d="M16.6569 14.4142L18.0711 13L25.1421 20.071L23.7279 21.4853L16.6569 14.4142Z" fill="%23A6ADB4"/><path d="M23.7279 18.6568L25.1421 20.071L18.0711 27.1421L16.6569 25.7279L23.7279 18.6568Z" fill="%23A6ADB4"/></svg>');
|
|
123
|
-
}
|
|
124
|
-
.bd-right-arrow.bd-disabled {
|
|
125
|
-
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><circle cx="20" cy="20" r="20" fill="%23F1F2F3"/><path d="M16.6569 14.4142L18.0711 13L25.1421 20.071L23.7279 21.4853L16.6569 14.4142Z" fill="%23A6ADB4"/><path d="M23.7279 18.6568L25.1421 20.071L18.0711 27.1421L16.6569 25.7279L23.7279 18.6568Z" fill="%23A6ADB4"/></svg>');
|
|
126
|
-
cursor: not-allowed;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
.bd-left-arrow.bd-disabled {
|
|
130
|
-
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><circle cx="20" cy="20" r="20" fill="%23F1F2F3"/><path d="M23.3431 14.4142L21.9289 13L14.8579 20.071L16.2721 21.4853L23.3431 14.4142Z" fill="%23A6ADB4"/><path d="M16.2721 18.6568L14.8579 20.071L21.9289 27.1421L23.3431 25.7279L16.2721 18.6568Z" fill="%23A6ADB4"/></svg>');
|
|
131
|
-
cursor: not-allowed;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
.bd-disabled {
|
|
135
|
-
background: var(--color-neutral-50);
|
|
136
|
-
color: var(--color-text-muted-inverse);
|
|
137
|
-
cursor: not-allowed;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
.bd-arrow:first-of-type {
|
|
141
|
-
right: 60px;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
.bd-arrow:last-of-type {
|
|
145
|
-
right: var(--spacing-10);
|
|
71
|
+
justify-content: flex-end;
|
|
72
|
+
margin-top: var(--spacing-24);
|
|
73
|
+
padding-inline: var(--spacing-10);
|
|
146
74
|
}
|
|
147
75
|
|
|
148
76
|
.bd-carousel-track {
|
|
@@ -259,14 +187,6 @@ export default css`
|
|
|
259
187
|
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40" fill="none"><circle cx="20" cy="20" r="19" fill="%23006DFF" stroke="%23006DFF" stroke-width="2"/><path d="M19 11H21V29H19V11Z" fill="white"/><path d="M29 19V21L11 21L11 19L29 19Z" fill="white"/></svg>');
|
|
260
188
|
}
|
|
261
189
|
|
|
262
|
-
.bd-left-arrow {
|
|
263
|
-
margin-right: var(--spacing-6);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
.bd-right-arrow {
|
|
267
|
-
margin-left: var(--spacing-6);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
190
|
@media ${unsafeCSS(below('md'))} {
|
|
271
191
|
.bd-carousel-track {
|
|
272
192
|
flex-wrap: nowrap;
|
|
@@ -284,23 +204,6 @@ export default css`
|
|
|
284
204
|
max-width: 90%;
|
|
285
205
|
padding: 1.5em;
|
|
286
206
|
}
|
|
287
|
-
|
|
288
|
-
.bd-arrow {
|
|
289
|
-
bottom: -75px;
|
|
290
|
-
/* no role: .bd-arrow is an <img> -- left-arrow.svg / right-arrow.svg in
|
|
291
|
-
carousel.js. It renders no text, so this styles nothing but the alt
|
|
292
|
-
text a browser shows when the SVG fails to load. Fallback typography
|
|
293
|
-
is not a role decision. */
|
|
294
|
-
font-size: var(--typography-fontSize-base);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
.bd-arrow:first-of-type {
|
|
298
|
-
right: 50px;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
.bd-arrow:last-of-type {
|
|
302
|
-
right: var(--spacing-6);
|
|
303
|
-
}
|
|
304
207
|
}
|
|
305
208
|
|
|
306
209
|
@media (max-width: 480px) {
|
|
@@ -4,6 +4,7 @@ import "../../tokens/tokens.js";
|
|
|
4
4
|
import carouselCSS from "../carousel/carousel.css.js";
|
|
5
5
|
import "../heading/heading.js";
|
|
6
6
|
import "../modal/modal.js";
|
|
7
|
+
import "../pagination-dots/pagination-dots.js";
|
|
7
8
|
export class CarouselItem extends LitElement {
|
|
8
9
|
static properties = {
|
|
9
10
|
icon : { type: String },
|
|
@@ -61,7 +62,9 @@ class CustomCarousel extends LitElement {
|
|
|
61
62
|
static properties = {
|
|
62
63
|
title : { type: String },
|
|
63
64
|
headingLevel: { type: Number, attribute: "heading-level" },
|
|
64
|
-
selectedItem: { type: Object }
|
|
65
|
+
selectedItem: { type: Object },
|
|
66
|
+
_pages : { state: true },
|
|
67
|
+
_page : { state: true }
|
|
65
68
|
};
|
|
66
69
|
|
|
67
70
|
constructor() {
|
|
@@ -69,6 +72,10 @@ class CustomCarousel extends LitElement {
|
|
|
69
72
|
this.selectedItem = null;
|
|
70
73
|
this.title = "";
|
|
71
74
|
this.headingLevel = 2;
|
|
75
|
+
// scroll stops, 1-based, read from the track; see _measure
|
|
76
|
+
this._pages = 1;
|
|
77
|
+
this._page = 1;
|
|
78
|
+
this._seeking = 0;
|
|
72
79
|
}
|
|
73
80
|
|
|
74
81
|
/**
|
|
@@ -98,36 +105,22 @@ class CustomCarousel extends LitElement {
|
|
|
98
105
|
firstUpdated() {
|
|
99
106
|
this.track = this.shadowRoot.querySelector(".bd-carousel-track");
|
|
100
107
|
this.slotElement = this.shadowRoot.querySelector("slot");
|
|
101
|
-
this.leftArrow = this.shadowRoot.querySelector(".bd-left-arrow");
|
|
102
|
-
this.rightArrow = this.shadowRoot.querySelector(".bd-right-arrow");
|
|
103
108
|
|
|
104
|
-
this.track.addEventListener("scroll", () => this.
|
|
109
|
+
this.track.addEventListener("scroll", () => this._measure(), { passive: true });
|
|
110
|
+
this.track.addEventListener("scrollend", () => { this._seeking = 0; this._measure(); });
|
|
111
|
+
// a visitor taking hold of the track ends any scroll a control started
|
|
112
|
+
for (const type of ["pointerdown", "touchstart", "wheel"]) {
|
|
113
|
+
this.track.addEventListener(type, () => { this._seeking = 0; }, { passive: true });
|
|
114
|
+
}
|
|
105
115
|
|
|
106
116
|
this.slotElement.addEventListener("slotchange", () => {
|
|
107
|
-
this.waitForImagesToLoad().then(() =>
|
|
108
|
-
this.updateArrowStates();
|
|
109
|
-
});
|
|
117
|
+
this.waitForImagesToLoad().then(() => this._measure());
|
|
110
118
|
});
|
|
111
119
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}, 0);
|
|
117
|
-
|
|
118
|
-
const arrows = this.shadowRoot.querySelectorAll(".bd-arrow");
|
|
119
|
-
arrows.forEach((arrow) => {
|
|
120
|
-
arrow.addEventListener("mousedown", () =>
|
|
121
|
-
arrow.classList.add("bd-active")
|
|
122
|
-
);
|
|
123
|
-
arrow.addEventListener("mouseup", () =>
|
|
124
|
-
arrow.classList.remove("bd-active")
|
|
125
|
-
);
|
|
126
|
-
arrow.addEventListener("mouseleave", () =>
|
|
127
|
-
arrow.classList.remove("bd-active")
|
|
128
|
-
);
|
|
129
|
-
arrow.addEventListener("keydown", (e) => this._onArrowKeydown(e, arrow));
|
|
130
|
-
});
|
|
120
|
+
this._resizeObserver = new ResizeObserver(() => this._measure());
|
|
121
|
+
this._resizeObserver.observe(this.track);
|
|
122
|
+
|
|
123
|
+
this.waitForImagesToLoad().then(() => this._measure());
|
|
131
124
|
|
|
132
125
|
this.addEventListener("open-modal", (e) => {
|
|
133
126
|
this.openModal(e.detail);
|
|
@@ -135,6 +128,11 @@ class CustomCarousel extends LitElement {
|
|
|
135
128
|
this.enableDragAndTouchScroll(this.track);
|
|
136
129
|
}
|
|
137
130
|
|
|
131
|
+
disconnectedCallback() {
|
|
132
|
+
super.disconnectedCallback();
|
|
133
|
+
this._resizeObserver?.disconnect();
|
|
134
|
+
}
|
|
135
|
+
|
|
138
136
|
waitForImagesToLoad() {
|
|
139
137
|
const assignedElements = this.slotElement.assignedElements({
|
|
140
138
|
flatten: true
|
|
@@ -155,40 +153,47 @@ class CustomCarousel extends LitElement {
|
|
|
155
153
|
return Promise.all(promises);
|
|
156
154
|
}
|
|
157
155
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
const
|
|
164
|
-
if (!
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
left : direction === "left" ? -itemWidth : itemWidth,
|
|
168
|
-
behavior: "smooth"
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
setTimeout(() => this.updateArrowStates(), 300);
|
|
156
|
+
/* The distance from one item to the next: an item's width and the gap. A
|
|
157
|
+
scroll stop is one of these, and the controls count stops, not items --
|
|
158
|
+
the last stop is wherever the track ends, which is short of a full step
|
|
159
|
+
when the items do not divide the width evenly. */
|
|
160
|
+
_step() {
|
|
161
|
+
const first = this.slotElement?.assignedElements({ flatten: true })[0];
|
|
162
|
+
if (!first) return 0;
|
|
163
|
+
const gap = parseFloat(getComputedStyle(this.track).columnGap) || 0;
|
|
164
|
+
return first.offsetWidth + gap;
|
|
172
165
|
}
|
|
173
166
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
this.
|
|
181
|
-
this.
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
167
|
+
/* How many stops the track has and which one it is at, from where it has
|
|
168
|
+
scrolled to -- by a control, a drag, a wheel or a keyboard. While a
|
|
169
|
+
control's smooth scroll is on its way, the dot it asked for stays current
|
|
170
|
+
rather than stepping through every stop it passes. */
|
|
171
|
+
_measure() {
|
|
172
|
+
if (!this.track) return;
|
|
173
|
+
const max = this.track.scrollWidth - this.track.clientWidth;
|
|
174
|
+
const step = this._step();
|
|
175
|
+
if (max <= 1 || step <= 0) {
|
|
176
|
+
this._pages = 1;
|
|
177
|
+
this._page = 1;
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
this._pages = Math.ceil((max - 1) / step) + 1;
|
|
181
|
+
const left = this.track.scrollLeft;
|
|
182
|
+
const page = left >= max - 1 ? this._pages : Math.min(this._pages, Math.round(left / step) + 1);
|
|
183
|
+
if (this._seeking && page !== this._seeking) return;
|
|
184
|
+
this._seeking = 0;
|
|
185
|
+
this._page = page;
|
|
185
186
|
}
|
|
186
187
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
188
|
+
_goToPage(n) {
|
|
189
|
+
const max = this.track.scrollWidth - this.track.clientWidth;
|
|
190
|
+
const reduce = window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
|
|
191
|
+
this._seeking = n;
|
|
192
|
+
this._page = n;
|
|
193
|
+
this.track.scrollTo({
|
|
194
|
+
left : Math.min((n - 1) * this._step(), max),
|
|
195
|
+
behavior: reduce ? "auto" : "smooth"
|
|
196
|
+
});
|
|
192
197
|
}
|
|
193
198
|
|
|
194
199
|
openModal(item) {
|
|
@@ -256,27 +261,18 @@ class CustomCarousel extends LitElement {
|
|
|
256
261
|
</div>`
|
|
257
262
|
: ""}
|
|
258
263
|
|
|
259
|
-
<img
|
|
260
|
-
src="left-arrow.svg"
|
|
261
|
-
alt="Left Arrow"
|
|
262
|
-
class="bd-arrow bd-left-arrow"
|
|
263
|
-
role="button"
|
|
264
|
-
aria-label="Scroll left"
|
|
265
|
-
@click="${() => this.scrollCarousel("left")}"
|
|
266
|
-
tabindex="0"
|
|
267
|
-
/>
|
|
268
264
|
<div class="bd-carousel-track">
|
|
269
265
|
<slot></slot>
|
|
270
266
|
</div>
|
|
271
|
-
<
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
267
|
+
<div class="bd-carousel-controls">
|
|
268
|
+
<bd-pagination-dots
|
|
269
|
+
count=${this._pages}
|
|
270
|
+
current=${this._page}
|
|
271
|
+
label=${this.title || "Slides"}
|
|
272
|
+
item-label="Go to item"
|
|
273
|
+
@bd-change=${e => this._goToPage(e.detail.current)}
|
|
274
|
+
></bd-pagination-dots>
|
|
275
|
+
</div>
|
|
280
276
|
</div>
|
|
281
277
|
|
|
282
278
|
${this.selectedItem
|
|
@@ -10,7 +10,7 @@ export default {
|
|
|
10
10
|
component: `
|
|
11
11
|
**CustomCarousel** and **CarouselItem** are Lit components for a scrollable, draggable card carousel with modal support.
|
|
12
12
|
|
|
13
|
-
- \`<bd-carousel-section>\` — scrollable container with
|
|
13
|
+
- \`<bd-carousel-section>\` — scrollable container with optional title, and the package's slide dots (\`bd-pagination-dots\`) under the track
|
|
14
14
|
- \`<bd-carousel-item>\` — individual card with icon, slotted content, and a \`+\` button to open a modal
|
|
15
15
|
|
|
16
16
|
### Usage
|
|
@@ -23,14 +23,15 @@ export default {
|
|
|
23
23
|
\`\`\`
|
|
24
24
|
|
|
25
25
|
### Behavior
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
26
|
+
- One dot per scroll stop: a stop is one item's width plus the gap, and the last is wherever the track ends
|
|
27
|
+
- A dot scrolls there smoothly (instantly under \`prefers-reduced-motion\`)
|
|
28
|
+
- When every item fits, there is nowhere to go and no controls render
|
|
29
|
+
- Click and drag (mouse + touch) supported for scrolling; the dots follow any scroll
|
|
29
30
|
- Clicking \`+\` on an item fires \`open-modal\` custom event, caught by the carousel to show \`<bd-modal>\`
|
|
30
31
|
- Modal closes via \`close-modal\` event
|
|
31
32
|
|
|
32
33
|
### Keyboard Support
|
|
33
|
-
-
|
|
34
|
+
- The dots are real buttons
|
|
34
35
|
- \`+\` button on items responds to Enter/Space
|
|
35
36
|
`
|
|
36
37
|
}
|
|
@@ -84,7 +85,7 @@ export const Default = {
|
|
|
84
85
|
${sampleItems.map(renderItem)}
|
|
85
86
|
</bd-carousel-section>
|
|
86
87
|
`,
|
|
87
|
-
parameters: { docs: { description: { story: '6-item carousel with title.
|
|
88
|
+
parameters: { docs: { description: { story: '6-item carousel with title. Move with the dots under the track, or drag.' } } }
|
|
88
89
|
};
|
|
89
90
|
|
|
90
91
|
export const NoTitle = {
|
|
@@ -104,7 +105,7 @@ export const FewItems = {
|
|
|
104
105
|
${sampleItems.slice(0, 2).map(renderItem)}
|
|
105
106
|
</bd-carousel-section>
|
|
106
107
|
`,
|
|
107
|
-
parameters: { docs: { description: { story: 'Only 2 items
|
|
108
|
+
parameters: { docs: { description: { story: 'Only 2 items. When every item fits there is nowhere to scroll, so no dots render.' } } }
|
|
108
109
|
};
|
|
109
110
|
|
|
110
111
|
export const SingleItem = {
|
|
@@ -114,7 +115,7 @@ export const SingleItem = {
|
|
|
114
115
|
${renderItem(sampleItems[0])}
|
|
115
116
|
</bd-carousel-section>
|
|
116
117
|
`,
|
|
117
|
-
parameters: { docs: { description: { story: 'Edge case with a single item
|
|
118
|
+
parameters: { docs: { description: { story: 'Edge case with a single item: nothing to scroll, so no dots render.' } } }
|
|
118
119
|
};
|
|
119
120
|
|
|
120
121
|
export const NoIcon = {
|
|
@@ -40,7 +40,12 @@ export default css`
|
|
|
40
40
|
/* the one focus ring — see Foundations/Focus. A two-colour ring whose
|
|
41
41
|
white inner band keeps it visible on a coloured or dark surface, which a
|
|
42
42
|
single-colour outline cannot do. */
|
|
43
|
-
|
|
43
|
+
/* forced-colors mode does not paint box-shadow, so the ring
|
|
44
|
+
vanishes there; it does paint an outline, in a system colour. This one
|
|
45
|
+
is transparent everywhere else, and the offset lands it on the ring's
|
|
46
|
+
outer band. */
|
|
47
|
+
outline: var(--border-width-2) solid transparent;
|
|
48
|
+
outline-offset: var(--focus-outline-offset);
|
|
44
49
|
box-shadow: var(--focus-outline-primary);
|
|
45
50
|
border-radius: var(--focus-outline-radius);
|
|
46
51
|
}
|
|
@@ -62,7 +62,12 @@ export const codeCSS = css`
|
|
|
62
62
|
/* the one focus ring — see Foundations/Focus. A two-colour ring whose
|
|
63
63
|
white inner band keeps it visible on a coloured or dark surface, which a
|
|
64
64
|
single-colour outline cannot do. */
|
|
65
|
-
|
|
65
|
+
/* forced-colors mode does not paint box-shadow, so the ring
|
|
66
|
+
vanishes there; it does paint an outline, in a system colour. This one
|
|
67
|
+
is transparent everywhere else, and the offset lands it on the ring's
|
|
68
|
+
outer band. */
|
|
69
|
+
outline: var(--border-width-2) solid transparent;
|
|
70
|
+
outline-offset: var(--focus-outline-offset);
|
|
66
71
|
box-shadow: var(--focus-outline-primary);
|
|
67
72
|
}
|
|
68
73
|
`;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LitElement, html } from 'lit';
|
|
1
|
+
import { LitElement, html, nothing } from 'lit';
|
|
2
2
|
// Installs the design tokens into document scope; see src/tokens/tokens.js
|
|
3
3
|
import '../../tokens/tokens.js';
|
|
4
4
|
import { codeCSS } from './code.css.js';
|
|
@@ -17,20 +17,61 @@ export class BdCode extends LitElement {
|
|
|
17
17
|
this.label = '';
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
/* Whether the block is wider than its box. Only then is there anything to
|
|
21
|
+
scroll, and only then does it need to be a tab stop. The box changes
|
|
22
|
+
size with the page, and the content with the light DOM, so both are
|
|
23
|
+
watched. */
|
|
24
|
+
_measure() {
|
|
25
|
+
const pre = this.shadowRoot?.querySelector('pre');
|
|
26
|
+
const scrollable = !!pre && pre.scrollWidth > pre.clientWidth;
|
|
27
|
+
if (scrollable !== this._scrollable) {
|
|
28
|
+
this._scrollable = scrollable;
|
|
29
|
+
this.requestUpdate();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
updated() {
|
|
34
|
+
const pre = this.shadowRoot.querySelector('pre');
|
|
35
|
+
if (pre && pre !== this._observed) {
|
|
36
|
+
this._resize?.disconnect();
|
|
37
|
+
this._resize = new ResizeObserver(() => this._measure());
|
|
38
|
+
this._resize.observe(pre);
|
|
39
|
+
this._observed = pre;
|
|
40
|
+
}
|
|
41
|
+
if (!pre) { this._resize?.disconnect(); this._observed = null; }
|
|
42
|
+
this._measure();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
connectedCallback() {
|
|
46
|
+
super.connectedCallback();
|
|
47
|
+
this._content = new MutationObserver(() => this._measure());
|
|
48
|
+
this._content.observe(this, { childList: true, characterData: true, subtree: true });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
disconnectedCallback() {
|
|
52
|
+
this._content?.disconnect();
|
|
53
|
+
this._resize?.disconnect();
|
|
54
|
+
this._observed = null;
|
|
55
|
+
super.disconnectedCallback();
|
|
56
|
+
}
|
|
57
|
+
|
|
20
58
|
render() {
|
|
21
59
|
// Block mode nests <code> inside <pre>: <pre> carries the preformatted
|
|
22
60
|
// whitespace, <code> carries "this is code". A bare <pre> is only
|
|
23
61
|
// preformatted text, so assistive tech got no signal it was code at all,
|
|
24
62
|
// even though the inline variant gave one.
|
|
25
63
|
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
// a
|
|
64
|
+
// A <pre> that scrolls horizontally has to be focusable, or it cannot be
|
|
65
|
+
// scrolled from the keyboard (WCAG 2.1.1), and a focusable element needs
|
|
66
|
+
// a name. It is a group, not a region: a named region is a landmark, so
|
|
67
|
+
// every code block on a page joined the landmark list a screen reader
|
|
68
|
+
// navigates by, beside header, main and footer. And it is a tab stop only
|
|
69
|
+
// while it overflows; a snippet that fits has nothing to scroll and was a
|
|
70
|
+
// stop that did nothing.
|
|
30
71
|
return this.block
|
|
31
72
|
? html`<pre
|
|
32
|
-
tabindex="0"
|
|
33
|
-
role="
|
|
73
|
+
tabindex="${this._scrollable ? '0' : nothing}"
|
|
74
|
+
role="group"
|
|
34
75
|
aria-label="${this.label || 'Code'}"
|
|
35
76
|
><code><slot></slot></code></pre>`
|
|
36
77
|
: html`<code><slot></slot></code>`;
|
|
@@ -33,10 +33,13 @@ Inline renders a single \`<code>\` with a tinted background. Block nests
|
|
|
33
33
|
\`<code>\` carries "this is code", which is the spec's pairing for a code block.
|
|
34
34
|
|
|
35
35
|
### The block is keyboard-scrollable
|
|
36
|
-
A long line scrolls horizontally, and a scrollable
|
|
37
|
-
|
|
38
|
-
\`tabindex="0"\`
|
|
39
|
-
|
|
36
|
+
A long line scrolls horizontally, and a scrollable box that cannot be focused
|
|
37
|
+
cannot be scrolled from the keyboard. So while the block is wider than its box
|
|
38
|
+
it takes \`tabindex="0"\` and shows a focus ring when reached by Tab; a block
|
|
39
|
+
that fits has nothing to scroll and is not a tab stop. It is a
|
|
40
|
+
\`role="group"\` named by \`label\`, defaulting to "Code" — a group rather
|
|
41
|
+
than a region, because a named region is a landmark, and a page of code
|
|
42
|
+
samples would otherwise fill the landmark list a screen reader navigates by.
|
|
40
43
|
|
|
41
44
|
### Notes
|
|
42
45
|
The inline variant sets its colour explicitly rather than inheriting, so it
|
|
@@ -2,7 +2,7 @@ import { LitElement, html } from "lit";
|
|
|
2
2
|
// Installs the design tokens into document scope; see src/tokens/tokens.js
|
|
3
3
|
import "../../tokens/tokens.js";
|
|
4
4
|
import { dividerHorizontal } from "./divider.css.js";
|
|
5
|
-
import { borderColor } from "../shared/token-scales.js";
|
|
5
|
+
import { borderColor, opacity } from "../shared/token-scales.js";
|
|
6
6
|
|
|
7
7
|
export class BdDividerHorizontal extends LitElement {
|
|
8
8
|
static styles = [dividerHorizontal];
|
|
@@ -27,7 +27,14 @@ export class BdDividerHorizontal extends LitElement {
|
|
|
27
27
|
ask for `brand-subtle`. The ink vocabulary is still accepted as a
|
|
28
28
|
fallback for the names already in the markup. */
|
|
29
29
|
this.color = "";
|
|
30
|
-
|
|
30
|
+
/* No opacity by default. It was 0.25, laid over --color-border-default,
|
|
31
|
+
and that composite -- rgb(225,227,230), 1.286:1 on the page -- is
|
|
32
|
+
--color-border-container to within one unit (225,227,229, 1.287:1). So
|
|
33
|
+
every bare divider was spelling a role that exists by dimming a
|
|
34
|
+
different one. The stylesheet now draws that role at full strength,
|
|
35
|
+
which renders the same rule, and opacity is only what a caller asks
|
|
36
|
+
for. */
|
|
37
|
+
this.opacity = "";
|
|
31
38
|
}
|
|
32
39
|
|
|
33
40
|
render() {
|
|
@@ -35,13 +42,14 @@ export class BdDividerHorizontal extends LitElement {
|
|
|
35
42
|
// stylesheet's --color-border-default stands rather than being overridden
|
|
36
43
|
// with an empty value.
|
|
37
44
|
const ink = borderColor(this.color);
|
|
45
|
+
const fade = opacity(this.opacity);
|
|
38
46
|
return html`
|
|
39
47
|
<hr
|
|
40
48
|
class="hr-line"
|
|
41
49
|
style="
|
|
42
50
|
width: ${this.width};
|
|
43
51
|
${ink ? `border-top-color: ${ink};` : ''}
|
|
44
|
-
opacity: ${
|
|
52
|
+
${fade ? `opacity: ${fade};` : ''}
|
|
45
53
|
"
|
|
46
54
|
/>
|
|
47
55
|
`;
|
|
@@ -2,7 +2,7 @@ import { LitElement, html } from "lit";
|
|
|
2
2
|
// Installs the design tokens into document scope; see src/tokens/tokens.js
|
|
3
3
|
import "../../tokens/tokens.js";
|
|
4
4
|
import { dividerVertical } from "./divider.css.js";
|
|
5
|
-
import { borderColor, spacing } from "../shared/token-scales.js";
|
|
5
|
+
import { borderColor, spacing, opacity, borderWidth } from "../shared/token-scales.js";
|
|
6
6
|
|
|
7
7
|
export class BdDividerVertical extends LitElement {
|
|
8
8
|
static styles = [dividerVertical];
|
|
@@ -18,13 +18,19 @@ export class BdDividerVertical extends LitElement {
|
|
|
18
18
|
constructor() {
|
|
19
19
|
super();
|
|
20
20
|
this.height = "16"; // a spacing step; was a 15px literal, off every scale
|
|
21
|
-
|
|
21
|
+
/* A border-width step, resolved to var(--border-width-N); "1px" and the
|
|
22
|
+
other px spellings are read as their step. It was the literal "1px",
|
|
23
|
+
written into the style as given -- out of lint:tokens' reach, so any
|
|
24
|
+
length, or any string at all, could stand in for a hairline. An
|
|
25
|
+
unknown value falls back to the 1px step. */
|
|
26
|
+
this.thickness = "1";
|
|
22
27
|
/* No colour by default — see the note in divider-horizontal.js. The
|
|
23
28
|
stylesheet's --color-border-default is the role for a hairline; "subtle"
|
|
24
29
|
was a text role that happened to resolve to the same step. The prop
|
|
25
30
|
resolves through BORDER_ROLES first; see token-scales.js. */
|
|
26
31
|
this.color = "";
|
|
27
|
-
|
|
32
|
+
// none by default, so none is written; a value is kept only from 0 to 1
|
|
33
|
+
this.opacity = "";
|
|
28
34
|
|
|
29
35
|
}
|
|
30
36
|
|
|
@@ -32,14 +38,15 @@ export class BdDividerVertical extends LitElement {
|
|
|
32
38
|
// See divider-horizontal.js: no colour means no declaration, so the
|
|
33
39
|
// stylesheet's --color-border-default stands.
|
|
34
40
|
const ink = borderColor(this.color);
|
|
41
|
+
const fade = opacity(this.opacity);
|
|
35
42
|
return html`
|
|
36
43
|
<div
|
|
37
44
|
class="divider"
|
|
38
45
|
style="
|
|
39
|
-
width: ${this.thickness};
|
|
46
|
+
width: ${borderWidth(this.thickness) || borderWidth('1')};
|
|
40
47
|
height: ${spacing(this.height)};
|
|
41
48
|
${ink ? `background-color: ${ink};` : ''}
|
|
42
|
-
opacity: ${
|
|
49
|
+
${fade ? `opacity: ${fade};` : ''}
|
|
43
50
|
"
|
|
44
51
|
></div>
|
|
45
52
|
`;
|