@astronox/core 0.2.2 → 0.2.4

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 (59) hide show
  1. package/package.json +1 -1
  2. package/src/admin/cache.ts +217 -0
  3. package/src/admin/index.ts +1 -0
  4. package/src/auth/firebase/client.ts +37 -7
  5. package/src/client/formSubmission.ts +177 -0
  6. package/src/client/index.ts +1 -0
  7. package/src/components/actions/Button/Button.css +1 -1
  8. package/src/components/actions/CardAction/CardAction.css +3 -3
  9. package/src/components/actions/ScrollToTop/ScrollToTop.css +11 -0
  10. package/src/components/actions/ThemeToggle/ThemeToggle.astro +59 -0
  11. package/src/components/actions/ThemeToggle/ThemeToggle.css +83 -0
  12. package/src/components/actions/ThemeToggle/themeToggleController.ts +111 -0
  13. package/src/components/content/CardBody/CardBody.css +6 -2
  14. package/src/components/content/CardFooter/CardFooter.astro +9 -0
  15. package/src/components/content/CardFooter/CardFooter.css +45 -3
  16. package/src/components/content/CardLink/CardLink.astro +12 -3
  17. package/src/components/content/CardLink/CardLink.css +32 -0
  18. package/src/components/content/MarkdownContent/MarkdownContent.css +14 -4
  19. package/src/components/content/MarkdownContent/MarkdownInline.astro +1 -1
  20. package/src/components/content/MarkdownContent/contracts.ts +1 -1
  21. package/src/components/content/MarkdownContent/markdownContentRenderer.ts +7 -4
  22. package/src/components/data-display/TextPair/TextPair.css +8 -2
  23. package/src/components/feedback/ThemeBootstrap/ThemeBootstrap.astro +31 -0
  24. package/src/components/forms/ChoiceGroup/ChoiceGroup.css +1 -1
  25. package/src/components/forms/PreparedFormBootstrap/PreparedFormBootstrap.astro +203 -0
  26. package/src/components/forms/PreparedFormBootstrap/contracts.ts +15 -0
  27. package/src/components/forms/Textarea/Textarea.astro +3 -0
  28. package/src/components/forms/Textarea/Textarea.css +8 -0
  29. package/src/components/icons/CircleSymbolIcon.css +3 -3
  30. package/src/components/index.ts +10 -0
  31. package/src/components/navigation/Breadcrumbs/Breadcrumbs.astro +5 -1
  32. package/src/components/navigation/Breadcrumbs/Breadcrumbs.css +12 -17
  33. package/src/components/navigation/DesktopNavigation.css +4 -1
  34. package/src/components/navigation/Footer/Footer.astro +102 -0
  35. package/src/components/navigation/Footer/Footer.css +155 -0
  36. package/src/components/navigation/Footer/contracts.ts +17 -0
  37. package/src/components/navigation/Header.astro +20 -2
  38. package/src/components/navigation/MobileNavigation.astro +107 -43
  39. package/src/components/navigation/MobileNavigation.css +109 -24
  40. package/src/components/navigation/NavigationBrand.css +1 -1
  41. package/src/components/navigation/NavigationControl.css +16 -2
  42. package/src/components/navigation/NavigationItemContent.astro +6 -1
  43. package/src/components/navigation/mobileNavigationController.ts +0 -16
  44. package/src/components/navigation/navigationTypes.ts +9 -1
  45. package/src/composition/Grid/Grid.css +16 -0
  46. package/src/data/firestore/client/contracts.ts +23 -0
  47. package/src/data/firestore/client/firebase.ts +45 -1
  48. package/src/display/contracts.ts +5 -1
  49. package/src/layouts/BaseLayout.astro +13 -1
  50. package/src/patterns/Combobox/comboboxController.ts +69 -0
  51. package/src/patterns/MapViewport/MapViewport.css +3 -3
  52. package/src/patterns/Table/TableBody.css +3 -3
  53. package/src/patterns/Table/TableCell.css +5 -1
  54. package/src/styles/base.css +24 -7
  55. package/src/styles/style.css +2 -0
  56. package/src/styles/tokens.css +60 -15
  57. package/src/theme/contracts.ts +2 -0
  58. package/src/theme/index.ts +1 -0
  59. package/src/theme/storage.ts +1 -0
@@ -0,0 +1,17 @@
1
+ import type { HTMLAttributes } from 'astro/types';
2
+ import type { Tone } from '../../../theme/contracts';
3
+
4
+ export type FooterItem = {
5
+ href?: HTMLAttributes<'a'>['href'];
6
+ label: string;
7
+ marker?: 'arrow';
8
+ prefix?: string;
9
+ rel?: HTMLAttributes<'a'>['rel'];
10
+ separator?: 'divider' | 'none';
11
+ target?: HTMLAttributes<'a'>['target'];
12
+ tone?: Tone;
13
+ };
14
+
15
+ export type FooterGroup = {
16
+ items: readonly FooterItem[];
17
+ };
@@ -1,8 +1,14 @@
1
1
  ---
2
2
  import type { HTMLAttributes } from 'astro/types';
3
3
  import type { SiteIdentity } from '../../config/siteConfig';
4
- import type { MobileNavigationLabels } from './navigationTypes';
5
- import type { NavigationItem } from '../../display/contracts';
4
+ import type {
5
+ MobileNavigationControlItem,
6
+ MobileNavigationLabels,
7
+ } from './navigationTypes';
8
+ import type {
9
+ NavigationItem,
10
+ NavigationLinkItem,
11
+ } from '../../display/contracts';
6
12
  import type { Tone } from '../../theme/contracts';
7
13
  import DesktopNavigation from './DesktopNavigation.astro';
8
14
  import MobileNavigation from './MobileNavigation.astro';
@@ -15,6 +21,9 @@ type Props = HTMLAttributes<'header'> & {
15
21
  identity: SiteIdentity;
16
22
  leftItems?: readonly NavigationItem[];
17
23
  menu: MobileNavigationLabels;
24
+ mobileFooterItems?: readonly MobileNavigationControlItem[];
25
+ mobileItems?: readonly NavigationLinkItem[];
26
+ mobileUtilityItems?: readonly MobileNavigationControlItem[];
18
27
  rightItems?: readonly NavigationItem[];
19
28
  sticky?: boolean;
20
29
  tone?: Tone;
@@ -30,12 +39,16 @@ const {
30
39
  identity,
31
40
  leftItems = [],
32
41
  menu,
42
+ mobileFooterItems = [],
43
+ mobileItems,
44
+ mobileUtilityItems = [],
33
45
  rightItems = [],
34
46
  sticky = false,
35
47
  tone,
36
48
  ...attrs
37
49
  } = Astro.props;
38
50
  const resolvedTone = tone ?? dataTone;
51
+ const hasMobileUtilityActions = Astro.slots.has('mobile-utility-actions');
39
52
  ---
40
53
 
41
54
  <header
@@ -52,10 +65,15 @@ const resolvedTone = tone ?? dataTone;
52
65
  homeLabel={homeLabel}
53
66
  leftItems={leftItems}
54
67
  menu={menu}
68
+ mobileFooterItems={mobileFooterItems}
69
+ mobileItems={mobileItems}
70
+ mobileUtilityItems={mobileUtilityItems}
71
+ hasUtilityActions={hasMobileUtilityActions}
55
72
  rightItems={rightItems}
56
73
  currentPath={currentPath}
57
74
  >
58
75
  <slot name="mobile-actions" slot="actions" />
76
+ <slot name="mobile-utility-actions" slot="utility-actions" />
59
77
  </MobileNavigation>
60
78
 
61
79
  <DesktopNavigation
@@ -1,52 +1,56 @@
1
1
  ---
2
2
  import type { SiteIdentity } from '../../config/siteConfig';
3
- import type { NavigationItem } from '../../display/contracts';
4
- import type { MobileNavigationLabels } from './navigationTypes';
3
+ import type {
4
+ NavigationItem,
5
+ NavigationLinkItem,
6
+ } from '../../display/contracts';
7
+ import type {
8
+ MobileNavigationControlItem,
9
+ MobileNavigationLabels,
10
+ } from './navigationTypes';
11
+ import CardLink from '../content/CardLink/CardLink.astro';
5
12
  import NavigationBrand from './NavigationBrand.astro';
6
13
  import NavigationLink from './NavigationLink.astro';
7
- import { isNavigationGroupItem } from './navigationState';
14
+ import {
15
+ isNavigationGroupItem,
16
+ isNavigationItemActive,
17
+ } from './navigationState';
8
18
 
9
19
  type Props = {
10
20
  currentPath: string;
21
+ hasUtilityActions?: boolean;
11
22
  homeHref: string;
12
23
  homeLabel: string;
13
24
  identity: SiteIdentity;
14
25
  leftItems: readonly NavigationItem[];
15
26
  menu: MobileNavigationLabels;
27
+ mobileFooterItems: readonly MobileNavigationControlItem[];
28
+ mobileItems?: readonly NavigationLinkItem[];
29
+ mobileUtilityItems: readonly MobileNavigationControlItem[];
16
30
  rightItems: readonly NavigationItem[];
17
31
  };
18
32
 
19
- type MobileNavigationGroup = {
20
- items: readonly NavigationItem[];
21
- label?: string;
22
- };
23
-
24
33
  const {
25
34
  currentPath,
35
+ hasUtilityActions,
26
36
  homeHref,
27
37
  homeLabel,
28
38
  identity,
29
39
  leftItems,
30
40
  menu,
41
+ mobileFooterItems,
42
+ mobileItems,
43
+ mobileUtilityItems,
31
44
  rightItems,
32
45
  } = Astro.props;
33
46
  const panelId = 'site-nav-mobile-panel';
34
47
  const closeLabel = menu.closeLabel ?? menu.openLabel;
35
- const mobileGroups: MobileNavigationGroup[] = [
36
- {
37
- items: leftItems,
38
- },
39
- ...rightItems.map((item) =>
40
- isNavigationGroupItem(item)
41
- ? {
42
- label: item.label,
43
- items: item.items,
44
- }
45
- : {
46
- items: [item],
47
- },
48
- ),
49
- ];
48
+ const fallbackItems = [...leftItems, ...rightItems].flatMap((item) =>
49
+ isNavigationGroupItem(item) ? [...item.items] : [item],
50
+ );
51
+ const resolvedMobileItems = mobileItems ?? fallbackItems;
52
+ const resolvedHasUtilityActions =
53
+ hasUtilityActions ?? Astro.slots.has('utility-actions');
50
54
  ---
51
55
 
52
56
  <div class="site-nav__mobile" data-mobile-navigation>
@@ -88,28 +92,88 @@ const mobileGroups: MobileNavigationGroup[] = [
88
92
  >
89
93
  <ul class="site-nav__mobile-list">
90
94
  {
91
- mobileGroups.map((group) => (
92
- <>
93
- {group.label ? (
94
- <li class="site-nav__mobile-group-label">
95
- <span>{group.label}</span>
96
- </li>
97
- ) : null}
98
- {group.items.map((item) => (
99
- <li>
100
- <NavigationLink
101
- item={item}
102
- currentPath={currentPath}
103
- accent="start"
104
- appearance="menu"
105
- class="site-nav__mobile-link"
106
- />
107
- </li>
108
- ))}
109
- </>
110
- ))
95
+ resolvedMobileItems.map((item) => {
96
+ const active = isNavigationItemActive(item, currentPath);
97
+ const rel =
98
+ item.rel ??
99
+ (item.target === '_blank' ? 'noopener noreferrer' : undefined);
100
+
101
+ return (
102
+ <li>
103
+ <CardLink
104
+ class="site-nav__mobile-link"
105
+ disabled={item.disabled}
106
+ href={item.href}
107
+ media={item.media}
108
+ mediaSize="compact"
109
+ rel={rel}
110
+ target={item.target}
111
+ textTreatment="heading"
112
+ title={item.label}
113
+ aria-current={active ? 'page' : undefined}
114
+ />
115
+ </li>
116
+ );
117
+ })
111
118
  }
112
119
  </ul>
120
+
121
+ {
122
+ mobileUtilityItems.length > 0 ||
123
+ resolvedHasUtilityActions ||
124
+ mobileFooterItems.length > 0 ? (
125
+ <div class="site-nav__mobile-controls">
126
+ {mobileUtilityItems.length > 0 || resolvedHasUtilityActions ? (
127
+ <ul class="site-nav__mobile-utility">
128
+ {mobileUtilityItems.map((item) => (
129
+ <li>
130
+ <NavigationLink
131
+ item={item}
132
+ currentPath={currentPath}
133
+ appearance="menu"
134
+ class="site-nav__mobile-utility-link"
135
+ data-mobile-navigation-label-visibility={
136
+ item.labelVisibility
137
+ }
138
+ />
139
+ </li>
140
+ ))}
141
+ {resolvedHasUtilityActions ? (
142
+ <li>
143
+ <slot name="utility-actions" />
144
+ </li>
145
+ ) : null}
146
+ </ul>
147
+ ) : null}
148
+
149
+ {mobileFooterItems.length > 0 ? (
150
+ <ul class="site-nav__mobile-footer">
151
+ {mobileFooterItems.map((item) => (
152
+ <li>
153
+ <NavigationLink
154
+ item={item}
155
+ currentPath={currentPath}
156
+ appearance="menu"
157
+ class="site-nav__mobile-footer-link"
158
+ data-mobile-navigation-accent={
159
+ item.accentColor ? true : undefined
160
+ }
161
+ data-mobile-navigation-label-visibility={
162
+ item.labelVisibility
163
+ }
164
+ style={
165
+ item.accentColor
166
+ ? `--mobile-navigation-accent: ${item.accentColor};`
167
+ : undefined
168
+ }
169
+ />
170
+ </li>
171
+ ))}
172
+ </ul>
173
+ ) : null}
174
+ </div>
175
+ ) : null
176
+ }
113
177
  </div>
114
178
  </div>
115
179
 
@@ -4,14 +4,18 @@
4
4
  }
5
5
 
6
6
  .site-nav__mobile-bar {
7
+ --site-nav-mobile-side-size: 4.75rem;
8
+
7
9
  position: relative;
8
10
  display: grid;
9
- grid-template-columns: auto minmax(0, 1fr) auto;
11
+ grid-template-columns:
12
+ var(--site-nav-mobile-side-size)
13
+ minmax(0, 1fr)
14
+ var(--site-nav-mobile-side-size);
10
15
  align-items: center;
11
- width: var(--nox-page-width);
16
+ width: 100%;
12
17
  min-height: var(--nox-site-header-compact-height);
13
- margin: 0 auto;
14
- gap: clamp(var(--nox-space-1), 2vw, var(--nox-space-3));
18
+ gap: 0;
15
19
  }
16
20
 
17
21
  .site-nav__menu-button {
@@ -29,6 +33,7 @@
29
33
  background: var(--nox-surface);
30
34
  color: var(--nox-text);
31
35
  cursor: pointer;
36
+ justify-self: center;
32
37
  }
33
38
 
34
39
  .site-nav__menu-icon {
@@ -72,12 +77,16 @@
72
77
 
73
78
  .site-nav__brand--mobile {
74
79
  z-index: 1;
75
- justify-self: stretch;
80
+ padding-inline: var(--nox-page-gutter);
81
+ justify-self: center;
76
82
  }
77
83
 
78
84
  .site-nav__mobile-action {
79
85
  z-index: 2;
80
- justify-self: end;
86
+ display: flex;
87
+ width: 100%;
88
+ justify-content: center;
89
+ justify-self: center;
81
90
  }
82
91
 
83
92
  .site-nav__mobile-action > * {
@@ -101,13 +110,15 @@
101
110
  .site-nav__mobile-panel {
102
111
  position: absolute;
103
112
  z-index: 2;
113
+ display: grid;
104
114
  inset-inline: 0;
105
115
  top: 100%;
106
116
  width: 100%;
107
- max-height: calc(100vh - var(--nox-site-header-compact-height));
108
- max-height: calc(100dvh - var(--nox-site-header-compact-height));
117
+ height: calc(100vh - var(--nox-site-header-compact-height));
118
+ height: calc(100dvh - var(--nox-site-header-compact-height));
119
+ grid-template-rows: minmax(0, 1fr) auto;
109
120
  overflow-x: hidden;
110
- overflow-y: auto;
121
+ overflow-y: hidden;
111
122
  padding: var(--nox-space-3);
112
123
  border-top: 3px solid var(--nox-link-active);
113
124
  border-bottom: var(--nox-border-width) solid var(--nox-border);
@@ -125,30 +136,90 @@
125
136
  .site-nav__mobile-list {
126
137
  display: grid;
127
138
  width: 100%;
139
+ min-height: 0;
128
140
  margin: 0;
129
141
  padding: 0;
130
- gap: var(--nox-space-1);
142
+ align-content: start;
143
+ gap: var(--nox-space-3);
131
144
  list-style: none;
145
+ overflow-x: hidden;
146
+ overflow-y: auto;
132
147
  }
133
148
 
134
149
  .site-nav__mobile-link {
135
- min-height: 3.125rem;
136
- padding-block: 0;
137
- border-radius: var(--nox-radius-sm);
150
+ --surface-radius: var(--nox-radius-md);
138
151
  }
139
152
 
140
- .site-nav__mobile-group-label {
141
- display: flex;
142
- min-height: 1.5rem;
143
- align-items: flex-end;
153
+ .site-nav__mobile-link[aria-current='page'] .text-pair__primary {
154
+ position: relative;
155
+ }
156
+
157
+ .site-nav__mobile-link[aria-current='page'] .text-pair__primary::after {
158
+ position: absolute;
159
+ right: 0;
160
+ bottom: calc(0px - var(--nox-space-2));
161
+ left: 0;
162
+ height: 2px;
163
+ border-radius: var(--nox-radius-pill);
164
+ background: var(--nox-source-tone-success);
165
+ content: '';
166
+ }
167
+
168
+ .site-nav__mobile-controls {
169
+ display: grid;
144
170
  margin-top: var(--nox-space-2);
145
- padding: var(--nox-space-2) var(--nox-space-4) 0;
146
- color: var(--nox-text-subtle);
147
- font-size: var(--nox-font-size-xs);
148
- font-weight: var(--nox-font-weight-strong);
149
- letter-spacing: var(--nox-letter-spacing-wider);
150
- line-height: 1;
151
- text-transform: uppercase;
171
+ padding: var(--nox-space-2) 0 0;
172
+ gap: var(--nox-space-3);
173
+ }
174
+
175
+ .site-nav__mobile-utility,
176
+ .site-nav__mobile-footer {
177
+ display: grid;
178
+ margin: 0;
179
+ padding: 0;
180
+ list-style: none;
181
+ }
182
+
183
+ .site-nav__mobile-utility {
184
+ grid-template-columns: repeat(2, minmax(0, 1fr));
185
+ gap: var(--nox-space-3);
186
+ }
187
+
188
+ .site-nav__mobile-footer {
189
+ grid-template-columns: repeat(3, minmax(0, 1fr));
190
+ gap: var(--nox-space-3);
191
+ }
192
+
193
+ .site-nav__mobile-utility-link,
194
+ .site-nav__mobile-footer-link {
195
+ min-height: var(--nox-navigation-block-size);
196
+ justify-content: center;
197
+ padding: 0;
198
+ border: var(--nox-border-width) solid var(--nox-border);
199
+ border-radius: var(--nox-radius-md);
200
+ background: var(--nox-surface-muted);
201
+ }
202
+
203
+ .site-nav__mobile-footer-link[data-mobile-navigation-accent] {
204
+ border-color: var(--mobile-navigation-accent);
205
+ background: var(--mobile-navigation-accent);
206
+ color: #fff;
207
+ }
208
+
209
+ [data-mobile-navigation-label-visibility='accessible']
210
+ [data-navigation-control-label] {
211
+ position: absolute;
212
+ overflow: hidden;
213
+ width: 1px;
214
+ height: 1px;
215
+ clip: rect(0 0 0 0);
216
+ white-space: nowrap;
217
+ }
218
+
219
+ .site-nav__mobile-footer-link [data-navigation-control-media] {
220
+ inline-size: 1.5rem;
221
+ block-size: 1.5rem;
222
+ border-radius: 0;
152
223
  }
153
224
 
154
225
  html[data-mobile-navigation-open],
@@ -166,6 +237,20 @@ html[data-mobile-navigation-open] body {
166
237
  .site-nav__menu-button:hover {
167
238
  background: var(--nox-surface-muted);
168
239
  }
240
+
241
+ .site-nav__mobile-footer-link[data-mobile-navigation-accent]:hover {
242
+ border-color: color-mix(
243
+ in srgb,
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
+ );
252
+ color: #fff;
253
+ }
169
254
  }
170
255
 
171
256
  @media (prefers-reduced-motion: no-preference) {
@@ -33,7 +33,7 @@
33
33
  .site-nav__brand--mobile {
34
34
  --site-nav-brand-block: var(--nox-site-header-compact-height);
35
35
  --site-nav-brand-inline: 100%;
36
- --site-nav-brand-image-scale: 56%;
36
+ --site-nav-brand-image-scale: 64%;
37
37
  }
38
38
 
39
39
  .site-nav__brand-name {
@@ -39,6 +39,20 @@
39
39
  block-size: 100%;
40
40
  }
41
41
 
42
+ [data-navigation-control-icon] {
43
+ display: grid;
44
+ inline-size: 1.5rem;
45
+ block-size: 1.5rem;
46
+ flex: 0 0 auto;
47
+ place-items: center;
48
+ }
49
+
50
+ [data-navigation-control-icon] > svg {
51
+ display: block;
52
+ inline-size: 100%;
53
+ block-size: 100%;
54
+ }
55
+
42
56
  [data-navigation-control-media-frame='bordered'] {
43
57
  border: var(--nox-border-width) solid var(--nox-border);
44
58
  }
@@ -92,11 +106,11 @@
92
106
  [data-navigation-control][data-navigation-control-appearance='bar']::after {
93
107
  position: absolute;
94
108
  right: var(--nox-space-4);
95
- bottom: var(--nox-space-2);
109
+ bottom: var(--nox-space-4);
96
110
  left: var(--nox-space-4);
97
111
  height: 3px;
98
112
  border-radius: var(--nox-radius-pill);
99
- background: var(--nox-link-active);
113
+ background: var(--nox-source-tone-success);
100
114
  content: '';
101
115
  opacity: 0;
102
116
  transform: translateY(var(--nox-space-1));
@@ -7,10 +7,15 @@ type Props = {
7
7
  };
8
8
 
9
9
  const { item } = Astro.props;
10
+ const Icon = item.icon;
10
11
  ---
11
12
 
12
13
  {
13
- item.media ? (
14
+ Icon ? (
15
+ <span data-navigation-control-icon aria-hidden="true">
16
+ <Icon />
17
+ </span>
18
+ ) : item.media ? (
14
19
  <span
15
20
  data-navigation-control-media
16
21
  data-navigation-control-media-frame={item.mediaFrame}
@@ -82,20 +82,6 @@ export function initMobileNavigation(root: HTMLElement): () => void {
82
82
  }
83
83
  };
84
84
 
85
- const handlePanelClick = (event: MouseEvent): void => {
86
- const target = event.target;
87
-
88
- if (!(target instanceof Element)) {
89
- return;
90
- }
91
-
92
- const link = target.closest('a[href]');
93
-
94
- if (link && panel.contains(link)) {
95
- close(true);
96
- }
97
- };
98
-
99
85
  const handleDesktopMediaChange = (): void => {
100
86
  if (desktopMedia.matches) {
101
87
  close(false);
@@ -105,7 +91,6 @@ export function initMobileNavigation(root: HTMLElement): () => void {
105
91
  setOpen(isOpen);
106
92
 
107
93
  trigger.addEventListener('click', handleTriggerClick);
108
- panel.addEventListener('click', handlePanelClick);
109
94
  document.addEventListener('click', handleDocumentClick);
110
95
  document.addEventListener('keydown', handleDocumentKeydown);
111
96
  desktopMedia.addEventListener('change', handleDesktopMediaChange);
@@ -113,7 +98,6 @@ export function initMobileNavigation(root: HTMLElement): () => void {
113
98
  return () => {
114
99
  setOpen(false);
115
100
  trigger.removeEventListener('click', handleTriggerClick);
116
- panel.removeEventListener('click', handlePanelClick);
117
101
  document.removeEventListener('click', handleDocumentClick);
118
102
  document.removeEventListener('keydown', handleDocumentKeydown);
119
103
  desktopMedia.removeEventListener('change', handleDesktopMediaChange);
@@ -1,11 +1,19 @@
1
1
  import type { AstroComponent } from '../widgetTypes';
2
- import type { DisplayMedia } from '../../display/contracts';
2
+ import type {
3
+ DisplayMedia,
4
+ NavigationLinkItem,
5
+ } from '../../display/contracts';
3
6
 
4
7
  export type MobileNavigationLabels = {
5
8
  closeLabel?: string;
6
9
  openLabel: string;
7
10
  };
8
11
 
12
+ export type MobileNavigationControlItem = NavigationLinkItem & {
13
+ accentColor?: string;
14
+ labelVisibility?: 'accessible' | 'visible';
15
+ };
16
+
9
17
  type SubNavigationVisual =
10
18
  | { icon?: AstroComponent; media?: never }
11
19
  | { icon?: never; media: DisplayMedia };
@@ -115,6 +115,10 @@
115
115
  grid-template-columns: 1fr;
116
116
  }
117
117
 
118
+ .grid[data-layout='fixed'][data-collapse-at='sm'][data-last-row='fill'] > * {
119
+ grid-column: auto;
120
+ }
121
+
118
122
  .grid:is(
119
123
  [data-layout='fixed'],
120
124
  [data-layout='content-sidebar']
@@ -163,6 +167,10 @@
163
167
  grid-template-columns: 1fr;
164
168
  }
165
169
 
170
+ .grid[data-layout='fixed'][data-collapse-at='md'][data-last-row='fill'] > * {
171
+ grid-column: auto;
172
+ }
173
+
166
174
  .grid:is(
167
175
  [data-layout='fixed'],
168
176
  [data-layout='content-sidebar']
@@ -211,6 +219,10 @@
211
219
  grid-template-columns: 1fr;
212
220
  }
213
221
 
222
+ .grid[data-layout='fixed'][data-collapse-at='lg'][data-last-row='fill'] > * {
223
+ grid-column: auto;
224
+ }
225
+
214
226
  .grid:is(
215
227
  [data-layout='fixed'],
216
228
  [data-layout='content-sidebar']
@@ -259,6 +271,10 @@
259
271
  grid-template-columns: 1fr;
260
272
  }
261
273
 
274
+ .grid[data-layout='fixed'][data-collapse-at='xl'][data-last-row='fill'] > * {
275
+ grid-column: auto;
276
+ }
277
+
262
278
  .grid:is(
263
279
  [data-layout='fixed'],
264
280
  [data-layout='content-sidebar']
@@ -53,3 +53,26 @@ export type ClientFirestoreRepositoryConfig<
53
53
  orderBy?: readonly ClientFirestoreOrder[];
54
54
  schema: ClientFirestoreSchema<TDocument>;
55
55
  };
56
+
57
+ export type ClientFirestoreBatchOperation =
58
+ | {
59
+ collection: string;
60
+ id?: string;
61
+ kind: 'create';
62
+ value: Record<string, unknown>;
63
+ }
64
+ | {
65
+ collection: string;
66
+ id: string;
67
+ kind: 'update';
68
+ value: Record<string, unknown>;
69
+ }
70
+ | {
71
+ collection: string;
72
+ id: string;
73
+ kind: 'delete';
74
+ };
75
+
76
+ export type ClientFirestoreBatchResult = {
77
+ createdIds: readonly string[];
78
+ };