@astronox/core 0.2.1 → 0.2.3

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 (88) hide show
  1. package/package.json +7 -1
  2. package/src/admin/cache.ts +217 -0
  3. package/src/admin/contracts.ts +28 -0
  4. package/src/admin/index.ts +3 -0
  5. package/src/admin/module.ts +28 -0
  6. package/src/auth/contracts.ts +53 -0
  7. package/src/auth/firebase/client.ts +83 -0
  8. package/src/auth/firebase/contracts.ts +8 -0
  9. package/src/auth/firebase/index.ts +1 -0
  10. package/src/auth/index.ts +2 -0
  11. package/src/auth/module.ts +99 -0
  12. package/src/client/formSubmission.ts +177 -0
  13. package/src/client/index.ts +4 -0
  14. package/src/client/preferences.ts +400 -0
  15. package/src/components/actions/Button/Button.astro +42 -6
  16. package/src/components/actions/Button/Button.css +19 -0
  17. package/src/components/actions/IdentityProviderAction/IdentityProviderAction.astro +35 -0
  18. package/src/components/actions/ScrollToTop/ScrollToTop.css +11 -0
  19. package/src/components/content/CardBody/CardBody.astro +3 -0
  20. package/src/components/content/CardBody/CardBody.css +16 -2
  21. package/src/components/content/CardFooter/CardFooter.astro +9 -0
  22. package/src/components/content/CardFooter/CardFooter.css +45 -3
  23. package/src/components/content/CardLink/CardLink.astro +12 -3
  24. package/src/components/content/CardLink/CardLink.css +32 -0
  25. package/src/components/content/MarkdownContent/MarkdownContent.css +14 -4
  26. package/src/components/content/MarkdownContent/MarkdownInline.astro +1 -1
  27. package/src/components/content/MarkdownContent/contracts.ts +1 -1
  28. package/src/components/content/MarkdownContent/markdownContentRenderer.ts +552 -0
  29. package/src/components/data-display/TextPair/TextPair.css +8 -2
  30. package/src/components/feedback/PreferenceBootstrap/PreferenceBootstrap.astro +403 -0
  31. package/src/components/forms/Checkbox/Checkbox.astro +6 -0
  32. package/src/components/forms/ChoiceGroup/ChoiceGroup.astro +57 -0
  33. package/src/components/forms/ChoiceGroup/ChoiceGroup.css +108 -0
  34. package/src/components/forms/ChoiceGroup/contracts.ts +9 -0
  35. package/src/components/forms/DateTimeInput/DateTimeInput.astro +14 -0
  36. package/src/components/forms/PreparedFormBootstrap/PreparedFormBootstrap.astro +203 -0
  37. package/src/components/forms/PreparedFormBootstrap/contracts.ts +15 -0
  38. package/src/components/forms/TextInput/TextInput.astro +7 -1
  39. package/src/components/forms/Textarea/Textarea.astro +35 -0
  40. package/src/components/forms/Textarea/Textarea.css +62 -0
  41. package/src/components/icons/BackIcon.astro +26 -0
  42. package/src/components/icons/DeleteIcon.astro +22 -0
  43. package/src/components/icons/StatusIndicatorIcon.astro +16 -0
  44. package/src/components/index.ts +18 -0
  45. package/src/components/media/CanvasFrame/CanvasFrame.astro +1 -17
  46. package/src/components/media/EmbedFrame/EmbedFrame.astro +1 -19
  47. package/src/components/media/EmbedFrame/EmbedFrame.css +0 -2
  48. package/src/components/navigation/Breadcrumbs/Breadcrumbs.astro +5 -1
  49. package/src/components/navigation/Breadcrumbs/Breadcrumbs.css +12 -17
  50. package/src/components/navigation/Footer/Footer.astro +102 -0
  51. package/src/components/navigation/Footer/Footer.css +155 -0
  52. package/src/components/navigation/Footer/contracts.ts +17 -0
  53. package/src/components/navigation/SubNavigation/SubNavigation.css +0 -5
  54. package/src/composition/Grid/Grid.css +16 -0
  55. package/src/composition/SurfaceGroup/SurfaceGroup.css +21 -7
  56. package/src/composition/SurfaceGroup/stickySurfaceGroupController.ts +3 -0
  57. package/src/data/firestore/client/contracts.ts +78 -0
  58. package/src/data/firestore/client/firebase.ts +56 -0
  59. package/src/data/firestore/client/index.ts +3 -0
  60. package/src/data/firestore/client/repository.ts +111 -0
  61. package/src/display/contracts.ts +44 -1
  62. package/src/display/index.ts +1 -0
  63. package/src/display/tableProgressive.ts +62 -0
  64. package/src/layouts/BaseLayout.astro +8 -1
  65. package/src/patterns/Combobox/Combobox.astro +23 -0
  66. package/src/patterns/Combobox/comboboxController.ts +69 -0
  67. package/src/patterns/Fullscreen/FullscreenControl.astro +4 -14
  68. package/src/patterns/Fullscreen/FullscreenControl.css +0 -26
  69. package/src/patterns/Modal/Modal.astro +1 -1
  70. package/src/patterns/Modal/Modal.css +38 -1
  71. package/src/patterns/Table/Table.astro +122 -7
  72. package/src/patterns/Table/TableBody.astro +8 -0
  73. package/src/patterns/Table/TableBody.css +3 -3
  74. package/src/patterns/Table/TableCell.css +11 -1
  75. package/src/patterns/Table/TableCellValue.astro +31 -0
  76. package/src/patterns/Table/TableFooter.astro +3 -1
  77. package/src/patterns/Table/TableHeader.astro +1 -0
  78. package/src/patterns/Table/TablePagination.astro +63 -47
  79. package/src/patterns/Table/TableSkeleton.astro +18 -22
  80. package/src/patterns/Table/TableSkeleton.css +30 -5
  81. package/src/patterns/Table/tableClientController.ts +253 -0
  82. package/src/patterns/Table/tableFilterController.ts +23 -31
  83. package/src/patterns/Table/tableProgressiveController.ts +615 -227
  84. package/src/patterns/Table/tableSortController.ts +25 -7
  85. package/src/styles/base.css +24 -7
  86. package/src/styles/interaction.css +16 -0
  87. package/src/styles/style.css +3 -0
  88. package/src/styles/tokens.css +5 -0
@@ -3,14 +3,17 @@ import type { HTMLAttributes } from 'astro/types';
3
3
  import type {
4
4
  TableColumn,
5
5
  TablePaginationModel,
6
+ TableProgressiveModel,
6
7
  TableRow,
7
8
  } from '../../display/contracts';
9
+ import { tableProgressiveBatchSize } from '../../display/tableProgressive';
8
10
  import type { Tone } from '../../theme/contracts';
9
11
  import SurfaceGroup from '../../composition/SurfaceGroup/SurfaceGroup.astro';
10
12
  import TableBody from './TableBody.astro';
11
13
  import TableFooter from './TableFooter.astro';
12
14
  import TableHeader from './TableHeader.astro';
13
15
  import TablePagination from './TablePagination.astro';
16
+ import TableSkeleton from './TableSkeleton.astro';
14
17
  import {
15
18
  getTableColumnDensity,
16
19
  getTableMinimumInlineSize,
@@ -33,6 +36,7 @@ type TablePaginationMessages = {
33
36
 
34
37
  type Props = Omit<HTMLAttributes<'div'>, 'style'> & {
35
38
  caption: string;
39
+ clientProgressive?: boolean;
36
40
  columns: readonly TableColumn[];
37
41
  density?: 'regular' | 'compact';
38
42
  emptyMessage: string;
@@ -44,6 +48,8 @@ type Props = Omit<HTMLAttributes<'div'>, 'style'> & {
44
48
  pagination?: TablePaginationModel;
45
49
  paginationKey?: string;
46
50
  paginationMessages?: TablePaginationMessages;
51
+ preparation?: 'automatic' | 'controlled';
52
+ progressive?: false | TableProgressiveModel;
47
53
  rowSizing?: 'content' | 'fill';
48
54
  rows: readonly TableRow[];
49
55
  skeletonRows?: number;
@@ -54,6 +60,7 @@ type Props = Omit<HTMLAttributes<'div'>, 'style'> & {
54
60
 
55
61
  const {
56
62
  caption,
63
+ clientProgressive = false,
57
64
  class: className,
58
65
  columns,
59
66
  'data-tone': dataTone,
@@ -67,6 +74,8 @@ const {
67
74
  pagination,
68
75
  paginationKey,
69
76
  paginationMessages,
77
+ preparation = 'automatic',
78
+ progressive,
70
79
  rowSizing = 'content',
71
80
  rows,
72
81
  skeletonRows,
@@ -95,6 +104,62 @@ const columnDensity = getTableColumnDensity(columns);
95
104
  const resolvedTone = tone ?? dataTone;
96
105
  const hasSortableColumns = columns.some((column) => column.sort);
97
106
  const hasFooter = Astro.slots.has('footer');
107
+ const configuredProgressive =
108
+ progressive && typeof progressive === 'object' ? progressive : undefined;
109
+ const batchSize = Math.max(
110
+ 1,
111
+ Math.floor(configuredProgressive?.batchSize ?? tableProgressiveBatchSize),
112
+ );
113
+ const resolvedRows = rows.map((row, index): TableRow => ({
114
+ ...row,
115
+ key: row.key ?? `${paginationKey ?? 'table'}-${index + 1}`,
116
+ }));
117
+ const inferredProgressive =
118
+ progressive === undefined && resolvedRows.length > batchSize
119
+ ? {
120
+ batchSize,
121
+ entries: resolvedRows.map((row) => ({
122
+ key: row.key!,
123
+ filters: row.filters,
124
+ sortValues: row.sortValues,
125
+ })),
126
+ }
127
+ : undefined;
128
+ const progressiveModel =
129
+ progressive === false
130
+ ? undefined
131
+ : configuredProgressive ?? inferredProgressive;
132
+ const hasProgressiveRows = Boolean(
133
+ progressiveModel && progressiveModel.entries.length > batchSize,
134
+ );
135
+ const hasProgressivePagination = hasProgressiveRows || clientProgressive;
136
+ const initialSkeletonRows = Math.max(
137
+ 1,
138
+ Math.floor(
139
+ skeletonRows ??
140
+ Math.min(
141
+ batchSize,
142
+ (progressiveModel?.entries.length ?? resolvedRows.length) || 1,
143
+ ),
144
+ ),
145
+ );
146
+ const preparesRows =
147
+ (resolvedRows.length > 0 || clientProgressive) && !errorMessage;
148
+ const initialLoadingState = errorMessage
149
+ ? 'error'
150
+ : resolvedRows.length === 0 && !clientProgressive
151
+ ? 'empty'
152
+ : 'pending';
153
+ const progressiveManifest = hasProgressiveRows
154
+ ? JSON.stringify({
155
+ ...progressiveModel,
156
+ batchSize,
157
+ }).replaceAll('<', '\\u003c')
158
+ : undefined;
159
+ const rowKeys = new Set(resolvedRows.map((row) => row.key));
160
+ const progressiveKeys = new Set(
161
+ progressiveModel?.entries.map((entry) => entry.key) ?? [],
162
+ );
98
163
 
99
164
  if (header === 'visible' && hasSortableColumns && !sortMessages) {
100
165
  throw new Error('Sortable table columns require localized sort messages');
@@ -102,6 +167,21 @@ if (header === 'visible' && hasSortableColumns && !sortMessages) {
102
167
  if (pagination?.nextHref && !paginationMessages) {
103
168
  throw new Error('Paginated tables require localized pagination messages');
104
169
  }
170
+ if (clientProgressive && !paginationMessages) {
171
+ throw new Error('Client-progressive tables require localized messages');
172
+ }
173
+ if (progressiveModel?.sources && !paginationKey) {
174
+ throw new Error('External progressive tables require a stable pagination key');
175
+ }
176
+ if (rowKeys.size !== resolvedRows.length) {
177
+ throw new Error('Table rows require unique keys');
178
+ }
179
+ if (
180
+ progressiveModel &&
181
+ progressiveKeys.size !== progressiveModel.entries.length
182
+ ) {
183
+ throw new Error('Progressive table entries require unique keys');
184
+ }
105
185
  ---
106
186
 
107
187
  <div
@@ -110,18 +190,32 @@ if (pagination?.nextHref && !paginationMessages) {
110
190
  data-widget="table"
111
191
  data-composition="table"
112
192
  data-table-column-density={columnDensity}
193
+ data-table-client-progressive={clientProgressive ? true : undefined}
113
194
  data-table-density={density}
114
195
  data-table-inline-overflow={inlineOverflow}
115
196
  data-table-index-minimum-rem={
116
197
  minimumInlineSize ? Number.parseFloat(minimumInlineSize) : undefined
117
198
  }
118
199
  data-table-layout={layout}
200
+ data-table-loading-state={initialLoadingState}
201
+ data-table-preparation={preparation}
119
202
  data-table-key={paginationKey}
120
203
  data-table-scroll-root
121
204
  data-table-sticky-header={stickyHeader ? true : undefined}
122
205
  data-tone={resolvedTone}
123
206
  style={tableStyle}
124
207
  >
208
+ {
209
+ preparesRows ? (
210
+ <script is:inline>
211
+ document.documentElement.setAttribute('data-nox-table-enhanced', '');
212
+ document.currentScript?.parentElement?.setAttribute(
213
+ 'aria-busy',
214
+ 'true',
215
+ );
216
+ </script>
217
+ ) : null
218
+ }
125
219
  <SurfaceGroup>
126
220
  <slot name="heading" />
127
221
  {
@@ -137,17 +231,17 @@ if (pagination?.nextHref && !paginationMessages) {
137
231
  filterEmptyMessage={filterEmptyMessage}
138
232
  layout={layout}
139
233
  rowSizing={rowSizing}
140
- rows={rows}
234
+ rows={resolvedRows}
235
+ skeletonRows={preparesRows ? initialSkeletonRows : 0}
141
236
  />
142
237
  {
143
- pagination && paginationMessages ? (
238
+ hasProgressivePagination || (pagination && paginationMessages) ? (
144
239
  <TablePagination
145
- columns={columns}
146
- errorLabel={paginationMessages.error}
147
- loadingLabel={paginationMessages.loading}
148
- nextLabel={paginationMessages.nextPage}
240
+ errorLabel={paginationMessages?.error ?? ''}
241
+ loadingLabel={paginationMessages?.loading ?? ''}
242
+ nextLabel={paginationMessages?.nextPage ?? ''}
149
243
  pagination={pagination}
150
- skeletonRows={skeletonRows}
244
+ progressive={hasProgressivePagination}
151
245
  />
152
246
  ) : null
153
247
  }
@@ -159,6 +253,27 @@ if (pagination?.nextHref && !paginationMessages) {
159
253
  ) : null
160
254
  }
161
255
  </SurfaceGroup>
256
+ {
257
+ progressiveManifest ? (
258
+ <script
259
+ is:inline
260
+ type="application/json"
261
+ data-table-progressive-manifest
262
+ set:html={progressiveManifest}
263
+ />
264
+ ) : null
265
+ }
266
+ {
267
+ preparesRows ? (
268
+ <template data-table-skeleton-template>
269
+ <table>
270
+ <tbody>
271
+ <TableSkeleton columns={columns} rows={initialSkeletonRows} />
272
+ </tbody>
273
+ </table>
274
+ </template>
275
+ ) : null
276
+ }
162
277
  </div>
163
278
 
164
279
  <script>
@@ -5,6 +5,7 @@ import type { Tone } from '../../theme/contracts';
5
5
  import TableSortIcon from '../../components/icons/TableSortIcon.astro';
6
6
  import TextBlock from '../../primitives/text/TextBlock/TextBlock.astro';
7
7
  import TableCellContent from './TableCell.astro';
8
+ import TableSkeleton from './TableSkeleton.astro';
8
9
  import {
9
10
  getTableColumnAlignment,
10
11
  type TableLayout,
@@ -20,6 +21,7 @@ type Props = HTMLAttributes<'div'> & {
20
21
  layout?: TableLayout;
21
22
  rowSizing?: 'content' | 'fill';
22
23
  rows: readonly TableRow[];
24
+ skeletonRows?: number;
23
25
  };
24
26
 
25
27
  const {
@@ -33,6 +35,7 @@ const {
33
35
  layout = 'default',
34
36
  rowSizing = 'content',
35
37
  rows,
38
+ skeletonRows = 0,
36
39
  ...attrs
37
40
  }: Props = Astro.props;
38
41
 
@@ -86,6 +89,11 @@ const stateMessage =
86
89
  ))
87
90
  }
88
91
  </tr>
92
+ {
93
+ skeletonRows > 0 ? (
94
+ <TableSkeleton columns={columns} rows={skeletonRows} />
95
+ ) : null
96
+ }
89
97
  {
90
98
  state && stateMessage ? (
91
99
  <tr class="table-body__row" data-table-state={state}>
@@ -130,7 +130,7 @@
130
130
  }
131
131
 
132
132
  .table-body__row {
133
- --table-primary-link-color: var(--nox-text);
133
+ --table-primary-link-color: var(--nox-content-link-color);
134
134
  --table-row-intrinsic-block-size: var(--table-row-min-block-size);
135
135
 
136
136
  display: grid;
@@ -173,7 +173,7 @@
173
173
  }
174
174
 
175
175
  .table-body__row[data-table-row-href]:focus-within {
176
- --table-primary-link-color: var(--nox-link-hover);
176
+ --table-primary-link-color: var(--nox-content-link-color);
177
177
  }
178
178
 
179
179
  .table-body__cell {
@@ -253,7 +253,7 @@
253
253
 
254
254
  @media (hover: hover) and (pointer: fine) {
255
255
  .table-body__row[data-table-row-href]:hover {
256
- --table-primary-link-color: var(--nox-link-hover);
256
+ --table-primary-link-color: var(--nox-content-link-color);
257
257
  }
258
258
  }
259
259
 
@@ -41,6 +41,12 @@
41
41
  min-inline-size: max-content;
42
42
  }
43
43
 
44
+ .table-cell[data-table-cell-kind='actions'] {
45
+ inline-size: max-content;
46
+ min-inline-size: max-content;
47
+ overflow: visible;
48
+ }
49
+
44
50
  .table-cell[data-table-cell-kind='tags']:not([data-wrap])
45
51
  > [data-composition='inline']
46
52
  > [data-widget='tag'] {
@@ -62,6 +68,10 @@
62
68
  var(--nox-motion-easing-standard);
63
69
  }
64
70
 
71
+ .table-cell__link [data-text-weight='strong'] {
72
+ font-weight: var(--nox-content-link-emphasis-font-weight);
73
+ }
74
+
65
75
  .table-cell__empty {
66
76
  color: var(--nox-text-muted);
67
77
  font-size: var(--nox-font-size-sm);
@@ -87,7 +97,7 @@
87
97
 
88
98
  @media (hover: hover) and (pointer: fine) {
89
99
  .table-cell__link:hover {
90
- color: var(--nox-link-hover);
100
+ color: var(--nox-content-link-color);
91
101
  }
92
102
  }
93
103
 
@@ -6,6 +6,7 @@ import TextPair from '../../components/data-display/TextPair/TextPair.astro';
6
6
  import Media from '../../primitives/media/Media/Media.astro';
7
7
  import Inline from '../../composition/Inline/Inline.astro';
8
8
  import Tag from '../../components/data-display/Tag/Tag.astro';
9
+ import Button from '../../components/actions/Button/Button.astro';
9
10
 
10
11
  type Props = {
11
12
  cell: TableCell;
@@ -84,3 +85,33 @@ const formatValue = (value: DisplayTextValue): string => String(value);
84
85
  </Inline>
85
86
  ) : null
86
87
  }
88
+
89
+ {
90
+ cell.kind === 'actions' ? (
91
+ <Inline align="center" gap="xs" justify="end" wrap={false}>
92
+ {cell.actions.map((action) => {
93
+ const Icon = action.icon;
94
+
95
+ return (
96
+ <Button
97
+ aria-controls={action.modalTargetId}
98
+ aria-haspopup={action.modalTargetId ? 'dialog' : undefined}
99
+ aria-label={action.label}
100
+ borderless={action.borderless !== false}
101
+ href={action.href}
102
+ hoverTone={action.hoverTone}
103
+ iconOnly
104
+ size="sm"
105
+ title={action.label}
106
+ tone={action.tone}
107
+ data-modal-open={action.modalTargetId}
108
+ >
109
+ <span data-tone={action.iconTone}>
110
+ <Icon />
111
+ </span>
112
+ </Button>
113
+ );
114
+ })}
115
+ </Inline>
116
+ ) : null
117
+ }
@@ -6,6 +6,7 @@ type TableFooterPadding = 'none' | 'sm' | 'md' | 'lg';
6
6
 
7
7
  type Props = HTMLAttributes<'div'> & {
8
8
  padding?: TableFooterPadding;
9
+ surface?: boolean;
9
10
  tone?: Tone;
10
11
  };
11
12
 
@@ -13,6 +14,7 @@ const {
13
14
  class: className,
14
15
  'data-tone': dataTone,
15
16
  padding = 'md',
17
+ surface = true,
16
18
  tone,
17
19
  ...attrs
18
20
  }: Props = Astro.props;
@@ -26,7 +28,7 @@ const resolvedTone = tone ?? dataTone;
26
28
  data-padding={padding}
27
29
  data-table-footer
28
30
  data-tone={resolvedTone}
29
- data-surface
31
+ data-surface={surface ? true : undefined}
30
32
  >
31
33
  <slot />
32
34
  </div>
@@ -53,6 +53,7 @@ const hasSortableColumns = columns.some((column) => column.sort);
53
53
  type="button"
54
54
  data-table-sort-column={column.id}
55
55
  data-table-sort-direction="none"
56
+ data-table-sort-first-direction={column.sort.firstDirection}
56
57
  data-table-sort-label={column.label}
57
58
  data-table-sort-type={column.sort.type}
58
59
  data-table-sort-tie-breaker={column.sort.tieBreaker}
@@ -1,70 +1,86 @@
1
1
  ---
2
2
  import type { HTMLAttributes } from 'astro/types';
3
- import type {
4
- TableColumn,
5
- TablePaginationModel,
6
- } from '../../display/contracts';
3
+ import type { TablePaginationModel } from '../../display/contracts';
7
4
  import TableFooter from './TableFooter.astro';
8
- import TableSkeleton from './TableSkeleton.astro';
9
5
 
10
6
  type Props = HTMLAttributes<'div'> & {
11
- columns: readonly TableColumn[];
12
7
  errorLabel: string;
13
8
  loadingLabel: string;
14
9
  nextLabel: string;
15
- pagination: TablePaginationModel;
16
- skeletonRows?: number;
10
+ pagination?: TablePaginationModel;
11
+ progressive?: boolean;
17
12
  };
18
13
 
19
14
  const {
20
15
  class: className,
21
- columns,
22
16
  errorLabel,
23
17
  loadingLabel,
24
18
  nextLabel,
25
19
  pagination,
26
- skeletonRows = 3,
20
+ progressive = false,
27
21
  ...attrs
28
22
  }: Props = Astro.props;
29
23
  ---
30
24
 
31
25
  {
32
- pagination.nextHref ? (
33
- <TableFooter
34
- {...attrs}
35
- class:list={['table-pagination', className]}
36
- padding="none"
37
- data-table-pagination
38
- data-table-page={pagination.page}
39
- data-table-total-pages={pagination.totalPages}
40
- data-table-loading-label={loadingLabel}
41
- data-table-error-label={errorLabel}
42
- data-table-progressive-state="idle"
43
- >
44
- <template data-table-skeleton-template>
45
- <TableSkeleton columns={columns} rows={skeletonRows} />
46
- </template>
47
- <div class="table-pagination__content">
48
- <a
49
- class="table-pagination__link"
50
- href={pagination.nextHref}
51
- data-table-pagination-link
52
- >
53
- {nextLabel}
54
- </a>
55
- <span
56
- class="table-pagination__status"
57
- data-table-pagination-status
58
- role="status"
59
- aria-live="polite"
60
- hidden
61
- />
62
- <span
63
- class="table-pagination__sentinel"
64
- data-table-pagination-sentinel
65
- aria-hidden="true"
66
- />
67
- </div>
68
- </TableFooter>
26
+ pagination?.nextHref || progressive ? (
27
+ <>
28
+ <TableFooter
29
+ {...attrs}
30
+ class:list={['table-pagination', className]}
31
+ padding="none"
32
+ surface={!progressive}
33
+ data-table-pagination
34
+ data-table-loading-label={loadingLabel}
35
+ data-table-error-label={errorLabel}
36
+ data-table-progressive-state="idle"
37
+ hidden={progressive || !pagination?.nextHref}
38
+ >
39
+ <div class="table-pagination__content">
40
+ {
41
+ pagination?.nextHref ? (
42
+ <a
43
+ class="table-pagination__link"
44
+ href={pagination.nextHref}
45
+ data-table-pagination-link
46
+ >
47
+ {nextLabel}
48
+ </a>
49
+ ) : null
50
+ }
51
+ <span
52
+ class="table-pagination__status"
53
+ data-table-pagination-status
54
+ role="status"
55
+ aria-live="polite"
56
+ hidden
57
+ />
58
+ <span
59
+ class="table-pagination__sentinel"
60
+ data-table-pagination-sentinel
61
+ aria-hidden="true"
62
+ />
63
+ </div>
64
+ </TableFooter>
65
+ {
66
+ progressive && pagination?.nextHref ? (
67
+ <noscript>
68
+ <TableFooter
69
+ class:list={['table-pagination', className]}
70
+ padding="none"
71
+ >
72
+ <div class="table-pagination__content">
73
+ <a
74
+ class="table-pagination__link"
75
+ href={pagination.nextHref}
76
+ >
77
+ {nextLabel}
78
+ </a>
79
+ </div>
80
+ </TableFooter>
81
+ </noscript>
82
+ ) : null
83
+ }
84
+ </>
69
85
  ) : null
70
86
  }
@@ -11,26 +11,22 @@ const { columns, rows = 3 }: Props = Astro.props;
11
11
  const rowCount = Math.max(1, Math.floor(rows));
12
12
  ---
13
13
 
14
- <table class="table-skeleton" aria-hidden="true">
15
- <tbody>
16
- {
17
- Array.from({ length: rowCount }, () => (
18
- <tr
19
- class="table-skeleton__row"
20
- data-table-skeleton-row
21
- aria-hidden="true"
14
+ {
15
+ Array.from({ length: rowCount }, () => (
16
+ <tr
17
+ class="table-skeleton__row"
18
+ data-table-skeleton-row
19
+ aria-hidden="true"
20
+ >
21
+ {columns.map((column) => (
22
+ <td
23
+ class="table-skeleton__cell"
24
+ data-align={getTableColumnAlignment(column)}
25
+ data-table-column-role={column.role}
22
26
  >
23
- {columns.map((column) => (
24
- <td
25
- class="table-skeleton__cell"
26
- data-align={getTableColumnAlignment(column)}
27
- data-table-column-role={column.role}
28
- >
29
- <span class="table-skeleton__placeholder" />
30
- </td>
31
- ))}
32
- </tr>
33
- ))
34
- }
35
- </tbody>
36
- </table>
27
+ <span class="table-skeleton__placeholder" />
28
+ </td>
29
+ ))}
30
+ </tr>
31
+ ))
32
+ }
@@ -1,16 +1,41 @@
1
- .table-skeleton {
2
- display: none;
3
- }
4
-
5
1
  .table-skeleton__row {
6
- display: grid;
2
+ display: none;
7
3
  min-width: 0;
8
4
  min-height: var(--table-row-min-block-size);
9
5
  grid-column: 1 / -1;
10
6
  grid-template-columns: subgrid;
7
+ border-block-start: 0;
8
+ }
9
+
10
+ .table-skeleton__row + .table-skeleton__row,
11
+ [data-nox-table-enhanced]
12
+ .table[data-table-loading-state='appending']
13
+ .table-body__row:not([hidden])
14
+ + .table-skeleton__row {
11
15
  border-block-start: var(--nox-border-width) solid var(--nox-border);
12
16
  }
13
17
 
18
+ [data-nox-table-enhanced]
19
+ .table[data-table-loading-state='pending']
20
+ .table-body__row[data-table-row],
21
+ [data-nox-table-enhanced]
22
+ .table[data-table-loading-state='refreshing']
23
+ .table-body__row[data-table-row] {
24
+ display: none;
25
+ }
26
+
27
+ [data-nox-table-enhanced]
28
+ .table[data-table-loading-state='pending']
29
+ .table-skeleton__row,
30
+ [data-nox-table-enhanced]
31
+ .table[data-table-loading-state='refreshing']
32
+ .table-skeleton__row,
33
+ [data-nox-table-enhanced]
34
+ .table[data-table-loading-state='appending']
35
+ .table-skeleton__row {
36
+ display: grid;
37
+ }
38
+
14
39
  .table-skeleton__cell {
15
40
  display: grid;
16
41
  min-width: 0;