@astronox/core 0.2.3 → 0.3.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/README.md +16 -1
- package/package.json +18 -4
- package/src/admin/authController.ts +166 -0
- package/src/admin/cache.ts +44 -17
- package/src/admin/index.ts +3 -0
- package/src/admin/preparedDestination.ts +114 -0
- package/src/admin/resourceClient.ts +107 -0
- package/src/ads/adsense/index.ts +1 -0
- package/src/ads/adsense.ts +112 -0
- package/src/ads/contracts.ts +11 -0
- package/src/ads/index.ts +2 -0
- package/src/ads/slot.ts +16 -0
- package/src/auth/contracts.ts +1 -4
- package/src/auth/firebase/client.ts +19 -6
- package/src/auth/firebase/index.ts +1 -0
- package/src/client/formSubmission.ts +6 -1
- package/src/client/preferences.ts +8 -4
- package/src/components/actions/Button/Button.astro +30 -42
- package/src/components/actions/Button/Button.css +1 -1
- package/src/components/actions/CardAction/CardAction.css +8 -13
- package/src/components/actions/IdentityProviderAction/IdentityProviderAction.astro +6 -8
- package/src/components/actions/MediaTrigger/MediaTrigger.astro +1 -1
- package/src/components/actions/ScrollToTop/ScrollToTop.astro +2 -1
- package/src/components/actions/ScrollToTop/ScrollToTop.css +2 -6
- package/src/components/actions/ThemeToggle/ThemeToggle.astro +59 -0
- package/src/components/actions/ThemeToggle/ThemeToggle.css +83 -0
- package/src/components/actions/ThemeToggle/themeToggleController.ts +110 -0
- package/src/components/content/CardFooter/CardFooter.css +2 -4
- package/src/components/content/CardHeader/CardHeader.astro +1 -4
- package/src/components/content/CardLink/CardLink.astro +3 -15
- package/src/components/content/CardLink/CardLink.css +4 -15
- package/src/components/content/CodeBlock/CodeBlock.astro +7 -6
- package/src/components/content/CodeBlock/CodeBlock.css +2 -5
- package/src/components/content/CodeBlock/codeBlockController.ts +1 -2
- package/src/components/content/MarkdownContent/MarkdownContent.astro +1 -3
- package/src/components/content/MarkdownContent/MarkdownContent.css +7 -2
- package/src/components/content/MarkdownContent/MarkdownInline.astro +11 -3
- package/src/components/content/MarkdownContent/MarkdownNode.astro +28 -33
- package/src/components/content/MarkdownContent/MarkdownSymbol.astro +30 -19
- package/src/components/content/MarkdownContent/contracts.ts +15 -15
- package/src/components/content/MarkdownContent/markdownContentRenderer.ts +27 -83
- package/src/components/feedback/PreferenceBootstrap/PreferenceBootstrap.astro +19 -22
- package/src/components/feedback/Spinner/Spinner.astro +1 -2
- package/src/components/feedback/ThemeBootstrap/ThemeBootstrap.astro +31 -0
- package/src/components/feedback/Tooltip/tooltipController.ts +1 -4
- package/src/components/forms/ChoiceGroup/ChoiceGroup.css +2 -5
- package/src/components/forms/DateTimeInput/DateTimeInput.astro +2 -1
- package/src/components/forms/PreparedFormBootstrap/PreparedFormBootstrap.astro +10 -8
- package/src/components/forms/PreparedFormBootstrap/parsePreparedFormState.ts +48 -0
- package/src/components/forms/Textarea/Textarea.astro +2 -2
- package/src/components/icons/BurnIcon.astro +2 -1
- package/src/components/icons/CircleSymbolIcon.css +3 -3
- package/src/components/icons/FieldIcon.astro +10 -4
- package/src/components/icons/GrassIcon.astro +3 -2
- package/src/components/icons/SlowIcon.astro +1 -2
- package/src/components/icons/StunIcon.astro +2 -1
- package/src/components/icons/WaterIcon.astro +3 -2
- package/src/components/index.ts +4 -0
- package/src/components/media/CanvasFrame/CanvasFrame.astro +1 -2
- package/src/components/media/CanvasFrame/canvasFrameController.ts +1 -4
- package/src/components/media/EmbedFrame/EmbedFrame.astro +5 -12
- package/src/components/media/EmbedFrame/embedFrameController.ts +4 -4
- package/src/components/monetization/AdSlot/AdSlot.astro +33 -0
- package/src/components/monetization/AdSlot/AdSlot.css +53 -0
- package/src/components/navigation/DesktopNavigation.css +5 -3
- package/src/components/navigation/Footer/Footer.css +3 -9
- package/src/components/navigation/Header.astro +20 -2
- package/src/components/navigation/MobileNavigation.astro +107 -43
- package/src/components/navigation/MobileNavigation.css +102 -26
- package/src/components/navigation/NavigationBrand.css +1 -1
- package/src/components/navigation/NavigationControl.css +16 -2
- package/src/components/navigation/NavigationItemContent.astro +6 -1
- package/src/components/navigation/mobileNavigationController.ts +0 -16
- package/src/components/navigation/navigationTypes.ts +6 -1
- package/src/components/seo/SeoHead.astro +1 -6
- package/src/composition/SurfaceGroup/SurfaceGroup.css +4 -14
- package/src/config/environment.ts +52 -0
- package/src/config/index.ts +3 -0
- package/src/config/integrations.ts +9 -0
- package/src/config/siteConfig.ts +5 -0
- package/src/config/storage.ts +16 -0
- package/src/content/admin.ts +354 -0
- package/src/content/cache.ts +177 -0
- package/src/content/catalog.ts +72 -0
- package/src/content/contracts.ts +77 -0
- package/src/content/drafts.ts +49 -0
- package/src/content/index.ts +7 -0
- package/src/content/markdown.ts +146 -0
- package/src/content/snapshot.ts +139 -0
- package/src/data/firestore/client/contracts.ts +10 -2
- package/src/data/index.ts +1 -0
- package/src/data/memoizedLoader.ts +13 -0
- package/src/display/contracts.ts +2 -5
- package/src/i18n/config.ts +2 -8
- package/src/i18n/coreMessages.ts +2 -9
- package/src/i18n/preference.ts +2 -6
- package/src/layouts/BaseLayout.astro +5 -0
- package/src/patterns/Combobox/Combobox.astro +9 -12
- package/src/patterns/Combobox/comboboxController.ts +14 -8
- package/src/patterns/Fullscreen/FullscreenControl.astro +5 -1
- package/src/patterns/Fullscreen/fullscreenController.ts +1 -3
- package/src/patterns/MapViewport/MapViewport.css +3 -3
- package/src/patterns/MapViewport/geometry.ts +1 -4
- package/src/patterns/Modal/Modal.css +1 -4
- package/src/patterns/Modal/ModalOpen.astro +5 -1
- package/src/patterns/Table/Table.astro +10 -10
- package/src/patterns/Table/TableBody.astro +1 -4
- package/src/patterns/Table/TableBody.css +2 -4
- package/src/patterns/Table/TablePagination.astro +23 -30
- package/src/patterns/Table/TableSkeleton.astro +1 -5
- package/src/patterns/Table/tableClientController.ts +2 -2
- package/src/patterns/Table/tableFilterController.ts +1 -3
- package/src/patterns/Table/tableHeaderOverflowController.ts +1 -4
- package/src/patterns/Table/tableProgressiveController.ts +62 -60
- package/src/patterns/Table/tableScrollController.ts +3 -2
- package/src/primitives/text/TextBlock/TextBlock.astro +1 -3
- package/src/publication/contracts.ts +11 -0
- package/src/publication/http.ts +31 -0
- package/src/publication/index.ts +2 -0
- package/src/routing/localized.ts +9 -12
- package/src/seo/localizedMeta.ts +2 -6
- package/src/styles/interaction.css +7 -7
- package/src/styles/style.css +12 -20
- package/src/styles/tokens.css +55 -15
- package/src/theme/contracts.ts +3 -6
- package/src/theme/index.ts +1 -0
- package/src/theme/storage.ts +1 -0
|
@@ -24,14 +24,14 @@ const codeBlockTones = new Set<Tone>([
|
|
|
24
24
|
'warning',
|
|
25
25
|
'danger',
|
|
26
26
|
]);
|
|
27
|
-
const defaultListMarker = {
|
|
27
|
+
const defaultListMarker: MarkdownListMarker = {
|
|
28
28
|
symbol: 'bullet',
|
|
29
29
|
tone: 'neutral',
|
|
30
|
-
}
|
|
31
|
-
const nestedListMarker = {
|
|
30
|
+
};
|
|
31
|
+
const nestedListMarker: MarkdownListMarker = {
|
|
32
32
|
symbol: 'arrow-right',
|
|
33
33
|
tone: 'neutral',
|
|
34
|
-
}
|
|
34
|
+
};
|
|
35
35
|
|
|
36
36
|
export function renderMarkdownDocument(
|
|
37
37
|
root: HTMLElement,
|
|
@@ -103,10 +103,7 @@ function renderNode(
|
|
|
103
103
|
document.createTextNode(node.after),
|
|
104
104
|
);
|
|
105
105
|
if (node.delta) {
|
|
106
|
-
const delta = createElement(
|
|
107
|
-
'span',
|
|
108
|
-
'markdown-content__change-delta',
|
|
109
|
-
);
|
|
106
|
+
const delta = createElement('span', 'markdown-content__change-delta');
|
|
110
107
|
delta.dataset.tone = node.delta.tone;
|
|
111
108
|
delta.append('(');
|
|
112
109
|
if (node.delta.sign !== 'neutral') {
|
|
@@ -125,10 +122,7 @@ function renderNode(
|
|
|
125
122
|
}
|
|
126
123
|
values.append(transition);
|
|
127
124
|
if (node.suffix?.length) {
|
|
128
|
-
const suffix = createElement(
|
|
129
|
-
'span',
|
|
130
|
-
'markdown-content__change-suffix',
|
|
131
|
-
);
|
|
125
|
+
const suffix = createElement('span', 'markdown-content__change-suffix');
|
|
132
126
|
appendInline(suffix, node.suffix);
|
|
133
127
|
values.append(suffix);
|
|
134
128
|
}
|
|
@@ -216,10 +210,7 @@ function renderListItem(
|
|
|
216
210
|
appendInline(heading, item.heading);
|
|
217
211
|
body.append(heading);
|
|
218
212
|
} else if (item.resource) {
|
|
219
|
-
const heading = createElement(
|
|
220
|
-
'div',
|
|
221
|
-
'markdown-content__resource-heading',
|
|
222
|
-
);
|
|
213
|
+
const heading = createElement('div', 'markdown-content__resource-heading');
|
|
223
214
|
if (item.resource.mediaPlacement === 'inline') {
|
|
224
215
|
heading.append(
|
|
225
216
|
renderMedia(
|
|
@@ -295,24 +286,35 @@ function renderSymbol(node: MarkdownSymbol): HTMLSpanElement {
|
|
|
295
286
|
symbol.dataset.markdownSymbol = node.name;
|
|
296
287
|
if (node.tone) symbol.dataset.tone = node.tone;
|
|
297
288
|
if (node.lang) symbol.lang = node.lang;
|
|
298
|
-
symbol.append(createSymbolIcon(node
|
|
289
|
+
symbol.append(createSymbolIcon(node));
|
|
299
290
|
|
|
300
291
|
if (node.label) {
|
|
301
292
|
const label = createElement('span', 'markdown-content__symbol-label');
|
|
302
293
|
label.textContent = node.label;
|
|
303
294
|
symbol.append(label);
|
|
304
295
|
} else if (node.accessibleLabel) {
|
|
305
|
-
const label = createElement(
|
|
306
|
-
'span',
|
|
307
|
-
'markdown-content__visually-hidden',
|
|
308
|
-
);
|
|
296
|
+
const label = createElement('span', 'markdown-content__visually-hidden');
|
|
309
297
|
label.textContent = node.accessibleLabel;
|
|
310
298
|
symbol.append(label);
|
|
311
299
|
}
|
|
312
300
|
return symbol;
|
|
313
301
|
}
|
|
314
302
|
|
|
315
|
-
function createSymbolIcon(
|
|
303
|
+
function createSymbolIcon(
|
|
304
|
+
node: MarkdownSymbol,
|
|
305
|
+
): SVGSVGElement | HTMLImageElement {
|
|
306
|
+
if (node.media) {
|
|
307
|
+
const image = document.createElement('img');
|
|
308
|
+
image.classList.add('markdown-content__symbol-media');
|
|
309
|
+
image.src = node.media.src;
|
|
310
|
+
image.alt = node.media.alt;
|
|
311
|
+
image.width = node.media.width;
|
|
312
|
+
image.height = node.media.height;
|
|
313
|
+
image.loading = node.media.loading ?? 'lazy';
|
|
314
|
+
image.decoding = node.media.decoding ?? 'async';
|
|
315
|
+
return image;
|
|
316
|
+
}
|
|
317
|
+
const name = node.name;
|
|
316
318
|
const svg = document.createElementNS(svgNamespace, 'svg');
|
|
317
319
|
svg.setAttribute('viewBox', '0 0 32 32');
|
|
318
320
|
svg.setAttribute('fill', 'none');
|
|
@@ -320,13 +322,6 @@ function createSymbolIcon(name: MarkdownSymbol['name']): SVGSVGElement {
|
|
|
320
322
|
svg.setAttribute('focusable', 'false');
|
|
321
323
|
svg.classList.add('markdown-content__symbol-icon');
|
|
322
324
|
|
|
323
|
-
if (name.startsWith('terrain-')) {
|
|
324
|
-
svg.classList.add('circle-symbol-icon');
|
|
325
|
-
svg.dataset.terrainIcon = name.slice('terrain-'.length);
|
|
326
|
-
appendTerrainIcon(svg, name);
|
|
327
|
-
return svg;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
325
|
if (name === 'bullet') {
|
|
331
326
|
const circle = document.createElementNS(svgNamespace, 'circle');
|
|
332
327
|
circle.setAttribute('cx', '16');
|
|
@@ -358,55 +353,6 @@ function createSymbolIcon(name: MarkdownSymbol['name']): SVGSVGElement {
|
|
|
358
353
|
return svg;
|
|
359
354
|
}
|
|
360
355
|
|
|
361
|
-
function appendTerrainIcon(
|
|
362
|
-
svg: SVGSVGElement,
|
|
363
|
-
name: Extract<MarkdownSymbol['name'], `terrain-${string}`>,
|
|
364
|
-
): void {
|
|
365
|
-
const surface = document.createElementNS(svgNamespace, 'circle');
|
|
366
|
-
surface.setAttribute('cx', '16');
|
|
367
|
-
surface.setAttribute('cy', '16');
|
|
368
|
-
surface.setAttribute('r', '13');
|
|
369
|
-
surface.classList.add('circle-symbol-icon__surface');
|
|
370
|
-
svg.append(surface);
|
|
371
|
-
|
|
372
|
-
if (name === 'terrain-field') {
|
|
373
|
-
const rectangle = document.createElementNS(svgNamespace, 'rect');
|
|
374
|
-
rectangle.setAttribute('x', '9');
|
|
375
|
-
rectangle.setAttribute('y', '9');
|
|
376
|
-
rectangle.setAttribute('width', '14');
|
|
377
|
-
rectangle.setAttribute('height', '14');
|
|
378
|
-
rectangle.setAttribute('rx', '1');
|
|
379
|
-
rectangle.classList.add('circle-symbol-icon__mark');
|
|
380
|
-
const grid = document.createElementNS(svgNamespace, 'path');
|
|
381
|
-
grid.setAttribute('d', 'M13.7 9v14m4.6-14v14M9 13.7h14M9 18.3h14');
|
|
382
|
-
grid.classList.add('circle-symbol-icon__mark');
|
|
383
|
-
svg.append(rectangle, grid);
|
|
384
|
-
return;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
const fill = document.createElementNS(svgNamespace, 'path');
|
|
388
|
-
fill.classList.add('circle-symbol-icon__fill');
|
|
389
|
-
fill.setAttribute(
|
|
390
|
-
'd',
|
|
391
|
-
name === 'terrain-grass'
|
|
392
|
-
? 'M23.5 8.5c.2 8.4-3.7 14-11.7 15.2-3.1-6.8.2-13.5 11.7-15.2Z'
|
|
393
|
-
: name === 'terrain-water'
|
|
394
|
-
? 'M16 5.5c3.1 4.2 7.5 9 7.5 13.3A7.5 7.5 0 1 1 8.5 18.8C8.5 14.5 12.9 9.7 16 5.5Z'
|
|
395
|
-
: 'm5.5 24 7.3-13 3.1 5.1 3.4-7.1 7.2 15H5.5Z',
|
|
396
|
-
);
|
|
397
|
-
const highlight = document.createElementNS(svgNamespace, 'path');
|
|
398
|
-
highlight.classList.add('circle-symbol-icon__highlight');
|
|
399
|
-
highlight.setAttribute(
|
|
400
|
-
'd',
|
|
401
|
-
name === 'terrain-grass'
|
|
402
|
-
? 'M10.4 23.5c2.2-5.3 5.4-8.7 10.2-11.6'
|
|
403
|
-
: name === 'terrain-water'
|
|
404
|
-
? 'M12.2 19.2a4.5 4.5 0 0 0 4.5 4.1'
|
|
405
|
-
: 'm10.8 14.6 2-3.6 3.1 5.1 1.4-2.9 2.4 3.6-3.5-1.1-2.5 2.1-2.9-3.2Z',
|
|
406
|
-
);
|
|
407
|
-
svg.append(fill, highlight);
|
|
408
|
-
}
|
|
409
|
-
|
|
410
356
|
function renderMedia(
|
|
411
357
|
media: DisplayMedia,
|
|
412
358
|
className: string,
|
|
@@ -476,7 +422,8 @@ function renderCodeBlock(
|
|
|
476
422
|
return content;
|
|
477
423
|
}
|
|
478
424
|
|
|
479
|
-
const shell = createElement('nox-code-block'
|
|
425
|
+
const shell = document.createElement('nox-code-block');
|
|
426
|
+
shell.className = 'code-block-shell';
|
|
480
427
|
shell.dataset.widget = 'code-block';
|
|
481
428
|
shell.dataset.tone = tone;
|
|
482
429
|
content.dataset.codeBlockContent = '';
|
|
@@ -530,10 +477,7 @@ function createCopyIcon(): SVGSVGElement {
|
|
|
530
477
|
return svg;
|
|
531
478
|
}
|
|
532
479
|
|
|
533
|
-
function createHeading(
|
|
534
|
-
level: number,
|
|
535
|
-
className: string,
|
|
536
|
-
): HTMLHeadingElement {
|
|
480
|
+
function createHeading(level: number, className: string): HTMLHeadingElement {
|
|
537
481
|
const heading = createElement(
|
|
538
482
|
`h${Math.min(6, Math.max(2, level))}` as keyof HTMLElementTagNameMap,
|
|
539
483
|
className,
|
|
@@ -100,9 +100,7 @@ const payload = definitions.map((definition) => ({
|
|
|
100
100
|
if (from in next && !(to in next)) next[to] = next[from];
|
|
101
101
|
delete next[from];
|
|
102
102
|
}
|
|
103
|
-
for (const [key, fallback] of Object.entries(
|
|
104
|
-
migration.defaults ?? {},
|
|
105
|
-
)) {
|
|
103
|
+
for (const [key, fallback] of Object.entries(migration.defaults ?? {})) {
|
|
106
104
|
if (!(key in next)) next[key] = fallback;
|
|
107
105
|
}
|
|
108
106
|
return next;
|
|
@@ -117,11 +115,7 @@ const payload = definitions.map((definition) => ({
|
|
|
117
115
|
for (const migration of migrations) {
|
|
118
116
|
const previous = parse(
|
|
119
117
|
read(
|
|
120
|
-
storageKey(
|
|
121
|
-
definition,
|
|
122
|
-
migration.fromVersion,
|
|
123
|
-
migration.fromKey,
|
|
124
|
-
),
|
|
118
|
+
storageKey(definition, migration.fromVersion, migration.fromKey),
|
|
125
119
|
),
|
|
126
120
|
);
|
|
127
121
|
const migrated = migrate(previous, migration);
|
|
@@ -138,9 +132,7 @@ const payload = definitions.map((definition) => ({
|
|
|
138
132
|
|
|
139
133
|
const previousSnapshot = window[globalKey];
|
|
140
134
|
const snapshot = {
|
|
141
|
-
values: isRecord(previousSnapshot?.values)
|
|
142
|
-
? previousSnapshot.values
|
|
143
|
-
: {},
|
|
135
|
+
values: isRecord(previousSnapshot?.values) ? previousSnapshot.values : {},
|
|
144
136
|
rawValues: isRecord(previousSnapshot?.rawValues)
|
|
145
137
|
? previousSnapshot.rawValues
|
|
146
138
|
: {},
|
|
@@ -190,7 +182,9 @@ const payload = definitions.map((definition) => ({
|
|
|
190
182
|
const options = [...root.querySelectorAll('[data-combobox-option]')];
|
|
191
183
|
const available = options
|
|
192
184
|
.map((option) => Number(option.dataset.comboboxOption))
|
|
193
|
-
.filter(
|
|
185
|
+
.filter(
|
|
186
|
+
(value) => Number.isFinite(value) && value >= min && value <= max,
|
|
187
|
+
)
|
|
194
188
|
.sort((left, right) => left - right);
|
|
195
189
|
const bounded = Math.min(max, Math.max(min, Number(requestedValue)));
|
|
196
190
|
const value =
|
|
@@ -202,10 +196,7 @@ const payload = definitions.map((definition) => ({
|
|
|
202
196
|
)
|
|
203
197
|
: Math.min(
|
|
204
198
|
max,
|
|
205
|
-
Math.max(
|
|
206
|
-
min,
|
|
207
|
-
min + Math.round((bounded - min) / step) * step,
|
|
208
|
-
),
|
|
199
|
+
Math.max(min, min + Math.round((bounded - min) / step) * step),
|
|
209
200
|
);
|
|
210
201
|
|
|
211
202
|
root.dataset.comboboxNumberMin = String(min);
|
|
@@ -228,8 +219,10 @@ const payload = definitions.map((definition) => ({
|
|
|
228
219
|
const increment = root.querySelector(
|
|
229
220
|
'[data-combobox-number-step="increment"]',
|
|
230
221
|
);
|
|
231
|
-
if (decrement instanceof HTMLButtonElement)
|
|
232
|
-
|
|
222
|
+
if (decrement instanceof HTMLButtonElement)
|
|
223
|
+
decrement.disabled = value <= min;
|
|
224
|
+
if (increment instanceof HTMLButtonElement)
|
|
225
|
+
increment.disabled = value >= max;
|
|
233
226
|
};
|
|
234
227
|
const applyOptions = (root, requestedValue) => {
|
|
235
228
|
const requested = Array.isArray(requestedValue)
|
|
@@ -291,7 +284,8 @@ const payload = definitions.map((definition) => ({
|
|
|
291
284
|
if (root instanceof HTMLElement && root.matches('[data-preference-id]')) {
|
|
292
285
|
applyBinding(root);
|
|
293
286
|
}
|
|
294
|
-
for (const element of root.querySelectorAll?.('[data-preference-id]') ??
|
|
287
|
+
for (const element of root.querySelectorAll?.('[data-preference-id]') ??
|
|
288
|
+
[]) {
|
|
295
289
|
applyBinding(element);
|
|
296
290
|
}
|
|
297
291
|
};
|
|
@@ -350,8 +344,8 @@ const payload = definitions.map((definition) => ({
|
|
|
350
344
|
detail?.mode === 'number'
|
|
351
345
|
? detail.value
|
|
352
346
|
: root.dataset.comboboxSelectionMode === 'multiple'
|
|
353
|
-
? detail?.values ?? []
|
|
354
|
-
: detail?.values?.[0] ?? '',
|
|
347
|
+
? (detail?.values ?? [])
|
|
348
|
+
: (detail?.values?.[0] ?? ''),
|
|
355
349
|
);
|
|
356
350
|
};
|
|
357
351
|
const handlePreferenceChange = (event) => {
|
|
@@ -385,7 +379,10 @@ const payload = definitions.map((definition) => ({
|
|
|
385
379
|
for (const node of record.addedNodes) applyBindings(node);
|
|
386
380
|
}
|
|
387
381
|
});
|
|
388
|
-
observer.observe(document.documentElement, {
|
|
382
|
+
observer.observe(document.documentElement, {
|
|
383
|
+
childList: true,
|
|
384
|
+
subtree: true,
|
|
385
|
+
});
|
|
389
386
|
document.addEventListener('change', handleChange, true);
|
|
390
387
|
document.addEventListener('combobox:change', handleComboboxChange);
|
|
391
388
|
document.addEventListener(changeEvent, handlePreferenceChange);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { defaultThemeStorageKey } from '../../../theme/storage';
|
|
3
|
+
|
|
4
|
+
type Props = {
|
|
5
|
+
storageKey?: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const { storageKey = defaultThemeStorageKey }: Props = Astro.props;
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
<script is:inline define:vars={{ storageKey }}>
|
|
12
|
+
(() => {
|
|
13
|
+
const root = document.documentElement;
|
|
14
|
+
let storedTheme = null;
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const value = window.localStorage.getItem(storageKey);
|
|
18
|
+
if (value === 'light' || value === 'dark') storedTheme = value;
|
|
19
|
+
} catch {
|
|
20
|
+
// The system preference remains available without local storage.
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const systemTheme = window.matchMedia?.('(prefers-color-scheme: dark)')
|
|
24
|
+
.matches
|
|
25
|
+
? 'dark'
|
|
26
|
+
: 'light';
|
|
27
|
+
|
|
28
|
+
root.dataset.theme = storedTheme ?? systemTheme;
|
|
29
|
+
root.dataset.themeStorageKey = storageKey;
|
|
30
|
+
})();
|
|
31
|
+
</script>
|
|
@@ -135,10 +135,7 @@ export function initTooltip(root: HTMLElement): () => void {
|
|
|
135
135
|
viewport.right - viewportGap,
|
|
136
136
|
boundaryRect?.right ?? viewport.right - viewportGap,
|
|
137
137
|
);
|
|
138
|
-
const maximumInlineSize = Math.max(
|
|
139
|
-
0,
|
|
140
|
-
horizontalRight - horizontalLeft,
|
|
141
|
-
);
|
|
138
|
+
const maximumInlineSize = Math.max(0, horizontalRight - horizontalLeft);
|
|
142
139
|
const maximumBlockSize = Math.max(
|
|
143
140
|
0,
|
|
144
141
|
viewport.bottom - viewport.top - viewportGap * 2,
|
|
@@ -19,10 +19,7 @@
|
|
|
19
19
|
.choice-group__options {
|
|
20
20
|
display: grid;
|
|
21
21
|
min-inline-size: 0;
|
|
22
|
-
grid-template-columns: repeat(
|
|
23
|
-
var(--choice-group-columns),
|
|
24
|
-
minmax(0, 1fr)
|
|
25
|
-
);
|
|
22
|
+
grid-template-columns: repeat(var(--choice-group-columns), minmax(0, 1fr));
|
|
26
23
|
gap: var(--nox-space-2);
|
|
27
24
|
}
|
|
28
25
|
|
|
@@ -81,7 +78,7 @@
|
|
|
81
78
|
background: color-mix(
|
|
82
79
|
in srgb,
|
|
83
80
|
var(--nox-tone-seed) 12%,
|
|
84
|
-
var(--nox-
|
|
81
|
+
var(--nox-theme-surface-base)
|
|
85
82
|
);
|
|
86
83
|
color: var(--nox-tone-text);
|
|
87
84
|
}
|
|
@@ -3,7 +3,8 @@ import type { HTMLAttributes } from 'astro/types';
|
|
|
3
3
|
import type { Tone } from '../../../theme/contracts';
|
|
4
4
|
import TextInput from '../TextInput/TextInput.astro';
|
|
5
5
|
|
|
6
|
-
type Props = Omit<HTMLAttributes<'input'>, 'type'> & {
|
|
6
|
+
type Props = Omit<HTMLAttributes<'input'>, 'autofocus' | 'type'> & {
|
|
7
|
+
autofocus?: boolean | 'desktop';
|
|
7
8
|
invalid?: boolean;
|
|
8
9
|
tone?: Tone;
|
|
9
10
|
};
|
|
@@ -34,8 +34,9 @@ const { formId, stateKey }: Props = Astro.props;
|
|
|
34
34
|
return item ? [item] : [];
|
|
35
35
|
};
|
|
36
36
|
const getComboboxRoot = (form, key) =>
|
|
37
|
-
[...form.querySelectorAll('[data-prepared-form-combobox]')]
|
|
38
|
-
|
|
37
|
+
[...form.querySelectorAll('[data-prepared-form-combobox]')].find(
|
|
38
|
+
(candidate) => candidate.dataset.preparedFormCombobox === key,
|
|
39
|
+
);
|
|
39
40
|
const isComboboxReady = (root) =>
|
|
40
41
|
root instanceof HTMLElement &&
|
|
41
42
|
root.querySelector('[data-combobox-options]') instanceof HTMLElement &&
|
|
@@ -110,7 +111,10 @@ const { formId, stateKey }: Props = Astro.props;
|
|
|
110
111
|
button.type = 'button';
|
|
111
112
|
button.role = 'option';
|
|
112
113
|
button.tabIndex = -1;
|
|
113
|
-
button.setAttribute(
|
|
114
|
+
button.setAttribute(
|
|
115
|
+
'aria-selected',
|
|
116
|
+
String(selected.has(option.value)),
|
|
117
|
+
);
|
|
114
118
|
button.dataset.comboboxOption = option.value;
|
|
115
119
|
button.dataset.comboboxSearchText = option.label;
|
|
116
120
|
button.dataset.interactionAccent = 'start';
|
|
@@ -194,10 +198,8 @@ const { formId, stateKey }: Props = Astro.props;
|
|
|
194
198
|
childList: true,
|
|
195
199
|
subtree: true,
|
|
196
200
|
});
|
|
197
|
-
document.addEventListener(
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
{ once: true },
|
|
201
|
-
);
|
|
201
|
+
document.addEventListener('DOMContentLoaded', () => observer.disconnect(), {
|
|
202
|
+
once: true,
|
|
203
|
+
});
|
|
202
204
|
})();
|
|
203
205
|
</script>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { PreparedFormState } from './contracts';
|
|
2
|
+
|
|
3
|
+
export function parsePreparedFormState(
|
|
4
|
+
value: unknown,
|
|
5
|
+
): PreparedFormState | null {
|
|
6
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
|
|
7
|
+
const state = value as PreparedFormState;
|
|
8
|
+
|
|
9
|
+
for (const record of [state.values, state.choices]) {
|
|
10
|
+
if (
|
|
11
|
+
record !== undefined &&
|
|
12
|
+
(!record ||
|
|
13
|
+
typeof record !== 'object' ||
|
|
14
|
+
Array.isArray(record) ||
|
|
15
|
+
Object.values(record).some((item) => typeof item !== 'string'))
|
|
16
|
+
) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (state.comboboxes === undefined) return state;
|
|
22
|
+
if (
|
|
23
|
+
!state.comboboxes ||
|
|
24
|
+
typeof state.comboboxes !== 'object' ||
|
|
25
|
+
Array.isArray(state.comboboxes)
|
|
26
|
+
) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
for (const combobox of Object.values(state.comboboxes)) {
|
|
30
|
+
if (
|
|
31
|
+
!combobox ||
|
|
32
|
+
typeof combobox !== 'object' ||
|
|
33
|
+
!Array.isArray(combobox.options) ||
|
|
34
|
+
!Array.isArray(combobox.values) ||
|
|
35
|
+
combobox.values.some((item) => typeof item !== 'string') ||
|
|
36
|
+
combobox.options.some(
|
|
37
|
+
(option) =>
|
|
38
|
+
!option ||
|
|
39
|
+
typeof option !== 'object' ||
|
|
40
|
+
typeof option.label !== 'string' ||
|
|
41
|
+
typeof option.value !== 'string',
|
|
42
|
+
)
|
|
43
|
+
) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return state;
|
|
48
|
+
}
|
|
@@ -31,5 +31,5 @@ const resolvedTone = tone ?? dataTone;
|
|
|
31
31
|
data-invalid={resolvedInvalid ? true : undefined}
|
|
32
32
|
data-size={size}
|
|
33
33
|
data-tone={resolvedTone}
|
|
34
|
-
aria-invalid={resolvedInvalid ? 'true' : ariaInvalid}
|
|
35
|
-
>
|
|
34
|
+
aria-invalid={resolvedInvalid ? 'true' : ariaInvalid}>{value}</textarea
|
|
35
|
+
>
|
|
@@ -20,5 +20,6 @@ const {
|
|
|
20
20
|
<circle class="circle-symbol-icon__surface" cx="16" cy="16" r="13"></circle>
|
|
21
21
|
<path
|
|
22
22
|
class="circle-symbol-icon__fill"
|
|
23
|
-
d="M17 7c1 5-3 6-3 10 0 1.5.8 2.7 2 3.4-.3-2.4 1.3-3.4 2.2-5.4 2.3 2 3.8 4.1 3.8 6.4 0 3.2-2.7 5.6-6 5.6s-6-2.4-6-5.8C10 15.7 15.7 13 17 7Z"
|
|
23
|
+
d="M17 7c1 5-3 6-3 10 0 1.5.8 2.7 2 3.4-.3-2.4 1.3-3.4 2.2-5.4 2.3 2 3.8 4.1 3.8 6.4 0 3.2-2.7 5.6-6 5.6s-6-2.4-6-5.8C10 15.7 15.7 13 17 7Z"
|
|
24
|
+
></path>
|
|
24
25
|
</svg>
|
|
@@ -3,17 +3,17 @@
|
|
|
3
3
|
--circle-symbol-icon-surface: color-mix(
|
|
4
4
|
in srgb,
|
|
5
5
|
var(--circle-symbol-icon-seed) 32%,
|
|
6
|
-
var(--nox-
|
|
6
|
+
var(--nox-theme-surface-base)
|
|
7
7
|
);
|
|
8
8
|
--circle-symbol-icon-border: color-mix(
|
|
9
9
|
in srgb,
|
|
10
10
|
var(--circle-symbol-icon-seed) 74%,
|
|
11
|
-
var(--nox-
|
|
11
|
+
var(--nox-theme-text-base)
|
|
12
12
|
);
|
|
13
13
|
--circle-symbol-icon-mark: color-mix(
|
|
14
14
|
in srgb,
|
|
15
15
|
var(--circle-symbol-icon-seed) 82%,
|
|
16
|
-
var(--nox-
|
|
16
|
+
var(--nox-theme-text-base)
|
|
17
17
|
);
|
|
18
18
|
|
|
19
19
|
color: var(--circle-symbol-icon-mark);
|
|
@@ -21,8 +21,14 @@ const {
|
|
|
21
21
|
focusable={focusable}
|
|
22
22
|
>
|
|
23
23
|
<circle class="circle-symbol-icon__surface" cx="16" cy="16" r="13"></circle>
|
|
24
|
-
<rect
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
<rect
|
|
25
|
+
class="circle-symbol-icon__mark"
|
|
26
|
+
x="9"
|
|
27
|
+
y="9"
|
|
28
|
+
width="14"
|
|
29
|
+
height="14"
|
|
30
|
+
rx="1"></rect>
|
|
31
|
+
<path
|
|
32
|
+
class="circle-symbol-icon__mark"
|
|
33
|
+
d="M13.7 9v14m4.6-14v14M9 13.7h14M9 18.3h14"></path>
|
|
28
34
|
</svg>
|
|
@@ -24,6 +24,7 @@ const {
|
|
|
24
24
|
<path
|
|
25
25
|
class="circle-symbol-icon__fill"
|
|
26
26
|
d="M23.5 8.5c.2 8.4-3.7 14-11.7 15.2-3.1-6.8.2-13.5 11.7-15.2Z"></path>
|
|
27
|
-
<path
|
|
28
|
-
|
|
27
|
+
<path
|
|
28
|
+
class="circle-symbol-icon__highlight"
|
|
29
|
+
d="M10.4 23.5c2.2-5.3 5.4-8.7 10.2-11.6"></path>
|
|
29
30
|
</svg>
|
|
@@ -20,6 +20,5 @@ const {
|
|
|
20
20
|
<circle class="circle-symbol-icon__surface" cx="16" cy="16" r="13"></circle>
|
|
21
21
|
<path
|
|
22
22
|
class="circle-symbol-icon__mark"
|
|
23
|
-
d="M16 8v16M9 12l14 8M9 20l14-8M13 10l3 3 3-3M13 22l3-3 3 3"
|
|
24
|
-
></path>
|
|
23
|
+
d="M16 8v16M9 12l14 8M9 20l14-8M13 10l3 3 3-3M13 22l3-3 3 3"></path>
|
|
25
24
|
</svg>
|
|
@@ -18,5 +18,6 @@ const {
|
|
|
18
18
|
focusable={focusable}
|
|
19
19
|
>
|
|
20
20
|
<circle class="circle-symbol-icon__surface" cx="16" cy="16" r="13"></circle>
|
|
21
|
-
<path class="circle-symbol-icon__fill" d="m18 7-8 11h5l-1 7 8-11h-5l1-7Z"
|
|
21
|
+
<path class="circle-symbol-icon__fill" d="m18 7-8 11h5l-1 7 8-11h-5l1-7Z"
|
|
22
|
+
></path>
|
|
22
23
|
</svg>
|
|
@@ -25,6 +25,7 @@ const {
|
|
|
25
25
|
class="circle-symbol-icon__fill"
|
|
26
26
|
d="M16 5.5c3.1 4.2 7.5 9 7.5 13.3A7.5 7.5 0 1 1 8.5 18.8C8.5 14.5 12.9 9.7 16 5.5Z"
|
|
27
27
|
></path>
|
|
28
|
-
<path
|
|
29
|
-
|
|
28
|
+
<path
|
|
29
|
+
class="circle-symbol-icon__highlight"
|
|
30
|
+
d="M12.2 19.2a4.5 4.5 0 0 0 4.5 4.1"></path>
|
|
30
31
|
</svg>
|
package/src/components/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { default as ActionListItem } from './actions/ActionListItem/ActionListItem.astro';
|
|
2
2
|
export { default as Button } from './actions/Button/Button.astro';
|
|
3
|
+
export { default as ThemeToggle } from './actions/ThemeToggle/ThemeToggle.astro';
|
|
3
4
|
export { default as CardAction } from './actions/CardAction/CardAction.astro';
|
|
4
5
|
export { default as IdentityProviderAction } from './actions/IdentityProviderAction/IdentityProviderAction.astro';
|
|
5
6
|
export { default as MediaTrigger } from './actions/MediaTrigger/MediaTrigger.astro';
|
|
@@ -29,14 +30,17 @@ export type {
|
|
|
29
30
|
PreparedFormOption,
|
|
30
31
|
PreparedFormState,
|
|
31
32
|
} from './forms/PreparedFormBootstrap/contracts';
|
|
33
|
+
export { parsePreparedFormState } from './forms/PreparedFormBootstrap/parsePreparedFormState';
|
|
32
34
|
export { default as TextInput } from './forms/TextInput/TextInput.astro';
|
|
33
35
|
export { default as Textarea } from './forms/Textarea/Textarea.astro';
|
|
34
36
|
export { default as Spinner } from './feedback/Spinner/Spinner.astro';
|
|
35
37
|
export { default as PreferenceBootstrap } from './feedback/PreferenceBootstrap/PreferenceBootstrap.astro';
|
|
38
|
+
export { default as ThemeBootstrap } from './feedback/ThemeBootstrap/ThemeBootstrap.astro';
|
|
36
39
|
export { default as Tooltip } from './feedback/Tooltip/Tooltip.astro';
|
|
37
40
|
export { default as CanvasFrame } from './media/CanvasFrame/CanvasFrame.astro';
|
|
38
41
|
export { default as EmbedFrame } from './media/EmbedFrame/EmbedFrame.astro';
|
|
39
42
|
export { default as MediaFrame } from './media/MediaFrame/MediaFrame.astro';
|
|
43
|
+
export { default as AdSlot } from './monetization/AdSlot/AdSlot.astro';
|
|
40
44
|
export { default as Breadcrumbs } from './navigation/Breadcrumbs/Breadcrumbs.astro';
|
|
41
45
|
export { default as DesktopNavigation } from './navigation/DesktopNavigation.astro';
|
|
42
46
|
export { default as Header } from './navigation/Header.astro';
|
|
@@ -53,10 +53,7 @@ export function markCanvasFrameReady(root: HTMLElement): void {
|
|
|
53
53
|
root.dataset.canvasFrameReady = 'true';
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
export function markCanvasFrameError(
|
|
57
|
-
root: HTMLElement,
|
|
58
|
-
message: string,
|
|
59
|
-
): void {
|
|
56
|
+
export function markCanvasFrameError(root: HTMLElement, message: string): void {
|
|
60
57
|
const loading = root.querySelector<HTMLElement>(
|
|
61
58
|
'[data-canvas-frame-loading]',
|
|
62
59
|
);
|
|
@@ -63,11 +63,9 @@ const resolvedTone = tone ?? dataTone;
|
|
|
63
63
|
data-fullscreen-region={fullscreen ? true : undefined}
|
|
64
64
|
data-widget="embed-frame"
|
|
65
65
|
data-tone={resolvedTone}
|
|
66
|
-
aria-busy={
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
: 'false'
|
|
70
|
-
}
|
|
66
|
+
aria-busy={activation === 'immediate' && loadingIndicator === 'spinner'
|
|
67
|
+
? 'true'
|
|
68
|
+
: 'false'}
|
|
71
69
|
>
|
|
72
70
|
<iframe
|
|
73
71
|
{...attrs}
|
|
@@ -78,16 +76,11 @@ const resolvedTone = tone ?? dataTone;
|
|
|
78
76
|
width={width}
|
|
79
77
|
height={height}
|
|
80
78
|
loading={loading}
|
|
81
|
-
allowfullscreen
|
|
82
|
-
></iframe>
|
|
79
|
+
allowfullscreen></iframe>
|
|
83
80
|
{
|
|
84
81
|
activation === 'manual' && activationLabel ? (
|
|
85
82
|
<span class="embed-frame__activation" data-embed-frame-activation-layer>
|
|
86
|
-
<Button
|
|
87
|
-
type="button"
|
|
88
|
-
prominent
|
|
89
|
-
data-embed-frame-activate
|
|
90
|
-
>
|
|
83
|
+
<Button type="button" prominent data-embed-frame-activate>
|
|
91
84
|
{activationLabel}
|
|
92
85
|
</Button>
|
|
93
86
|
</span>
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
const activateEvent = 'nox:embed-frame:activate';
|
|
2
2
|
|
|
3
|
-
export function activateEmbedFrame(
|
|
3
|
+
export function activateEmbedFrame(
|
|
4
|
+
root: HTMLElement,
|
|
5
|
+
): HTMLIFrameElement | null {
|
|
4
6
|
const iframe = root.querySelector<HTMLIFrameElement>(
|
|
5
7
|
'[data-embed-frame-content]',
|
|
6
8
|
);
|
|
@@ -14,9 +16,7 @@ export const initEmbedFrame = (root: HTMLElement): (() => void) => {
|
|
|
14
16
|
const iframe = root.querySelector<HTMLIFrameElement>(
|
|
15
17
|
'[data-embed-frame-content]',
|
|
16
18
|
);
|
|
17
|
-
const loading = root.querySelector<HTMLElement>(
|
|
18
|
-
'[data-embed-frame-loading]',
|
|
19
|
-
);
|
|
19
|
+
const loading = root.querySelector<HTMLElement>('[data-embed-frame-loading]');
|
|
20
20
|
const activationLayer = root.querySelector<HTMLElement>(
|
|
21
21
|
'[data-embed-frame-activation-layer]',
|
|
22
22
|
);
|