@astronox/core 0.2.4 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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.astro +7 -0
- package/src/components/actions/CardAction/CardAction.css +31 -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 +7 -4
- 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.astro +10 -2
- package/src/components/navigation/MobileNavigation.css +8 -22
- package/src/components/navigation/navigationTypes.ts +1 -4
- package/src/components/seo/SeoHead.astro +21 -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 +7 -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 +14 -7
- package/src/styles/style.css +11 -20
- package/src/theme/contracts.ts +1 -6
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { AdConsentProvider, AdProvider } from './contracts';
|
|
2
|
+
import { setAdSlotState } from './slot';
|
|
3
|
+
|
|
4
|
+
declare global {
|
|
5
|
+
interface Window {
|
|
6
|
+
adsbygoogle?: Record<string, unknown>[];
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function createAdsenseProvider({
|
|
11
|
+
clientId,
|
|
12
|
+
consent,
|
|
13
|
+
loadTimeoutMs,
|
|
14
|
+
}: {
|
|
15
|
+
clientId: string;
|
|
16
|
+
consent: AdConsentProvider;
|
|
17
|
+
loadTimeoutMs: number;
|
|
18
|
+
}): AdProvider {
|
|
19
|
+
let loading: Promise<void> | null = null;
|
|
20
|
+
|
|
21
|
+
const load = async (): Promise<void> => {
|
|
22
|
+
if (!(await consent.canLoadAds())) {
|
|
23
|
+
throw new Error('Advertising consent was not granted');
|
|
24
|
+
}
|
|
25
|
+
loading ??= loadAdsenseScript(clientId, loadTimeoutMs).catch((reason) => {
|
|
26
|
+
loading = null;
|
|
27
|
+
throw reason;
|
|
28
|
+
});
|
|
29
|
+
return loading;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
load,
|
|
34
|
+
async request(slot) {
|
|
35
|
+
setAdSlotState(slot, 'loading');
|
|
36
|
+
try {
|
|
37
|
+
await load();
|
|
38
|
+
window.adsbygoogle ??= [];
|
|
39
|
+
window.adsbygoogle.push({});
|
|
40
|
+
observeAdsenseSlot(slot);
|
|
41
|
+
} catch (reason) {
|
|
42
|
+
setAdSlotState(slot, 'blocked');
|
|
43
|
+
throw reason;
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function loadAdsenseScript(
|
|
50
|
+
clientId: string,
|
|
51
|
+
loadTimeoutMs: number,
|
|
52
|
+
): Promise<void> {
|
|
53
|
+
const existing = document.querySelector<HTMLScriptElement>(
|
|
54
|
+
'script[data-astronox-adsense]',
|
|
55
|
+
);
|
|
56
|
+
if (existing?.dataset.loaded === 'true') return Promise.resolve();
|
|
57
|
+
|
|
58
|
+
return new Promise((resolve, reject) => {
|
|
59
|
+
const script = existing ?? document.createElement('script');
|
|
60
|
+
script.async = true;
|
|
61
|
+
script.crossOrigin = 'anonymous';
|
|
62
|
+
script.dataset.astronoxAdsense = '';
|
|
63
|
+
script.src = `https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=${encodeURIComponent(clientId)}`;
|
|
64
|
+
const timeout = window.setTimeout(
|
|
65
|
+
() => reject(new Error('AdSense loading timed out')),
|
|
66
|
+
loadTimeoutMs,
|
|
67
|
+
);
|
|
68
|
+
script.addEventListener(
|
|
69
|
+
'load',
|
|
70
|
+
() => {
|
|
71
|
+
window.clearTimeout(timeout);
|
|
72
|
+
script.dataset.loaded = 'true';
|
|
73
|
+
resolve();
|
|
74
|
+
},
|
|
75
|
+
{ once: true },
|
|
76
|
+
);
|
|
77
|
+
script.addEventListener(
|
|
78
|
+
'error',
|
|
79
|
+
() => {
|
|
80
|
+
window.clearTimeout(timeout);
|
|
81
|
+
reject(new Error('AdSense failed to load'));
|
|
82
|
+
},
|
|
83
|
+
{ once: true },
|
|
84
|
+
);
|
|
85
|
+
if (!existing) document.head.append(script);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function observeAdsenseSlot(slot: HTMLElement): void {
|
|
90
|
+
const advertisement = slot.querySelector<HTMLElement>('.adsbygoogle');
|
|
91
|
+
if (!advertisement) {
|
|
92
|
+
setAdSlotState(slot, 'unavailable');
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const sync = (): void => {
|
|
97
|
+
const status = advertisement.dataset.adStatus;
|
|
98
|
+
if (status === 'filled') setAdSlotState(slot, 'filled');
|
|
99
|
+
if (status === 'unfilled') setAdSlotState(slot, 'unavailable');
|
|
100
|
+
};
|
|
101
|
+
sync();
|
|
102
|
+
if (advertisement.dataset.adStatus) return;
|
|
103
|
+
|
|
104
|
+
const observer = new MutationObserver(() => {
|
|
105
|
+
sync();
|
|
106
|
+
if (advertisement.dataset.adStatus) observer.disconnect();
|
|
107
|
+
});
|
|
108
|
+
observer.observe(advertisement, {
|
|
109
|
+
attributeFilter: ['data-ad-status'],
|
|
110
|
+
attributes: true,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type AdSlotState =
|
|
2
|
+
'idle' | 'loading' | 'filled' | 'blocked' | 'unavailable';
|
|
3
|
+
|
|
4
|
+
export type AdConsentProvider = {
|
|
5
|
+
canLoadAds(): boolean | Promise<boolean>;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export interface AdProvider {
|
|
9
|
+
load(): Promise<void>;
|
|
10
|
+
request(slot: HTMLElement): Promise<void>;
|
|
11
|
+
}
|
package/src/ads/index.ts
ADDED
package/src/ads/slot.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AdSlotState } from './contracts';
|
|
2
|
+
|
|
3
|
+
export function setAdSlotState(slot: HTMLElement, state: AdSlotState): void {
|
|
4
|
+
slot.dataset.adState = state;
|
|
5
|
+
slot.setAttribute('aria-busy', state === 'loading' ? 'true' : 'false');
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function getAdSlotState(slot: HTMLElement): AdSlotState {
|
|
9
|
+
const state = slot.dataset.adState;
|
|
10
|
+
return state === 'loading' ||
|
|
11
|
+
state === 'filled' ||
|
|
12
|
+
state === 'blocked' ||
|
|
13
|
+
state === 'unavailable'
|
|
14
|
+
? state
|
|
15
|
+
: 'idle';
|
|
16
|
+
}
|
package/src/auth/contracts.ts
CHANGED
|
@@ -22,10 +22,7 @@ export interface IdentityProvider<TCredential = string> {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
export interface SessionProvider<TCredential = string> {
|
|
25
|
-
create(
|
|
26
|
-
credential: TCredential,
|
|
27
|
-
actor: AuthActor,
|
|
28
|
-
): Promise<AuthSessionGrant>;
|
|
25
|
+
create(credential: TCredential, actor: AuthActor): Promise<AuthSessionGrant>;
|
|
29
26
|
get(token: string): Promise<AuthSession | null>;
|
|
30
27
|
revoke(token: string): Promise<void>;
|
|
31
28
|
}
|
|
@@ -5,9 +5,10 @@ import {
|
|
|
5
5
|
GoogleAuthProvider,
|
|
6
6
|
getAuth,
|
|
7
7
|
initializeAuth,
|
|
8
|
-
|
|
8
|
+
onIdTokenChanged,
|
|
9
9
|
signInWithPopup,
|
|
10
10
|
signOut,
|
|
11
|
+
getIdTokenResult,
|
|
11
12
|
type Auth,
|
|
12
13
|
type User,
|
|
13
14
|
} from 'firebase/auth';
|
|
@@ -42,10 +43,11 @@ function initializeBrowserAuth(app: FirebaseApp): Auth {
|
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
function toAuthActor(user: User): AuthActor {
|
|
46
|
+
async function toAuthActor(user: User): Promise<AuthActor> {
|
|
47
|
+
const token = await getIdTokenResult(user);
|
|
46
48
|
return {
|
|
47
49
|
id: user.uid,
|
|
48
|
-
claims: {},
|
|
50
|
+
claims: { ...token.claims },
|
|
49
51
|
displayName: user.displayName ?? undefined,
|
|
50
52
|
email: user.email ?? undefined,
|
|
51
53
|
imageUrl: user.photoURL ?? undefined,
|
|
@@ -64,17 +66,28 @@ export function createFirebaseGoogleAuthClient(
|
|
|
64
66
|
|
|
65
67
|
return {
|
|
66
68
|
observe(listener, onError) {
|
|
67
|
-
|
|
69
|
+
let revision = 0;
|
|
70
|
+
return onIdTokenChanged(
|
|
68
71
|
auth,
|
|
69
72
|
(user) => {
|
|
70
|
-
|
|
73
|
+
const currentRevision = ++revision;
|
|
74
|
+
if (!user) {
|
|
75
|
+
listener(null);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
void toAuthActor(user).then(
|
|
79
|
+
(actor) => {
|
|
80
|
+
if (revision === currentRevision) listener(actor);
|
|
81
|
+
},
|
|
82
|
+
(reason) => onError?.(reason),
|
|
83
|
+
);
|
|
71
84
|
},
|
|
72
85
|
onError,
|
|
73
86
|
);
|
|
74
87
|
},
|
|
75
88
|
async signIn() {
|
|
76
89
|
const result = await signInWithPopup(auth, provider);
|
|
77
|
-
return toAuthActor(result.user);
|
|
90
|
+
return await toAuthActor(result.user);
|
|
78
91
|
},
|
|
79
92
|
async signOut() {
|
|
80
93
|
await signOut(auth);
|
|
@@ -140,7 +140,12 @@ function compareValues(
|
|
|
140
140
|
left.getTime() === right.getTime()
|
|
141
141
|
);
|
|
142
142
|
}
|
|
143
|
-
if (
|
|
143
|
+
if (
|
|
144
|
+
!left ||
|
|
145
|
+
!right ||
|
|
146
|
+
typeof left !== 'object' ||
|
|
147
|
+
typeof right !== 'object'
|
|
148
|
+
) {
|
|
144
149
|
return false;
|
|
145
150
|
}
|
|
146
151
|
|
|
@@ -84,7 +84,9 @@ export function definePreference<T>(
|
|
|
84
84
|
): PreferenceDefinition<T> {
|
|
85
85
|
assertDefinition(definition);
|
|
86
86
|
if (!matchesPreferenceSchema(definition.defaultValue, definition.schema)) {
|
|
87
|
-
throw new TypeError(
|
|
87
|
+
throw new TypeError(
|
|
88
|
+
`Invalid default value for preference ${definition.id}`,
|
|
89
|
+
);
|
|
88
90
|
}
|
|
89
91
|
return definition;
|
|
90
92
|
}
|
|
@@ -221,7 +223,10 @@ export function createPreferenceRegistry({
|
|
|
221
223
|
};
|
|
222
224
|
},
|
|
223
225
|
destroy(): void {
|
|
224
|
-
document.removeEventListener(
|
|
226
|
+
document.removeEventListener(
|
|
227
|
+
preferenceChangeEvent,
|
|
228
|
+
handlePreferenceChange,
|
|
229
|
+
);
|
|
225
230
|
window.removeEventListener('storage', handleStorage);
|
|
226
231
|
listeners.clear();
|
|
227
232
|
},
|
|
@@ -340,8 +345,7 @@ function applyMigration(
|
|
|
340
345
|
|
|
341
346
|
function getGlobalSnapshot(): PreferenceSnapshot | undefined {
|
|
342
347
|
return (globalThis as Record<string, unknown>)[snapshotGlobalKey] as
|
|
343
|
-
|
|
|
344
|
-
| undefined;
|
|
348
|
+
PreferenceSnapshot | undefined;
|
|
345
349
|
}
|
|
346
350
|
|
|
347
351
|
function setGlobalSnapshot(snapshot: PreferenceSnapshot): void {
|
|
@@ -57,21 +57,17 @@ const resolvedTone = tone ?? dataTone;
|
|
|
57
57
|
rel={resolvedRel}
|
|
58
58
|
target={target}
|
|
59
59
|
>
|
|
60
|
-
{
|
|
61
|
-
|
|
62
|
-
<
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
) : null
|
|
66
|
-
}
|
|
60
|
+
{hasLeading ? (
|
|
61
|
+
<span class="nox-button__affix">
|
|
62
|
+
<slot name="leading" />
|
|
63
|
+
</span>
|
|
64
|
+
) : null}
|
|
67
65
|
<slot />
|
|
68
|
-
{
|
|
69
|
-
|
|
70
|
-
<
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
) : null
|
|
74
|
-
}
|
|
66
|
+
{hasTrailing ? (
|
|
67
|
+
<span class="nox-button__affix">
|
|
68
|
+
<slot name="trailing" />
|
|
69
|
+
</span>
|
|
70
|
+
) : null}
|
|
75
71
|
</a>
|
|
76
72
|
) : href ? (
|
|
77
73
|
<span
|
|
@@ -86,21 +82,17 @@ const resolvedTone = tone ?? dataTone;
|
|
|
86
82
|
data-button-size={size}
|
|
87
83
|
data-tone={resolvedTone}
|
|
88
84
|
>
|
|
89
|
-
{
|
|
90
|
-
|
|
91
|
-
<
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
) : null
|
|
95
|
-
}
|
|
85
|
+
{hasLeading ? (
|
|
86
|
+
<span class="nox-button__affix">
|
|
87
|
+
<slot name="leading" />
|
|
88
|
+
</span>
|
|
89
|
+
) : null}
|
|
96
90
|
<slot />
|
|
97
|
-
{
|
|
98
|
-
|
|
99
|
-
<
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
) : null
|
|
103
|
-
}
|
|
91
|
+
{hasTrailing ? (
|
|
92
|
+
<span class="nox-button__affix">
|
|
93
|
+
<slot name="trailing" />
|
|
94
|
+
</span>
|
|
95
|
+
) : null}
|
|
104
96
|
</span>
|
|
105
97
|
) : (
|
|
106
98
|
<button
|
|
@@ -116,21 +108,17 @@ const resolvedTone = tone ?? dataTone;
|
|
|
116
108
|
disabled={disabled}
|
|
117
109
|
type={type}
|
|
118
110
|
>
|
|
119
|
-
{
|
|
120
|
-
|
|
121
|
-
<
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
) : null
|
|
125
|
-
}
|
|
111
|
+
{hasLeading ? (
|
|
112
|
+
<span class="nox-button__affix">
|
|
113
|
+
<slot name="leading" />
|
|
114
|
+
</span>
|
|
115
|
+
) : null}
|
|
126
116
|
<slot />
|
|
127
|
-
{
|
|
128
|
-
|
|
129
|
-
<
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
) : null
|
|
133
|
-
}
|
|
117
|
+
{hasTrailing ? (
|
|
118
|
+
<span class="nox-button__affix">
|
|
119
|
+
<slot name="trailing" />
|
|
120
|
+
</span>
|
|
121
|
+
) : null}
|
|
134
122
|
</button>
|
|
135
123
|
)
|
|
136
124
|
}
|
|
@@ -7,6 +7,7 @@ import Media from '../../../primitives/media/Media/Media.astro';
|
|
|
7
7
|
|
|
8
8
|
type Props = Omit<HTMLAttributes<'button'>, 'type'> &
|
|
9
9
|
Omit<HTMLAttributes<'a'>, 'href'> & {
|
|
10
|
+
collapse?: 'none' | 'icon';
|
|
10
11
|
compactLabel?: string;
|
|
11
12
|
disabled?: boolean;
|
|
12
13
|
href?: string;
|
|
@@ -24,6 +25,8 @@ type Props = Omit<HTMLAttributes<'button'>, 'type'> &
|
|
|
24
25
|
const {
|
|
25
26
|
class: className,
|
|
26
27
|
'data-tone': dataTone,
|
|
28
|
+
'aria-label': ariaLabel,
|
|
29
|
+
collapse = 'none',
|
|
27
30
|
compactLabel,
|
|
28
31
|
disabled = false,
|
|
29
32
|
href,
|
|
@@ -44,6 +47,8 @@ const resolvedRel =
|
|
|
44
47
|
rel ?? (target === '_blank' ? 'noopener noreferrer' : undefined);
|
|
45
48
|
const resolvedTone = tone ?? dataTone;
|
|
46
49
|
const hasVisual = Boolean(Icon || media);
|
|
50
|
+
const resolvedAriaLabel =
|
|
51
|
+
ariaLabel ?? (collapse === 'icon' && hasVisual ? label : undefined);
|
|
47
52
|
const isLink = Boolean(href);
|
|
48
53
|
const isDisabledLink = isLink && disabled;
|
|
49
54
|
const Root = isLink ? (isDisabledLink ? 'span' : 'a') : 'button';
|
|
@@ -51,8 +56,10 @@ const Root = isLink ? (isDisabledLink ? 'span' : 'a') : 'button';
|
|
|
51
56
|
|
|
52
57
|
<Root
|
|
53
58
|
{...attrs}
|
|
59
|
+
aria-label={resolvedAriaLabel}
|
|
54
60
|
aria-disabled={isDisabledLink ? 'true' : undefined}
|
|
55
61
|
class:list={['card-action', className]}
|
|
62
|
+
data-card-action-collapse={hasVisual ? collapse : undefined}
|
|
56
63
|
data-card-action-compact-label={compactLabel ? true : undefined}
|
|
57
64
|
data-card-action-hover-tone={hoverTone}
|
|
58
65
|
data-card-action-label-treatment={labelTreatment}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
--card-action-icon-size: var(--nox-compact-media-responsive-size);
|
|
8
8
|
--card-action-hover-seed: var(--nox-tone-seed);
|
|
9
9
|
|
|
10
|
+
position: relative;
|
|
10
11
|
display: grid;
|
|
11
12
|
min-width: 0;
|
|
12
13
|
min-height: var(--card-action-size);
|
|
@@ -59,8 +60,7 @@
|
|
|
59
60
|
color: var(--nox-tone-color);
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
.card-action[data-card-action-visual-position='end']
|
|
63
|
-
.card-action__visual {
|
|
63
|
+
.card-action[data-card-action-visual-position='end'] .card-action__visual {
|
|
64
64
|
grid-column: 2;
|
|
65
65
|
border-inline-start: var(--surface-border-width) solid
|
|
66
66
|
var(--surface-border-color);
|
|
@@ -98,8 +98,7 @@
|
|
|
98
98
|
overflow-wrap: anywhere;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
.card-action[data-card-action-label-treatment='heading']
|
|
102
|
-
.card-action__label {
|
|
101
|
+
.card-action[data-card-action-label-treatment='heading'] .card-action__label {
|
|
103
102
|
font-size: var(--nox-font-size-lg);
|
|
104
103
|
letter-spacing: var(--nox-letter-spacing-wide);
|
|
105
104
|
text-transform: uppercase;
|
|
@@ -110,8 +109,7 @@
|
|
|
110
109
|
grid-row: 1;
|
|
111
110
|
}
|
|
112
111
|
|
|
113
|
-
.card-action[data-card-action-visual-position='end']
|
|
114
|
-
.card-action__label {
|
|
112
|
+
.card-action[data-card-action-visual-position='end'] .card-action__label {
|
|
115
113
|
grid-column: 1;
|
|
116
114
|
}
|
|
117
115
|
|
|
@@ -172,13 +170,36 @@
|
|
|
172
170
|
}
|
|
173
171
|
|
|
174
172
|
@media (width < 48rem) {
|
|
175
|
-
.card-action[data-card-action-
|
|
176
|
-
|
|
173
|
+
.card-action[data-card-action-collapse='icon'][data-has-visual] {
|
|
174
|
+
grid-template-columns: minmax(0, 1fr);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.card-action[data-card-action-collapse='icon'][data-has-visual]
|
|
178
|
+
.card-action__visual {
|
|
179
|
+
grid-column: 1;
|
|
180
|
+
inline-size: 100%;
|
|
181
|
+
border-inline: 0;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.card-action[data-card-action-collapse='icon'][data-has-visual]
|
|
185
|
+
.card-action__label {
|
|
186
|
+
position: absolute;
|
|
187
|
+
overflow: hidden;
|
|
188
|
+
width: 1px;
|
|
189
|
+
min-height: 0;
|
|
190
|
+
height: 1px;
|
|
191
|
+
margin: -1px;
|
|
192
|
+
padding: 0;
|
|
193
|
+
clip: rect(0 0 0 0);
|
|
194
|
+
clip-path: inset(50%);
|
|
195
|
+
white-space: nowrap;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.card-action[data-card-action-compact-label] .card-action__label-full {
|
|
177
199
|
display: none;
|
|
178
200
|
}
|
|
179
201
|
|
|
180
|
-
.card-action[data-card-action-compact-label]
|
|
181
|
-
.card-action__label-compact {
|
|
202
|
+
.card-action[data-card-action-compact-label] .card-action__label-compact {
|
|
182
203
|
display: block;
|
|
183
204
|
}
|
|
184
205
|
|
|
@@ -3,18 +3,14 @@ import type { HTMLAttributes } from 'astro/types';
|
|
|
3
3
|
import type { Tone } from '../../../theme/contracts';
|
|
4
4
|
import Button from '../Button/Button.astro';
|
|
5
5
|
|
|
6
|
-
type Props = HTMLAttributes<'button'> & {
|
|
6
|
+
type Props = Omit<HTMLAttributes<'button'>, 'disabled'> & {
|
|
7
|
+
disabled?: boolean;
|
|
7
8
|
label: string;
|
|
8
9
|
pendingLabel?: string;
|
|
9
10
|
tone?: Tone;
|
|
10
11
|
};
|
|
11
12
|
|
|
12
|
-
const {
|
|
13
|
-
label,
|
|
14
|
-
pendingLabel = label,
|
|
15
|
-
tone,
|
|
16
|
-
...attrs
|
|
17
|
-
}: Props = Astro.props;
|
|
13
|
+
const { label, pendingLabel = label, tone, ...attrs }: Props = Astro.props;
|
|
18
14
|
---
|
|
19
15
|
|
|
20
16
|
<Button
|
|
@@ -28,7 +24,9 @@ const {
|
|
|
28
24
|
>
|
|
29
25
|
{
|
|
30
26
|
Astro.slots.has('leading') ? (
|
|
31
|
-
<span slot="leading"
|
|
27
|
+
<span slot="leading">
|
|
28
|
+
<slot name="leading" />
|
|
29
|
+
</span>
|
|
32
30
|
) : null
|
|
33
31
|
}
|
|
34
32
|
<span data-auth-provider-action-label>{label}</span>
|
|
@@ -3,7 +3,8 @@ import type { HTMLAttributes } from 'astro/types';
|
|
|
3
3
|
import Button from '../Button/Button.astro';
|
|
4
4
|
import ArrowUpIcon from '../../icons/ArrowUpIcon.astro';
|
|
5
5
|
|
|
6
|
-
type Props = Omit<HTMLAttributes<'button'>, 'type'> & {
|
|
6
|
+
type Props = Omit<HTMLAttributes<'button'>, 'disabled' | 'type'> & {
|
|
7
|
+
disabled?: boolean;
|
|
7
8
|
label: string;
|
|
8
9
|
};
|
|
9
10
|
|
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
.scroll-to-top.nox-button {
|
|
2
2
|
position: fixed;
|
|
3
3
|
z-index: 45;
|
|
4
|
-
inset-inline-end: calc(
|
|
5
|
-
|
|
6
|
-
);
|
|
7
|
-
inset-block-end: calc(
|
|
8
|
-
var(--nox-space-5) + env(safe-area-inset-bottom)
|
|
9
|
-
);
|
|
4
|
+
inset-inline-end: calc(var(--nox-space-5) + env(safe-area-inset-right));
|
|
5
|
+
inset-block-end: calc(var(--nox-space-5) + env(safe-area-inset-bottom));
|
|
10
6
|
inline-size: var(--nox-section-compact-block-size);
|
|
11
7
|
block-size: var(--nox-section-compact-block-size);
|
|
12
8
|
min-block-size: 0;
|
|
@@ -30,9 +30,9 @@ const {
|
|
|
30
30
|
data-theme-toggle-dark-label={darkLabel}
|
|
31
31
|
data-theme-toggle-light-label={lightLabel}
|
|
32
32
|
data-navigation-control={appearance === 'circle' ? '' : undefined}
|
|
33
|
-
data-navigation-control-appearance={
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
data-navigation-control-appearance={appearance === 'circle'
|
|
34
|
+
? 'bar'
|
|
35
|
+
: undefined}
|
|
36
36
|
>
|
|
37
37
|
<span
|
|
38
38
|
class="theme-toggle__icon"
|
|
@@ -11,8 +11,7 @@ function isColorTheme(value: string | null): value is ColorTheme {
|
|
|
11
11
|
|
|
12
12
|
function getStorageKey(): string {
|
|
13
13
|
return (
|
|
14
|
-
document.documentElement.dataset.themeStorageKey ??
|
|
15
|
-
defaultThemeStorageKey
|
|
14
|
+
document.documentElement.dataset.themeStorageKey ?? defaultThemeStorageKey
|
|
16
15
|
);
|
|
17
16
|
}
|
|
18
17
|
|
|
@@ -31,7 +30,7 @@ function getSystemTheme(): ColorTheme {
|
|
|
31
30
|
|
|
32
31
|
function getCurrentTheme(): ColorTheme {
|
|
33
32
|
const value = document.documentElement.dataset.theme ?? null;
|
|
34
|
-
return isColorTheme(value) ? value : readStoredTheme() ?? getSystemTheme();
|
|
33
|
+
return isColorTheme(value) ? value : (readStoredTheme() ?? getSystemTheme());
|
|
35
34
|
}
|
|
36
35
|
|
|
37
36
|
function updateToggle(toggle: HTMLElement, theme: ColorTheme): void {
|
|
@@ -55,7 +54,11 @@ function updateToggles(theme: ColorTheme): void {
|
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
function applyTheme(theme: ColorTheme, persist: boolean): void {
|
|
58
|
-
document.documentElement
|
|
57
|
+
const root = document.documentElement;
|
|
58
|
+
root.dataset.themeSwitching = 'true';
|
|
59
|
+
root.dataset.theme = theme;
|
|
60
|
+
void root.offsetWidth;
|
|
61
|
+
delete root.dataset.themeSwitching;
|
|
59
62
|
|
|
60
63
|
if (persist) {
|
|
61
64
|
try {
|
|
@@ -42,16 +42,14 @@
|
|
|
42
42
|
var(--card-footer-size);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
.card-footer[data-has-leading-action][data-has-action]
|
|
46
|
-
.card-footer__layout {
|
|
45
|
+
.card-footer[data-has-leading-action][data-has-action] .card-footer__layout {
|
|
47
46
|
grid-template-columns:
|
|
48
47
|
var(--card-footer-size)
|
|
49
48
|
minmax(0, 1fr)
|
|
50
49
|
var(--card-footer-size);
|
|
51
50
|
}
|
|
52
51
|
|
|
53
|
-
.card-footer[data-has-leading-action][data-has-media]
|
|
54
|
-
.card-footer__layout {
|
|
52
|
+
.card-footer[data-has-leading-action][data-has-media] .card-footer__layout {
|
|
55
53
|
grid-template-columns:
|
|
56
54
|
var(--card-footer-size)
|
|
57
55
|
var(--card-footer-size)
|
|
@@ -2,10 +2,7 @@
|
|
|
2
2
|
import type { HTMLAttributes } from 'astro/types';
|
|
3
3
|
import type { Tone } from '../../../theme/contracts';
|
|
4
4
|
import type { DisplayMedia, TextWeight } from '../../../display/contracts';
|
|
5
|
-
import type {
|
|
6
|
-
AstroComponent,
|
|
7
|
-
HeadingLevel,
|
|
8
|
-
} from '../../widgetTypes';
|
|
5
|
+
import type { AstroComponent, HeadingLevel } from '../../widgetTypes';
|
|
9
6
|
import Inline from '../../../composition/Inline/Inline.astro';
|
|
10
7
|
import Media from '../../../primitives/media/Media/Media.astro';
|
|
11
8
|
|
|
@@ -90,11 +90,7 @@ const resolvedTone = tone ?? dataTone;
|
|
|
90
90
|
{Icon ? (
|
|
91
91
|
<Icon class="card-link__icon" data-tone={iconTone} />
|
|
92
92
|
) : media ? (
|
|
93
|
-
<Media
|
|
94
|
-
media={media}
|
|
95
|
-
fit="contain"
|
|
96
|
-
size="intrinsic"
|
|
97
|
-
/>
|
|
93
|
+
<Media media={media} fit="contain" size="intrinsic" />
|
|
98
94
|
) : null}
|
|
99
95
|
</span>
|
|
100
96
|
) : null
|
|
@@ -104,11 +100,7 @@ const resolvedTone = tone ?? dataTone;
|
|
|
104
100
|
{
|
|
105
101
|
hasContentMedia && media ? (
|
|
106
102
|
<span class="card-link__content-media" aria-hidden="true">
|
|
107
|
-
<Media
|
|
108
|
-
media={media}
|
|
109
|
-
fit="contain"
|
|
110
|
-
size="intrinsic"
|
|
111
|
-
/>
|
|
103
|
+
<Media media={media} fit="contain" size="intrinsic" />
|
|
112
104
|
</span>
|
|
113
105
|
) : null
|
|
114
106
|
}
|
|
@@ -134,11 +126,7 @@ const resolvedTone = tone ?? dataTone;
|
|
|
134
126
|
{Icon ? (
|
|
135
127
|
<Icon class="card-link__icon" data-tone={iconTone} />
|
|
136
128
|
) : media ? (
|
|
137
|
-
<Media
|
|
138
|
-
media={media}
|
|
139
|
-
fit="contain"
|
|
140
|
-
size="intrinsic"
|
|
141
|
-
/>
|
|
129
|
+
<Media media={media} fit="contain" size="intrinsic" />
|
|
142
130
|
) : null}
|
|
143
131
|
</span>
|
|
144
132
|
) : null
|