@repobit/dex-system-design 1.0.1 → 2.0.0
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 +81 -0
- package/README.md +43 -12
- package/package.json +4 -2
- 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
|
@@ -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
|
`;
|
|
@@ -3,7 +3,10 @@ import { css } from "lit";
|
|
|
3
3
|
export const dividerHorizontal = css`
|
|
4
4
|
.hr-line {
|
|
5
5
|
border: none;
|
|
6
|
-
|
|
6
|
+
/* --color-border-container, the rule every bare divider already rendered:
|
|
7
|
+
it used to be --color-border-default dimmed to 0.25, which composites to
|
|
8
|
+
the same colour. See divider-horizontal.js. */
|
|
9
|
+
border-top: var(--border-width-1) solid var(--color-border-container);
|
|
7
10
|
margin: 0;
|
|
8
11
|
width: 100%;
|
|
9
12
|
margin-left: auto;
|
|
@@ -33,12 +33,12 @@ Two divider components are available:
|
|
|
33
33
|
control : { type: 'select' },
|
|
34
34
|
options : ['primary', 'strong', 'muted', 'subtle', 'inverse', 'success', 'warning', 'error'],
|
|
35
35
|
description: '(Horizontal) A semantic colour role, not a CSS colour.',
|
|
36
|
-
table : { type: { summary: 'string' }, defaultValue: { summary: '
|
|
36
|
+
table : { type: { summary: 'string' }, defaultValue: { summary: "'' (--color-border-container)" }, category: 'Horizontal' }
|
|
37
37
|
},
|
|
38
38
|
hOpacity: {
|
|
39
39
|
control : 'text',
|
|
40
|
-
description: '(Horizontal) Opacity
|
|
41
|
-
table : { type: { summary: 'string' }, defaultValue: { summary: '
|
|
40
|
+
description: '(Horizontal) Opacity from 0 to 1. Anything else is ignored. None by default.',
|
|
41
|
+
table : { type: { summary: 'string' }, defaultValue: { summary: "''" }, category: 'Horizontal' }
|
|
42
42
|
},
|
|
43
43
|
height: {
|
|
44
44
|
control: { type: 'select' },
|
|
@@ -49,8 +49,8 @@ Two divider components are available:
|
|
|
49
49
|
},
|
|
50
50
|
thickness: {
|
|
51
51
|
control : 'text',
|
|
52
|
-
description: '(Vertical)
|
|
53
|
-
table : { type: { summary: 'string' }, defaultValue: { summary: '
|
|
52
|
+
description: '(Vertical) A border-width step — 0, 1, 2, 3, 4 or 8, also written "1px" etc. Resolves to var(--border-width-N); anything else is the 1px step.',
|
|
53
|
+
table : { type: { summary: 'string' }, defaultValue: { summary: '1' }, category: 'Vertical' }
|
|
54
54
|
},
|
|
55
55
|
vColor: {
|
|
56
56
|
control : { type: 'select' },
|
|
@@ -71,7 +71,7 @@ Two divider components are available:
|
|
|
71
71
|
export const HorizontalDefault = {
|
|
72
72
|
name : 'Horizontal — Default',
|
|
73
73
|
render : () => html`<bd-divider-horizontal></bd-divider-horizontal>`,
|
|
74
|
-
parameters: { docs: { description: { story: 'Default horizontal divider
|
|
74
|
+
parameters: { docs: { description: { story: 'Default horizontal divider: --color-border-container at full strength, no opacity.' } } }
|
|
75
75
|
};
|
|
76
76
|
|
|
77
77
|
export const HorizontalFullWidth = {
|