zdp-design-system 0.43.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.
Files changed (110) hide show
  1. package/CHANGELOG.md +449 -0
  2. package/LICENSE +21 -0
  3. package/README.md +568 -0
  4. package/THIRD_PARTY_NOTICES.md +34 -0
  5. package/dist/code.ts +2 -0
  6. package/dist/combobox.ts +9 -0
  7. package/dist/command.ts +1 -0
  8. package/dist/components/Accordion.svelte +97 -0
  9. package/dist/components/Avatar.svelte +90 -0
  10. package/dist/components/Badge.svelte +61 -0
  11. package/dist/components/Breadcrumb.svelte +97 -0
  12. package/dist/components/Button.svelte +163 -0
  13. package/dist/components/Callout.svelte +81 -0
  14. package/dist/components/Card.svelte +151 -0
  15. package/dist/components/CardHeader.svelte +58 -0
  16. package/dist/components/Checkbox.svelte +135 -0
  17. package/dist/components/CodeBlock.svelte +247 -0
  18. package/dist/components/Combobox.svelte +552 -0
  19. package/dist/components/CommandField.svelte +230 -0
  20. package/dist/components/ConfirmAction.svelte +307 -0
  21. package/dist/components/Container.svelte +63 -0
  22. package/dist/components/Dialog.svelte +303 -0
  23. package/dist/components/Disclosure.svelte +176 -0
  24. package/dist/components/Divider.svelte +41 -0
  25. package/dist/components/EmptyState.svelte +79 -0
  26. package/dist/components/ErrorText.svelte +18 -0
  27. package/dist/components/Field.svelte +38 -0
  28. package/dist/components/Grid.svelte +76 -0
  29. package/dist/components/HelpText.svelte +17 -0
  30. package/dist/components/Icon.svelte +45 -0
  31. package/dist/components/IconButton.svelte +162 -0
  32. package/dist/components/IdentityChip.svelte +130 -0
  33. package/dist/components/Inline.svelte +85 -0
  34. package/dist/components/InlineCode.svelte +27 -0
  35. package/dist/components/Input.svelte +109 -0
  36. package/dist/components/Kbd.svelte +63 -0
  37. package/dist/components/KeyValue.svelte +73 -0
  38. package/dist/components/Label.svelte +43 -0
  39. package/dist/components/Link.svelte +70 -0
  40. package/dist/components/LocaleSwitcher.svelte +209 -0
  41. package/dist/components/Menu.svelte +491 -0
  42. package/dist/components/Page.svelte +36 -0
  43. package/dist/components/PageHeader.svelte +93 -0
  44. package/dist/components/Pagination.svelte +297 -0
  45. package/dist/components/Popover.svelte +208 -0
  46. package/dist/components/Progress.svelte +111 -0
  47. package/dist/components/Radio.svelte +132 -0
  48. package/dist/components/Section.svelte +52 -0
  49. package/dist/components/SegmentedControl.svelte +190 -0
  50. package/dist/components/Select.svelte +88 -0
  51. package/dist/components/ShareDock.svelte +304 -0
  52. package/dist/components/Sheet.svelte +332 -0
  53. package/dist/components/ShortcutHint.svelte +52 -0
  54. package/dist/components/Skeleton.svelte +82 -0
  55. package/dist/components/SkipLink.svelte +40 -0
  56. package/dist/components/SortHeader.svelte +138 -0
  57. package/dist/components/Spinner.svelte +82 -0
  58. package/dist/components/Stack.svelte +62 -0
  59. package/dist/components/StatusToast.svelte +133 -0
  60. package/dist/components/Surface.svelte +53 -0
  61. package/dist/components/Switch.svelte +152 -0
  62. package/dist/components/Table.svelte +94 -0
  63. package/dist/components/TableToolbar.svelte +195 -0
  64. package/dist/components/Tabs.svelte +205 -0
  65. package/dist/components/TermSheet.svelte +392 -0
  66. package/dist/components/TermTrigger.svelte +70 -0
  67. package/dist/components/TextScaleControl.svelte +219 -0
  68. package/dist/components/Textarea.svelte +106 -0
  69. package/dist/components/ThemeToggle.svelte +148 -0
  70. package/dist/components/Toast.svelte +180 -0
  71. package/dist/components/Toolbar.svelte +83 -0
  72. package/dist/components/Tooltip.svelte +199 -0
  73. package/dist/components/VisuallyHidden.svelte +18 -0
  74. package/dist/disclosure.ts +11 -0
  75. package/dist/focusable.ts +36 -0
  76. package/dist/identity.ts +5 -0
  77. package/dist/index.d.ts +106 -0
  78. package/dist/index.js +76 -0
  79. package/dist/index.ts +106 -0
  80. package/dist/menu.ts +12 -0
  81. package/dist/modal-layer.ts +108 -0
  82. package/dist/pagination.ts +10 -0
  83. package/dist/preferences.js +14 -0
  84. package/dist/preferences.ts +36 -0
  85. package/dist/progress.ts +4 -0
  86. package/dist/schemas/design-tokens.schema.json +119 -0
  87. package/dist/segmented.ts +8 -0
  88. package/dist/share.d.ts +48 -0
  89. package/dist/share.js +115 -0
  90. package/dist/share.ts +99 -0
  91. package/dist/sheet.ts +3 -0
  92. package/dist/shortcuts.js +125 -0
  93. package/dist/shortcuts.ts +153 -0
  94. package/dist/styles/brand-fonts.css +10 -0
  95. package/dist/styles/components.css +4686 -0
  96. package/dist/styles/expressive-fonts.css +2 -0
  97. package/dist/styles/index.css +2 -0
  98. package/dist/styles/locale-fonts.css +4 -0
  99. package/dist/styles/tokens.css +413 -0
  100. package/dist/table-tools.ts +10 -0
  101. package/dist/term.ts +16 -0
  102. package/dist/theme.ts +2 -0
  103. package/dist/toast.ts +14 -0
  104. package/dist/tokens/zdp.tokens.json +241 -0
  105. package/dist/tokens.js +122 -0
  106. package/dist/tokens.ts +123 -0
  107. package/docs/CONSUMER_CONTRACT.md +482 -0
  108. package/docs/EXTERNAL_UI_ADOPTION.md +141 -0
  109. package/docs/INTERACTIVE_PRIMITIVE_AUDIT.md +127 -0
  110. package/package.json +78 -0
@@ -0,0 +1,297 @@
1
+ <script lang="ts">
2
+ import type { ZdpPaginationItem } from '../pagination';
3
+
4
+ type ZdpPaginationRenderItem =
5
+ | (Extract<ZdpPaginationItem, { readonly type: 'page' }> & {
6
+ readonly href: string | null;
7
+ })
8
+ | Extract<ZdpPaginationItem, { readonly type: 'ellipsis' }>;
9
+
10
+ export let currentPage = 1;
11
+ export let totalPages = 1;
12
+ export let siblingCount = 1;
13
+ export let ariaLabel = 'Pagination';
14
+ export let previousLabel = 'Previous';
15
+ export let nextLabel = 'Next';
16
+ export let pageLabel: (page: number) => string = (page) => `${page} page`;
17
+ export let currentLabel: (page: number) => string = (page) => `Current page, page ${page}`;
18
+ export let hrefForPage: ((page: number) => string | null) | null = null;
19
+ export let onPageChange: ((event: MouseEvent, page: number) => void) | null = null;
20
+
21
+ $: normalizedTotalPages = normalizePositiveInteger(totalPages);
22
+ $: activePage = clampPage(currentPage, normalizedTotalPages);
23
+ $: safeSiblingCount = clampSiblingCount(siblingCount);
24
+ $: paginationItems = toRenderItems(
25
+ buildPaginationItems(activePage, normalizedTotalPages, safeSiblingCount)
26
+ );
27
+ $: previousPage = Math.max(1, activePage - 1);
28
+ $: nextPage = Math.min(normalizedTotalPages, activePage + 1);
29
+ $: previousHref = activePage > 1 ? hrefFor(previousPage) : null;
30
+ $: nextHref = activePage < normalizedTotalPages ? hrefFor(nextPage) : null;
31
+ $: hasPrevious = activePage > 1;
32
+ $: hasNext = activePage < normalizedTotalPages;
33
+
34
+ function normalizeInteger(value: number, fallback: number): number {
35
+ return Number.isFinite(value) ? Math.floor(value) : fallback;
36
+ }
37
+
38
+ function normalizePositiveInteger(value: number): number {
39
+ return Math.max(1, normalizeInteger(value, 1));
40
+ }
41
+
42
+ function clampPage(value: number, total: number): number {
43
+ return Math.min(total, Math.max(1, normalizeInteger(value, 1)));
44
+ }
45
+
46
+ function clampSiblingCount(value: number): number {
47
+ return Math.min(3, Math.max(0, normalizeInteger(value, 1)));
48
+ }
49
+
50
+ function hrefFor(page: number): string | null {
51
+ return hrefForPage?.(page) ?? null;
52
+ }
53
+
54
+ function toRenderItems(items: readonly ZdpPaginationItem[]): readonly ZdpPaginationRenderItem[] {
55
+ return items.map((item) => (item.type === 'page' ? { ...item, href: hrefFor(item.page) } : item));
56
+ }
57
+
58
+ function buildPaginationItems(
59
+ current: number,
60
+ total: number,
61
+ siblings: number
62
+ ): readonly ZdpPaginationItem[] {
63
+ const pages = new Set<number>([1, total]);
64
+
65
+ for (let page = current - siblings; page <= current + siblings; page += 1) {
66
+ if (page >= 1 && page <= total) {
67
+ pages.add(page);
68
+ }
69
+ }
70
+
71
+ const sortedPages = [...pages].sort((left, right) => left - right);
72
+ const items: ZdpPaginationItem[] = [];
73
+ let previousPageNumber = 0;
74
+
75
+ for (const page of sortedPages) {
76
+ if (previousPageNumber > 0 && page - previousPageNumber > 1) {
77
+ if (page - previousPageNumber === 2) {
78
+ const adjacentPage = previousPageNumber + 1;
79
+ items.push({
80
+ type: 'page',
81
+ page: adjacentPage,
82
+ key: `page-${adjacentPage}`
83
+ });
84
+ } else {
85
+ items.push({
86
+ type: 'ellipsis',
87
+ key: `ellipsis-${previousPageNumber}-${page}`
88
+ });
89
+ }
90
+ }
91
+
92
+ items.push({
93
+ type: 'page',
94
+ page,
95
+ key: `page-${page}`
96
+ });
97
+ previousPageNumber = page;
98
+ }
99
+
100
+ return items;
101
+ }
102
+
103
+ function handlePageClick(event: MouseEvent, page: number): void {
104
+ onPageChange?.(event, page);
105
+ }
106
+
107
+ function labelForPage(page: number): string {
108
+ return page === activePage ? currentLabel(page) : pageLabel(page);
109
+ }
110
+ </script>
111
+
112
+ <nav class="zdp-pagination" aria-label={ariaLabel}>
113
+ <ol class="zdp-pagination__list">
114
+ <li class="zdp-pagination__item">
115
+ {#if previousHref}
116
+ <a
117
+ class="zdp-pagination__link zdp-pagination__link--control"
118
+ href={previousHref}
119
+ aria-label={previousLabel}
120
+ onclick={(event) => handlePageClick(event, previousPage)}
121
+ >
122
+ {previousLabel}
123
+ </a>
124
+ {:else}
125
+ <button
126
+ class="zdp-pagination__link zdp-pagination__link--control"
127
+ type="button"
128
+ aria-label={previousLabel}
129
+ disabled={!hasPrevious}
130
+ onclick={(event) => handlePageClick(event, previousPage)}
131
+ >
132
+ {previousLabel}
133
+ </button>
134
+ {/if}
135
+ </li>
136
+
137
+ {#each paginationItems as item (item.key)}
138
+ <li class="zdp-pagination__item">
139
+ {#if item.type === 'ellipsis'}
140
+ <span class="zdp-pagination__ellipsis" aria-hidden="true">...</span>
141
+ {:else if item.href && item.page !== activePage}
142
+ <a
143
+ class="zdp-pagination__link"
144
+ href={item.href}
145
+ aria-label={labelForPage(item.page)}
146
+ aria-current={item.page === activePage ? 'page' : undefined}
147
+ onclick={(event) => handlePageClick(event, item.page)}
148
+ >
149
+ {item.page}
150
+ </a>
151
+ {:else}
152
+ <button
153
+ class="zdp-pagination__link"
154
+ type="button"
155
+ aria-label={labelForPage(item.page)}
156
+ aria-current={item.page === activePage ? 'page' : undefined}
157
+ disabled={item.page === activePage}
158
+ onclick={(event) => handlePageClick(event, item.page)}
159
+ >
160
+ {item.page}
161
+ </button>
162
+ {/if}
163
+ </li>
164
+ {/each}
165
+
166
+ <li class="zdp-pagination__item">
167
+ {#if nextHref}
168
+ <a
169
+ class="zdp-pagination__link zdp-pagination__link--control"
170
+ href={nextHref}
171
+ aria-label={nextLabel}
172
+ onclick={(event) => handlePageClick(event, nextPage)}
173
+ >
174
+ {nextLabel}
175
+ </a>
176
+ {:else}
177
+ <button
178
+ class="zdp-pagination__link zdp-pagination__link--control"
179
+ type="button"
180
+ aria-label={nextLabel}
181
+ disabled={!hasNext}
182
+ onclick={(event) => handlePageClick(event, nextPage)}
183
+ >
184
+ {nextLabel}
185
+ </button>
186
+ {/if}
187
+ </li>
188
+ </ol>
189
+ </nav>
190
+
191
+ <style>
192
+ .zdp-pagination {
193
+ --zdp-pagination-focus-bleed: calc(var(--zdp-control-focus-outline-width) + var(--zdp-control-focus-outline-offset));
194
+
195
+ color: var(--zdp-color-ink-normal);
196
+ font-family: var(--zdp-font-family-sans);
197
+ font-size: var(--zdp-type-control-size);
198
+ line-height: var(--zdp-type-control-line-height);
199
+ max-width: 100%;
200
+ min-width: 0;
201
+ overscroll-behavior-inline: contain;
202
+ overflow-x: auto;
203
+ padding-block: var(--zdp-pagination-focus-bleed);
204
+ padding-inline: var(--zdp-pagination-focus-bleed);
205
+ scrollbar-gutter: stable;
206
+ scroll-padding-inline: var(--zdp-pagination-focus-bleed);
207
+ -webkit-overflow-scrolling: touch;
208
+ touch-action: pan-x pan-y;
209
+ }
210
+
211
+ .zdp-pagination__list {
212
+ align-items: center;
213
+ display: flex;
214
+ flex-wrap: nowrap;
215
+ gap: var(--zdp-space-2);
216
+ list-style: none;
217
+ margin: 0;
218
+ min-width: 0;
219
+ padding: 0;
220
+ width: max-content;
221
+ }
222
+
223
+ .zdp-pagination__item {
224
+ display: inline-flex;
225
+ flex: 0 0 auto;
226
+ min-width: 0;
227
+ }
228
+
229
+ .zdp-pagination__link,
230
+ .zdp-pagination__ellipsis {
231
+ align-items: center;
232
+ border: var(--zdp-control-border-width) solid transparent;
233
+ border-radius: var(--zdp-control-radius);
234
+ box-sizing: border-box;
235
+ display: inline-flex;
236
+ font-family: var(--zdp-font-family-sans);
237
+ font-size: var(--zdp-type-control-size);
238
+ font-weight: var(--zdp-font-weight-medium);
239
+ justify-content: center;
240
+ line-height: var(--zdp-type-control-line-height);
241
+ min-block-size: var(--zdp-control-height-sm);
242
+ min-inline-size: var(--zdp-control-height-sm);
243
+ padding: var(--zdp-space-1) var(--zdp-space-2);
244
+ text-align: center;
245
+ white-space: nowrap;
246
+ -webkit-user-select: none;
247
+ user-select: none;
248
+ }
249
+
250
+ .zdp-pagination__link {
251
+ background: var(--zdp-color-surface-panel);
252
+ border-color: var(--zdp-color-line-subtle);
253
+ color: var(--zdp-color-ink-normal);
254
+ cursor: pointer;
255
+ text-decoration-line: none;
256
+ transition:
257
+ background-color var(--zdp-motion-fast) ease,
258
+ border-color var(--zdp-motion-fast) ease,
259
+ color var(--zdp-motion-fast) ease;
260
+ }
261
+
262
+ .zdp-pagination__link--control {
263
+ padding-inline: var(--zdp-space-3);
264
+ }
265
+
266
+ .zdp-pagination__link:hover:not(:disabled):not([aria-current='page']) {
267
+ background: var(--zdp-color-surface-raised);
268
+ border-color: var(--zdp-color-line-strong);
269
+ color: var(--zdp-color-ink-strong);
270
+ }
271
+
272
+ .zdp-pagination__link:focus-visible {
273
+ border-color: var(--zdp-color-focus-line);
274
+ outline: var(--zdp-control-focus-outline-width) solid var(--zdp-color-focus-surface);
275
+ outline-offset: var(--zdp-control-focus-outline-offset);
276
+ }
277
+
278
+ .zdp-pagination__link[aria-current='page'] {
279
+ background: var(--zdp-color-accent-primary-soft);
280
+ border-color: var(--zdp-color-accent-primary-strong);
281
+ color: var(--zdp-color-ink-strong);
282
+ cursor: default;
283
+ }
284
+
285
+ .zdp-pagination__link:disabled {
286
+ cursor: not-allowed;
287
+ opacity: 0.56;
288
+ }
289
+
290
+ .zdp-pagination__link[aria-current='page']:disabled {
291
+ opacity: 1;
292
+ }
293
+
294
+ .zdp-pagination__ellipsis {
295
+ color: var(--zdp-color-ink-muted);
296
+ }
297
+ </style>
@@ -0,0 +1,208 @@
1
+ <script lang="ts" context="module">
2
+ let nextPopoverInstanceId = 0;
3
+ </script>
4
+
5
+ <script lang="ts">
6
+ export let open = false;
7
+ export let idPrefix: string | null = null;
8
+ export let placement: 'top' | 'right' | 'bottom' | 'left' = 'bottom';
9
+ export let align: 'start' | 'center' | 'end' = 'start';
10
+ export let labelledBy: string | null = null;
11
+ export let describedBy: string | null = null;
12
+ export let role: 'dialog' | 'group' | null = 'dialog';
13
+ export let closeOnEscape = true;
14
+ export let closeOnOutside = true;
15
+ export let onOpenChange: ((open: boolean) => void) | null = null;
16
+
17
+ const fallbackIdPrefix = `zdp-popover-${++nextPopoverInstanceId}`;
18
+
19
+ let rootElement: HTMLElement | null = null;
20
+ let previousFocusElement: HTMLElement | null = null;
21
+ let knownOpenState = false;
22
+ let restoreFocusAfterClose = false;
23
+
24
+ $: resolvedIdPrefix = toDomId(idPrefix ?? fallbackIdPrefix);
25
+ $: triggerId = `${resolvedIdPrefix}-trigger`;
26
+ $: panelId = `${resolvedIdPrefix}-panel`;
27
+
28
+ $: if (open !== knownOpenState) {
29
+ knownOpenState = open;
30
+
31
+ if (open) {
32
+ capturePreviousFocus();
33
+ } else if (restoreFocusAfterClose) {
34
+ restorePreviousFocus();
35
+ restoreFocusAfterClose = false;
36
+ }
37
+ }
38
+
39
+ function setOpen(nextOpen: boolean): void {
40
+ if (open === nextOpen) {
41
+ return;
42
+ }
43
+
44
+ open = nextOpen;
45
+ onOpenChange?.(nextOpen);
46
+ }
47
+
48
+ function toggle(): void {
49
+ if (open) {
50
+ close(false);
51
+ return;
52
+ }
53
+
54
+ setOpen(true);
55
+ }
56
+
57
+ function close(restoreFocus = true): void {
58
+ restoreFocusAfterClose = restoreFocus;
59
+ setOpen(false);
60
+ }
61
+
62
+ function capturePreviousFocus(): void {
63
+ if (typeof document === 'undefined') {
64
+ return;
65
+ }
66
+
67
+ const activeElement = document.activeElement;
68
+ previousFocusElement = activeElement instanceof HTMLElement ? activeElement : null;
69
+ }
70
+
71
+ function restorePreviousFocus(): void {
72
+ if (typeof document === 'undefined') {
73
+ return;
74
+ }
75
+
76
+ if (previousFocusElement !== null && document.contains(previousFocusElement)) {
77
+ previousFocusElement.focus();
78
+ }
79
+
80
+ previousFocusElement = null;
81
+ }
82
+
83
+ function handleDocumentClick(event: MouseEvent): void {
84
+ if (!open || !closeOnOutside || rootElement?.contains(event.target as Node)) {
85
+ return;
86
+ }
87
+
88
+ close(false);
89
+ }
90
+
91
+ function handleDocumentKeydown(event: KeyboardEvent): void {
92
+ if (open && event.key === 'Escape' && closeOnEscape) {
93
+ event.preventDefault();
94
+ close();
95
+ }
96
+ }
97
+
98
+ function toDomId(id: string): string {
99
+ return id.trim().replace(/[^a-zA-Z0-9_-]+/g, '-') || 'popover';
100
+ }
101
+ </script>
102
+
103
+ <svelte:document onclick={handleDocumentClick} onkeydown={handleDocumentKeydown} />
104
+
105
+ <span
106
+ class={`zdp-popover zdp-popover--${placement} zdp-popover--align-${align}`}
107
+ data-open={open ? 'true' : 'false'}
108
+ bind:this={rootElement}
109
+ >
110
+ <span class="zdp-popover__trigger" id={triggerId}>
111
+ <slot name="trigger" open={open} toggle={toggle} close={close} panelId={panelId} triggerId={triggerId} />
112
+ </span>
113
+ {#if open}
114
+ <div
115
+ class="zdp-popover__panel"
116
+ id={panelId}
117
+ role={role ?? undefined}
118
+ aria-labelledby={labelledBy ?? triggerId}
119
+ aria-describedby={describedBy ?? undefined}
120
+ tabindex="-1"
121
+ >
122
+ <slot open={open} close={close} panelId={panelId} triggerId={triggerId} />
123
+ </div>
124
+ {/if}
125
+ </span>
126
+
127
+ <style>
128
+ .zdp-popover {
129
+ display: inline-flex;
130
+ min-width: 0;
131
+ position: relative;
132
+ vertical-align: middle;
133
+ }
134
+
135
+ .zdp-popover__trigger {
136
+ display: inline-flex;
137
+ min-width: 0;
138
+ -webkit-user-select: none;
139
+ user-select: none;
140
+ }
141
+
142
+ .zdp-popover__panel {
143
+ background: var(--zdp-color-surface-panel);
144
+ border: var(--zdp-control-border-width) solid var(--zdp-color-line-strong);
145
+ border-radius: var(--zdp-control-radius);
146
+ color: var(--zdp-color-ink-normal);
147
+ display: grid;
148
+ font-family: var(--zdp-font-family-sans);
149
+ gap: var(--zdp-space-3);
150
+ inline-size: max-content;
151
+ max-block-size: min(24rem, calc(100vh - var(--zdp-space-8)));
152
+ max-inline-size: min(22rem, calc(100vw - var(--zdp-space-6)));
153
+ min-inline-size: 12rem;
154
+ overflow: auto;
155
+ padding: var(--zdp-space-3);
156
+ position: absolute;
157
+ z-index: 40;
158
+ }
159
+
160
+ .zdp-popover__panel:focus-visible {
161
+ border-color: var(--zdp-color-focus-line);
162
+ outline: var(--zdp-control-focus-outline-width) solid var(--zdp-color-focus-surface);
163
+ outline-offset: var(--zdp-control-focus-outline-offset);
164
+ }
165
+
166
+ .zdp-popover--top .zdp-popover__panel {
167
+ bottom: calc(100% + var(--zdp-space-2));
168
+ }
169
+
170
+ .zdp-popover--right .zdp-popover__panel {
171
+ left: calc(100% + var(--zdp-space-2));
172
+ top: 0;
173
+ }
174
+
175
+ .zdp-popover--bottom .zdp-popover__panel {
176
+ top: calc(100% + var(--zdp-space-2));
177
+ }
178
+
179
+ .zdp-popover--left .zdp-popover__panel {
180
+ right: calc(100% + var(--zdp-space-2));
181
+ top: 0;
182
+ }
183
+
184
+ .zdp-popover--align-start .zdp-popover__panel {
185
+ left: 0;
186
+ }
187
+
188
+ .zdp-popover--align-center .zdp-popover__panel {
189
+ left: 50%;
190
+ translate: -50% 0;
191
+ }
192
+
193
+ .zdp-popover--align-end .zdp-popover__panel {
194
+ right: 0;
195
+ }
196
+
197
+ .zdp-popover--right.zdp-popover--align-end .zdp-popover__panel,
198
+ .zdp-popover--left.zdp-popover--align-end .zdp-popover__panel {
199
+ bottom: 0;
200
+ top: auto;
201
+ }
202
+
203
+ .zdp-popover--right.zdp-popover--align-center .zdp-popover__panel,
204
+ .zdp-popover--left.zdp-popover--align-center .zdp-popover__panel {
205
+ top: 50%;
206
+ translate: 0 -50%;
207
+ }
208
+ </style>
@@ -0,0 +1,111 @@
1
+ <script lang="ts">
2
+ import type { ZdpProgressSize, ZdpProgressTone } from '../progress';
3
+
4
+ export let value: number | null = null;
5
+ export let min = 0;
6
+ export let max = 100;
7
+ export let tone: ZdpProgressTone = 'primary';
8
+ export let size: ZdpProgressSize = 'md';
9
+ export let ariaLabel = 'Progress';
10
+ export let labelledBy: string | null = null;
11
+ export let describedBy: string | null = null;
12
+ export let valueText: string | null = null;
13
+
14
+ $: hasRange = Number.isFinite(min) && Number.isFinite(max) && max > min;
15
+ $: hasValue = value !== null && Number.isFinite(value) && hasRange;
16
+ $: clampedValue = hasValue ? Math.min(max, Math.max(min, value ?? min)) : min;
17
+ $: progressPercent = hasRange ? ((clampedValue - min) / (max - min)) * 100 : 0;
18
+ $: progressStyle = `--zdp-progress-value: ${progressPercent}%;`;
19
+ </script>
20
+
21
+ <div
22
+ class={`zdp-progress zdp-progress--${tone} zdp-progress--${size}`}
23
+ aria-label={labelledBy ? undefined : ariaLabel}
24
+ aria-labelledby={labelledBy ?? undefined}
25
+ aria-describedby={describedBy ?? undefined}
26
+ aria-valuemin={hasRange ? min : undefined}
27
+ aria-valuemax={hasRange ? max : undefined}
28
+ aria-valuenow={hasValue ? clampedValue : undefined}
29
+ aria-valuetext={valueText ?? undefined}
30
+ data-indeterminate={hasValue ? undefined : 'true'}
31
+ role="progressbar"
32
+ style={progressStyle}
33
+ >
34
+ <span class="zdp-progress__track" aria-hidden="true">
35
+ <span class="zdp-progress__bar"></span>
36
+ </span>
37
+ </div>
38
+
39
+ <style>
40
+ .zdp-progress {
41
+ --zdp-progress-accent: var(--zdp-color-accent-primary);
42
+ --zdp-progress-value: 0%;
43
+
44
+ box-sizing: border-box;
45
+ display: grid;
46
+ inline-size: min(100%, 28rem);
47
+ min-width: 0;
48
+ }
49
+
50
+ .zdp-progress__track {
51
+ background: var(--zdp-color-surface-raised);
52
+ border: var(--zdp-control-border-width) solid var(--zdp-color-line-subtle);
53
+ border-radius: var(--zdp-control-radius);
54
+ box-sizing: border-box;
55
+ display: block;
56
+ inline-size: 100%;
57
+ overflow: hidden;
58
+ -webkit-user-select: none;
59
+ user-select: none;
60
+ }
61
+
62
+ .zdp-progress--sm .zdp-progress__track {
63
+ block-size: var(--zdp-space-2);
64
+ }
65
+
66
+ .zdp-progress--md .zdp-progress__track {
67
+ block-size: var(--zdp-space-3);
68
+ }
69
+
70
+ .zdp-progress__bar {
71
+ background: var(--zdp-progress-accent);
72
+ block-size: 100%;
73
+ display: block;
74
+ inline-size: var(--zdp-progress-value);
75
+ min-inline-size: 0;
76
+ transition: inline-size var(--zdp-motion-fast) ease;
77
+ -webkit-user-select: none;
78
+ user-select: none;
79
+ }
80
+
81
+ .zdp-progress[data-indeterminate="true"] .zdp-progress__bar {
82
+ inline-size: 100%;
83
+ opacity: 0.64;
84
+ }
85
+
86
+ .zdp-progress--neutral {
87
+ --zdp-progress-accent: var(--zdp-color-line-strong);
88
+ }
89
+
90
+ .zdp-progress--primary {
91
+ --zdp-progress-accent: var(--zdp-color-accent-primary);
92
+ }
93
+
94
+ .zdp-progress--success {
95
+ --zdp-progress-accent: var(--zdp-color-accent-success);
96
+ }
97
+
98
+ .zdp-progress--warning {
99
+ --zdp-progress-accent: var(--zdp-color-accent-warning);
100
+ }
101
+
102
+ .zdp-progress--danger {
103
+ --zdp-progress-accent: var(--zdp-color-accent-danger);
104
+ }
105
+
106
+ @media (prefers-reduced-motion: reduce) {
107
+ .zdp-progress__bar {
108
+ transition: none;
109
+ }
110
+ }
111
+ </style>