@astronox/core 0.2.4 → 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/CardAction/CardAction.css +5 -10
- 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 +3 -3
- package/src/components/actions/ThemeToggle/themeToggleController.ts +2 -3
- 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/Tooltip/tooltipController.ts +1 -4
- package/src/components/forms/ChoiceGroup/ChoiceGroup.css +1 -4
- 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/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 +2 -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 +1 -2
- package/src/components/navigation/Footer/Footer.css +3 -9
- package/src/components/navigation/MobileNavigation.css +3 -12
- package/src/components/navigation/navigationTypes.ts +1 -4
- 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 +1 -5
- package/src/i18n/config.ts +2 -8
- package/src/i18n/coreMessages.ts +2 -9
- package/src/i18n/preference.ts +2 -6
- 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/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 +11 -20
- package/src/theme/contracts.ts +1 -6
|
@@ -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>
|
|
@@ -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
|
@@ -30,6 +30,7 @@ export type {
|
|
|
30
30
|
PreparedFormOption,
|
|
31
31
|
PreparedFormState,
|
|
32
32
|
} from './forms/PreparedFormBootstrap/contracts';
|
|
33
|
+
export { parsePreparedFormState } from './forms/PreparedFormBootstrap/parsePreparedFormState';
|
|
33
34
|
export { default as TextInput } from './forms/TextInput/TextInput.astro';
|
|
34
35
|
export { default as Textarea } from './forms/Textarea/Textarea.astro';
|
|
35
36
|
export { default as Spinner } from './feedback/Spinner/Spinner.astro';
|
|
@@ -39,6 +40,7 @@ export { default as Tooltip } from './feedback/Tooltip/Tooltip.astro';
|
|
|
39
40
|
export { default as CanvasFrame } from './media/CanvasFrame/CanvasFrame.astro';
|
|
40
41
|
export { default as EmbedFrame } from './media/EmbedFrame/EmbedFrame.astro';
|
|
41
42
|
export { default as MediaFrame } from './media/MediaFrame/MediaFrame.astro';
|
|
43
|
+
export { default as AdSlot } from './monetization/AdSlot/AdSlot.astro';
|
|
42
44
|
export { default as Breadcrumbs } from './navigation/Breadcrumbs/Breadcrumbs.astro';
|
|
43
45
|
export { default as DesktopNavigation } from './navigation/DesktopNavigation.astro';
|
|
44
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
|
);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from 'astro/types';
|
|
3
|
+
import type { AdSlotState } from '../../../ads/contracts';
|
|
4
|
+
|
|
5
|
+
type Props = Omit<HTMLAttributes<'aside'>, 'aria-label'> & {
|
|
6
|
+
label: string;
|
|
7
|
+
size?: 'compact' | 'standard' | 'tall';
|
|
8
|
+
state?: AdSlotState;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
class: className,
|
|
13
|
+
label,
|
|
14
|
+
size = 'standard',
|
|
15
|
+
state = 'idle',
|
|
16
|
+
...attrs
|
|
17
|
+
}: Props = Astro.props;
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
<aside
|
|
21
|
+
{...attrs}
|
|
22
|
+
class:list={['ad-slot', className]}
|
|
23
|
+
data-ad-size={size}
|
|
24
|
+
data-ad-state={state}
|
|
25
|
+
data-widget="ad-slot"
|
|
26
|
+
aria-label={label}
|
|
27
|
+
aria-busy={state === 'loading' ? 'true' : 'false'}
|
|
28
|
+
>
|
|
29
|
+
<div class="ad-slot__content"><slot /></div>
|
|
30
|
+
<div class="ad-slot__fallback">
|
|
31
|
+
<slot name="fallback"><div class="ad-slot__fallback-canvas"></div></slot>
|
|
32
|
+
</div>
|
|
33
|
+
</aside>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
.ad-slot {
|
|
2
|
+
--ad-slot-min-block-size: 6rem;
|
|
3
|
+
|
|
4
|
+
display: grid;
|
|
5
|
+
inline-size: 100%;
|
|
6
|
+
min-block-size: var(--ad-slot-min-block-size);
|
|
7
|
+
overflow: hidden;
|
|
8
|
+
place-items: center;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.ad-slot[data-ad-size='compact'] {
|
|
12
|
+
--ad-slot-min-block-size: 4rem;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.ad-slot[data-ad-size='tall'] {
|
|
16
|
+
--ad-slot-min-block-size: 15rem;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.ad-slot__content,
|
|
20
|
+
.ad-slot__fallback {
|
|
21
|
+
grid-area: 1 / 1;
|
|
22
|
+
inline-size: 100%;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.ad-slot__fallback {
|
|
26
|
+
display: none;
|
|
27
|
+
block-size: 100%;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.ad-slot__fallback-canvas {
|
|
31
|
+
block-size: 100%;
|
|
32
|
+
min-block-size: var(--ad-slot-min-block-size);
|
|
33
|
+
background:
|
|
34
|
+
linear-gradient(
|
|
35
|
+
135deg,
|
|
36
|
+
transparent 0 46%,
|
|
37
|
+
color-mix(in srgb, var(--nox-theme-border-base) 55%, transparent) 46% 54%,
|
|
38
|
+
transparent 54% 100%
|
|
39
|
+
),
|
|
40
|
+
var(--nox-surface-muted);
|
|
41
|
+
border: 1px solid var(--nox-theme-border-base);
|
|
42
|
+
inline-size: 100%;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.ad-slot[data-ad-state='blocked'] .ad-slot__content,
|
|
46
|
+
.ad-slot[data-ad-state='unavailable'] .ad-slot__content {
|
|
47
|
+
display: none;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.ad-slot[data-ad-state='blocked'] .ad-slot__fallback,
|
|
51
|
+
.ad-slot[data-ad-state='unavailable'] .ad-slot__fallback {
|
|
52
|
+
display: block;
|
|
53
|
+
}
|
|
@@ -33,15 +33,11 @@
|
|
|
33
33
|
column-gap: var(--nox-space-4);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
.site-footer__content[data-footer-columns='2'][
|
|
37
|
-
data-footer-column-separator='divider'
|
|
38
|
-
] {
|
|
36
|
+
.site-footer__content[data-footer-columns='2'][data-footer-column-separator='divider'] {
|
|
39
37
|
column-gap: 0;
|
|
40
38
|
}
|
|
41
39
|
|
|
42
|
-
.site-footer__content[data-footer-columns='2'][
|
|
43
|
-
data-footer-column-separator='divider'
|
|
44
|
-
]
|
|
40
|
+
.site-footer__content[data-footer-columns='2'][data-footer-column-separator='divider']
|
|
45
41
|
> .site-footer__group:nth-child(even)::before {
|
|
46
42
|
display: inline-grid;
|
|
47
43
|
width: calc(
|
|
@@ -146,9 +142,7 @@
|
|
|
146
142
|
grid-column: auto;
|
|
147
143
|
}
|
|
148
144
|
|
|
149
|
-
.site-footer__content[data-footer-columns='2'][
|
|
150
|
-
data-footer-column-separator='divider'
|
|
151
|
-
]
|
|
145
|
+
.site-footer__content[data-footer-columns='2'][data-footer-column-separator='divider']
|
|
152
146
|
> .site-footer__group:nth-child(even)::before {
|
|
153
147
|
content: none;
|
|
154
148
|
}
|
|
@@ -94,8 +94,7 @@
|
|
|
94
94
|
border-radius: var(--nox-radius-md);
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
.site-nav__mobile-action
|
|
98
|
-
> .dropdown[data-dropdown-trigger-appearance='bar'] {
|
|
97
|
+
.site-nav__mobile-action > .dropdown[data-dropdown-trigger-appearance='bar'] {
|
|
99
98
|
min-width: 0;
|
|
100
99
|
block-size: var(--nox-site-header-compact-height);
|
|
101
100
|
border-radius: 0;
|
|
@@ -239,16 +238,8 @@ html[data-mobile-navigation-open] body {
|
|
|
239
238
|
}
|
|
240
239
|
|
|
241
240
|
.site-nav__mobile-footer-link[data-mobile-navigation-accent]:hover {
|
|
242
|
-
border-color: color-mix(
|
|
243
|
-
|
|
244
|
-
var(--mobile-navigation-accent) 88%,
|
|
245
|
-
#000
|
|
246
|
-
);
|
|
247
|
-
background: color-mix(
|
|
248
|
-
in srgb,
|
|
249
|
-
var(--mobile-navigation-accent) 88%,
|
|
250
|
-
#000
|
|
251
|
-
);
|
|
241
|
+
border-color: color-mix(in srgb, var(--mobile-navigation-accent) 88%, #000);
|
|
242
|
+
background: color-mix(in srgb, var(--mobile-navigation-accent) 88%, #000);
|
|
252
243
|
color: #fff;
|
|
253
244
|
}
|
|
254
245
|
}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import type { AstroComponent } from '../widgetTypes';
|
|
2
|
-
import type {
|
|
3
|
-
DisplayMedia,
|
|
4
|
-
NavigationLinkItem,
|
|
5
|
-
} from '../../display/contracts';
|
|
2
|
+
import type { DisplayMedia, NavigationLinkItem } from '../../display/contracts';
|
|
6
3
|
|
|
7
4
|
export type MobileNavigationLabels = {
|
|
8
5
|
closeLabel?: string;
|
|
@@ -72,12 +72,7 @@ const jsonLdNodes = [
|
|
|
72
72
|
<title>{seo.documentTitle}</title>
|
|
73
73
|
{
|
|
74
74
|
site.seo.icons?.map((icon) => (
|
|
75
|
-
<link
|
|
76
|
-
rel="icon"
|
|
77
|
-
href={icon.href}
|
|
78
|
-
type={icon.type}
|
|
79
|
-
sizes={icon.sizes}
|
|
80
|
-
/>
|
|
75
|
+
<link rel="icon" href={icon.href} type={icon.type} sizes={icon.sizes} />
|
|
81
76
|
))
|
|
82
77
|
}
|
|
83
78
|
{
|
|
@@ -34,9 +34,7 @@
|
|
|
34
34
|
content: '';
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
.surface-group:has(> [data-sticky-surface]):not(
|
|
38
|
-
[data-sticky-measured]
|
|
39
|
-
)::before,
|
|
37
|
+
.surface-group:has(> [data-sticky-surface]):not([data-sticky-measured])::before,
|
|
40
38
|
.surface-group[data-sticky-active]::before {
|
|
41
39
|
visibility: visible;
|
|
42
40
|
}
|
|
@@ -52,16 +50,10 @@
|
|
|
52
50
|
[data-surface-sizing='content']
|
|
53
51
|
),
|
|
54
52
|
.surface-group
|
|
55
|
-
> [data-surface]:not([hidden]):not(
|
|
56
|
-
[data-surface-sizing='content']
|
|
57
|
-
):has(
|
|
53
|
+
> [data-surface]:not([hidden]):not([data-surface-sizing='content']):has(
|
|
58
54
|
~ [data-surface]:not([hidden])[data-surface-sizing='content']
|
|
59
55
|
):not(
|
|
60
|
-
:has(
|
|
61
|
-
~ [data-surface]:not([hidden]):not(
|
|
62
|
-
[data-surface-sizing='content']
|
|
63
|
-
)
|
|
64
|
-
)
|
|
56
|
+
:has(~ [data-surface]:not([hidden]):not([data-surface-sizing='content']))
|
|
65
57
|
) {
|
|
66
58
|
flex: 1 1 auto;
|
|
67
59
|
min-height: 0;
|
|
@@ -73,9 +65,7 @@
|
|
|
73
65
|
border-end-end-radius: 0;
|
|
74
66
|
}
|
|
75
67
|
|
|
76
|
-
.surface-group
|
|
77
|
-
> [data-surface]:not([hidden])
|
|
78
|
-
~ [data-surface]:not([hidden]) {
|
|
68
|
+
.surface-group > [data-surface]:not([hidden]) ~ [data-surface]:not([hidden]) {
|
|
79
69
|
border-block-start-width: 0;
|
|
80
70
|
border-start-start-radius: 0;
|
|
81
71
|
border-start-end-radius: 0;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export function readRequiredEnvironmentValue(
|
|
2
|
+
name: string,
|
|
3
|
+
value: string | undefined,
|
|
4
|
+
): string {
|
|
5
|
+
const resolved = value?.trim();
|
|
6
|
+
if (!resolved) throw new Error(`Missing environment variable: ${name}`);
|
|
7
|
+
return resolved;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function readOptionalEnvironmentValue(
|
|
11
|
+
value: string | undefined,
|
|
12
|
+
): string | undefined {
|
|
13
|
+
const resolved = value?.trim();
|
|
14
|
+
return resolved || undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function readRequiredEnvironmentUrl(
|
|
18
|
+
name: string,
|
|
19
|
+
value: string | undefined,
|
|
20
|
+
): string {
|
|
21
|
+
const resolved = readRequiredEnvironmentValue(name, value);
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const url = new URL(resolved);
|
|
25
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:')
|
|
26
|
+
throw new Error();
|
|
27
|
+
return url.toString();
|
|
28
|
+
} catch {
|
|
29
|
+
throw new Error(`Invalid environment URL: ${name}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function readEnvironmentRecord(
|
|
34
|
+
name: string,
|
|
35
|
+
value: string | undefined,
|
|
36
|
+
): Readonly<Record<string, string>> {
|
|
37
|
+
const resolved = readRequiredEnvironmentValue(name, value);
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const parsed: unknown = JSON.parse(resolved);
|
|
41
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
42
|
+
throw new Error();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const entries = Object.entries(parsed);
|
|
46
|
+
if (entries.some(([, entry]) => typeof entry !== 'string'))
|
|
47
|
+
throw new Error();
|
|
48
|
+
return Object.fromEntries(entries) as Readonly<Record<string, string>>;
|
|
49
|
+
} catch {
|
|
50
|
+
throw new Error(`Invalid environment record: ${name}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
package/src/config/index.ts
CHANGED
package/src/config/siteConfig.ts
CHANGED
|
@@ -13,6 +13,7 @@ export type SiteLocaleConfig = {
|
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
export type SiteIdentity = {
|
|
16
|
+
id: string;
|
|
16
17
|
name: string;
|
|
17
18
|
shortName?: string;
|
|
18
19
|
logo?: {
|
|
@@ -68,6 +69,10 @@ export type SiteConfig = {
|
|
|
68
69
|
export function defineSiteConfig<const TConfig extends SiteConfig>(
|
|
69
70
|
config: TConfig,
|
|
70
71
|
): TConfig {
|
|
72
|
+
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(config.identity.id)) {
|
|
73
|
+
throw new Error(`Invalid site identity: ${config.identity.id}`);
|
|
74
|
+
}
|
|
75
|
+
|
|
71
76
|
const localeIds = new Set<string>();
|
|
72
77
|
const urlCodes = new Set<string>();
|
|
73
78
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const storageSegmentPattern = /^[a-z0-9][a-z0-9-]*$/;
|
|
2
|
+
|
|
3
|
+
export function createSiteStorageKey(
|
|
4
|
+
siteId: string,
|
|
5
|
+
...segments: readonly string[]
|
|
6
|
+
): string {
|
|
7
|
+
const values = [siteId, ...segments];
|
|
8
|
+
if (
|
|
9
|
+
segments.length === 0 ||
|
|
10
|
+
values.some((segment) => !storageSegmentPattern.test(segment))
|
|
11
|
+
) {
|
|
12
|
+
throw new Error('Invalid site storage key');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return values.join(':');
|
|
16
|
+
}
|