@astronox/core 0.2.1 → 0.2.2
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/package.json +7 -1
- package/src/admin/contracts.ts +28 -0
- package/src/admin/index.ts +2 -0
- package/src/admin/module.ts +28 -0
- package/src/auth/contracts.ts +53 -0
- package/src/auth/firebase/client.ts +53 -0
- package/src/auth/firebase/contracts.ts +8 -0
- package/src/auth/firebase/index.ts +1 -0
- package/src/auth/index.ts +2 -0
- package/src/auth/module.ts +99 -0
- package/src/client/index.ts +3 -0
- package/src/client/preferences.ts +400 -0
- package/src/components/actions/Button/Button.astro +42 -6
- package/src/components/actions/Button/Button.css +19 -0
- package/src/components/actions/IdentityProviderAction/IdentityProviderAction.astro +35 -0
- package/src/components/content/CardBody/CardBody.astro +3 -0
- package/src/components/content/CardBody/CardBody.css +10 -0
- package/src/components/content/MarkdownContent/markdownContentRenderer.ts +549 -0
- package/src/components/feedback/PreferenceBootstrap/PreferenceBootstrap.astro +403 -0
- package/src/components/forms/Checkbox/Checkbox.astro +6 -0
- package/src/components/forms/ChoiceGroup/ChoiceGroup.astro +57 -0
- package/src/components/forms/ChoiceGroup/ChoiceGroup.css +108 -0
- package/src/components/forms/ChoiceGroup/contracts.ts +9 -0
- package/src/components/forms/DateTimeInput/DateTimeInput.astro +14 -0
- package/src/components/forms/TextInput/TextInput.astro +7 -1
- package/src/components/forms/Textarea/Textarea.astro +32 -0
- package/src/components/forms/Textarea/Textarea.css +54 -0
- package/src/components/icons/BackIcon.astro +26 -0
- package/src/components/icons/DeleteIcon.astro +22 -0
- package/src/components/icons/StatusIndicatorIcon.astro +16 -0
- package/src/components/index.ts +10 -0
- package/src/components/media/CanvasFrame/CanvasFrame.astro +1 -17
- package/src/components/media/EmbedFrame/EmbedFrame.astro +1 -19
- package/src/components/media/EmbedFrame/EmbedFrame.css +0 -2
- package/src/components/navigation/SubNavigation/SubNavigation.css +0 -5
- package/src/composition/SurfaceGroup/SurfaceGroup.css +21 -7
- package/src/composition/SurfaceGroup/stickySurfaceGroupController.ts +3 -0
- package/src/data/firestore/client/contracts.ts +55 -0
- package/src/data/firestore/client/firebase.ts +12 -0
- package/src/data/firestore/client/index.ts +3 -0
- package/src/data/firestore/client/repository.ts +111 -0
- package/src/display/contracts.ts +40 -0
- package/src/display/index.ts +1 -0
- package/src/display/tableProgressive.ts +62 -0
- package/src/patterns/Combobox/Combobox.astro +23 -0
- package/src/patterns/Fullscreen/FullscreenControl.astro +4 -14
- package/src/patterns/Fullscreen/FullscreenControl.css +0 -26
- package/src/patterns/Modal/Modal.astro +1 -1
- package/src/patterns/Modal/Modal.css +38 -1
- package/src/patterns/Table/Table.astro +122 -7
- package/src/patterns/Table/TableBody.astro +8 -0
- package/src/patterns/Table/TableCell.css +6 -0
- package/src/patterns/Table/TableCellValue.astro +31 -0
- package/src/patterns/Table/TableFooter.astro +3 -1
- package/src/patterns/Table/TableHeader.astro +1 -0
- package/src/patterns/Table/TablePagination.astro +63 -47
- package/src/patterns/Table/TableSkeleton.astro +18 -22
- package/src/patterns/Table/TableSkeleton.css +30 -5
- package/src/patterns/Table/tableClientController.ts +253 -0
- package/src/patterns/Table/tableFilterController.ts +23 -31
- package/src/patterns/Table/tableProgressiveController.ts +615 -227
- package/src/patterns/Table/tableSortController.ts +25 -7
- package/src/styles/interaction.css +16 -0
- package/src/styles/style.css +2 -0
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
import type { HTMLAttributes } from 'astro/types';
|
|
3
|
+
import type {
|
|
4
|
+
PreferenceBinding,
|
|
5
|
+
PreferenceNumberBounds,
|
|
6
|
+
} from '../../client/preferences';
|
|
3
7
|
import type { Tone } from '../../theme/contracts';
|
|
4
8
|
import type {
|
|
5
9
|
ComboboxOption,
|
|
@@ -15,6 +19,7 @@ type SharedProps = Omit<HTMLAttributes<'div'>, 'id'> & {
|
|
|
15
19
|
id: string;
|
|
16
20
|
label: string;
|
|
17
21
|
disabled?: boolean;
|
|
22
|
+
preference?: PreferenceBinding;
|
|
18
23
|
size?: ComboboxSize;
|
|
19
24
|
tone?: Tone;
|
|
20
25
|
width?: 'auto' | 'fill';
|
|
@@ -41,6 +46,7 @@ type NumberProps = SharedProps & {
|
|
|
41
46
|
value: number;
|
|
42
47
|
decrementLabel: string;
|
|
43
48
|
incrementLabel: string;
|
|
49
|
+
preferenceBounds?: PreferenceNumberBounds;
|
|
44
50
|
};
|
|
45
51
|
|
|
46
52
|
type Props = OptionsProps | NumberProps;
|
|
@@ -130,6 +136,8 @@ if (mode === 'number') {
|
|
|
130
136
|
'max',
|
|
131
137
|
'min',
|
|
132
138
|
'mode',
|
|
139
|
+
'preference',
|
|
140
|
+
'preferenceBounds',
|
|
133
141
|
'size',
|
|
134
142
|
'step',
|
|
135
143
|
'tone',
|
|
@@ -185,6 +193,7 @@ if (mode === 'number') {
|
|
|
185
193
|
'mode',
|
|
186
194
|
'options',
|
|
187
195
|
'placeholder',
|
|
196
|
+
'preference',
|
|
188
197
|
'removeLabel',
|
|
189
198
|
'searchPlaceholder',
|
|
190
199
|
'selectionMode',
|
|
@@ -233,6 +242,20 @@ const searchId = `${props.id}-search`;
|
|
|
233
242
|
data-combobox-number-step={step}
|
|
234
243
|
data-disabled={disabled ? true : undefined}
|
|
235
244
|
data-tone={resolvedTone}
|
|
245
|
+
data-preference-id={props.preference?.id}
|
|
246
|
+
data-preference-path={props.preference?.path}
|
|
247
|
+
data-preference-control={
|
|
248
|
+
props.preference
|
|
249
|
+
? mode === 'number'
|
|
250
|
+
? 'combobox-number'
|
|
251
|
+
: 'combobox-options'
|
|
252
|
+
: undefined
|
|
253
|
+
}
|
|
254
|
+
data-preference-number-bounds={
|
|
255
|
+
mode === 'number' && (props as NumberProps).preferenceBounds
|
|
256
|
+
? JSON.stringify((props as NumberProps).preferenceBounds)
|
|
257
|
+
: undefined
|
|
258
|
+
}
|
|
236
259
|
>
|
|
237
260
|
<span class="combobox__sizer" aria-hidden="true">
|
|
238
261
|
{
|
|
@@ -6,8 +6,6 @@ import EnterFullscreenIcon from '../../components/icons/EnterFullscreenIcon.astr
|
|
|
6
6
|
import ExitFullscreenIcon from '../../components/icons/ExitFullscreenIcon.astro';
|
|
7
7
|
import ExternalLinkIcon from '../../components/icons/ExternalLinkIcon.astro';
|
|
8
8
|
|
|
9
|
-
type FullscreenControlPlacement = 'inline' | 'overlay';
|
|
10
|
-
type FullscreenOverlayOffset = 'default' | 'native-controls';
|
|
11
9
|
type FullscreenUnsupportedFallback = {
|
|
12
10
|
href: string;
|
|
13
11
|
label: string;
|
|
@@ -17,8 +15,6 @@ type FullscreenUnsupportedFallback = {
|
|
|
17
15
|
type Props = Omit<HTMLAttributes<'button'>, 'aria-controls' | 'type'> & {
|
|
18
16
|
enterLabel: string;
|
|
19
17
|
exitLabel: string;
|
|
20
|
-
overlayOffset?: FullscreenOverlayOffset;
|
|
21
|
-
placement?: FullscreenControlPlacement;
|
|
22
18
|
targetId: string;
|
|
23
19
|
tone?: Tone;
|
|
24
20
|
unsupportedFallback?: FullscreenUnsupportedFallback;
|
|
@@ -28,8 +24,6 @@ const {
|
|
|
28
24
|
class: className,
|
|
29
25
|
enterLabel,
|
|
30
26
|
exitLabel,
|
|
31
|
-
overlayOffset = 'default',
|
|
32
|
-
placement = 'inline',
|
|
33
27
|
targetId,
|
|
34
28
|
tone,
|
|
35
29
|
unsupportedFallback,
|
|
@@ -44,10 +38,9 @@ const resolvedClass = ['fullscreen-control', className]
|
|
|
44
38
|
{...attrs}
|
|
45
39
|
class={resolvedClass}
|
|
46
40
|
type="button"
|
|
47
|
-
borderless
|
|
48
|
-
full
|
|
41
|
+
borderless
|
|
42
|
+
full
|
|
49
43
|
iconOnly
|
|
50
|
-
size={placement === 'overlay' ? 'md' : undefined}
|
|
51
44
|
tone={tone}
|
|
52
45
|
aria-controls={targetId}
|
|
53
46
|
aria-label={enterLabel}
|
|
@@ -56,8 +49,6 @@ const resolvedClass = ['fullscreen-control', className]
|
|
|
56
49
|
data-fullscreen-control
|
|
57
50
|
data-fullscreen-enter-label={enterLabel}
|
|
58
51
|
data-fullscreen-exit-label={exitLabel}
|
|
59
|
-
data-fullscreen-overlay-offset={overlayOffset}
|
|
60
|
-
data-fullscreen-placement={placement}
|
|
61
52
|
data-fullscreen-target={targetId}
|
|
62
53
|
>
|
|
63
54
|
<span class="fullscreen-control__icon" data-fullscreen-enter-icon>
|
|
@@ -73,10 +64,9 @@ const resolvedClass = ['fullscreen-control', className]
|
|
|
73
64
|
<Button
|
|
74
65
|
href={unsupportedFallback.href}
|
|
75
66
|
target={unsupportedFallback.target ?? '_blank'}
|
|
76
|
-
borderless
|
|
77
|
-
full
|
|
67
|
+
borderless
|
|
68
|
+
full
|
|
78
69
|
iconOnly
|
|
79
|
-
size={placement === 'overlay' ? 'md' : undefined}
|
|
80
70
|
tone={tone}
|
|
81
71
|
aria-label={unsupportedFallback.label}
|
|
82
72
|
title={unsupportedFallback.label}
|
|
@@ -15,32 +15,6 @@
|
|
|
15
15
|
display: grid;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
.fullscreen-control[data-fullscreen-placement='overlay'].nox-button {
|
|
19
|
-
position: absolute;
|
|
20
|
-
z-index: 2;
|
|
21
|
-
display: none;
|
|
22
|
-
inset-inline-end: var(--nox-space-3);
|
|
23
|
-
inset-block-end: var(--nox-space-3);
|
|
24
|
-
inline-size: var(--nox-section-responsive-block-size);
|
|
25
|
-
block-size: var(--nox-section-responsive-block-size);
|
|
26
|
-
min-block-size: 0;
|
|
27
|
-
border-radius: var(--nox-radius-md);
|
|
28
|
-
box-shadow: var(--nox-shadow-surface);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
[data-fullscreen-region]:fullscreen
|
|
32
|
-
.fullscreen-control[data-fullscreen-placement='overlay'].nox-button {
|
|
33
|
-
display: inline-flex;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
[data-fullscreen-region]:fullscreen
|
|
37
|
-
.fullscreen-control[data-fullscreen-placement='overlay'][data-fullscreen-overlay-offset='native-controls'].nox-button {
|
|
38
|
-
inset-block-end: calc(
|
|
39
|
-
var(--nox-fullscreen-native-controls-block-size, 2.5rem) +
|
|
40
|
-
var(--nox-space-3)
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
18
|
[data-fullscreen-region]:fullscreen {
|
|
45
19
|
inline-size: 100vw;
|
|
46
20
|
block-size: 100vh;
|
|
@@ -4,7 +4,7 @@ import type { Tone } from '../../theme/contracts';
|
|
|
4
4
|
import ModalClose from './ModalClose.astro';
|
|
5
5
|
|
|
6
6
|
type ModalCloseControl = 'default' | 'delegated';
|
|
7
|
-
type ModalSizing = 'content' | 'viewport';
|
|
7
|
+
type ModalSizing = 'adaptive' | 'confirmation' | 'content' | 'viewport';
|
|
8
8
|
|
|
9
9
|
type Props = HTMLAttributes<'dialog'> & {
|
|
10
10
|
id: string;
|
|
@@ -48,6 +48,33 @@ html:has(.modal[open]) [data-site-viewport] {
|
|
|
48
48
|
overflow: auto;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
.modal[data-sizing='confirmation'] .modal__content {
|
|
52
|
+
inline-size: min(42rem, 100%);
|
|
53
|
+
block-size: auto;
|
|
54
|
+
max-block-size: 100%;
|
|
55
|
+
align-self: center;
|
|
56
|
+
overflow: auto;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.modal[data-sizing='adaptive'] .modal__content {
|
|
60
|
+
display: flex;
|
|
61
|
+
block-size: 100%;
|
|
62
|
+
min-block-size: 0;
|
|
63
|
+
flex-direction: column;
|
|
64
|
+
justify-content: center;
|
|
65
|
+
overflow: hidden;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.modal[data-sizing='adaptive']
|
|
69
|
+
.modal__content
|
|
70
|
+
> [data-composition='surface-group'] {
|
|
71
|
+
inline-size: 100%;
|
|
72
|
+
min-block-size: 0;
|
|
73
|
+
max-block-size: 100%;
|
|
74
|
+
flex: 0 1 auto;
|
|
75
|
+
overflow: hidden;
|
|
76
|
+
}
|
|
77
|
+
|
|
51
78
|
.modal__default-close {
|
|
52
79
|
position: absolute;
|
|
53
80
|
z-index: 1;
|
|
@@ -81,7 +108,17 @@ html:has(.modal[open]) [data-site-viewport] {
|
|
|
81
108
|
padding-inline: 0;
|
|
82
109
|
}
|
|
83
110
|
|
|
84
|
-
.
|
|
111
|
+
.modal:not(
|
|
112
|
+
[data-sizing='adaptive'],
|
|
113
|
+
[data-sizing='confirmation']
|
|
114
|
+
)
|
|
115
|
+
.modal__content
|
|
116
|
+
[data-surface] {
|
|
85
117
|
border-radius: 0;
|
|
86
118
|
}
|
|
119
|
+
|
|
120
|
+
.modal:is([data-sizing='adaptive'], [data-sizing='confirmation'])
|
|
121
|
+
.modal__content {
|
|
122
|
+
padding: var(--nox-space-2);
|
|
123
|
+
}
|
|
87
124
|
}
|
|
@@ -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={
|
|
234
|
+
rows={resolvedRows}
|
|
235
|
+
skeletonRows={preparesRows ? initialSkeletonRows : 0}
|
|
141
236
|
/>
|
|
142
237
|
{
|
|
143
|
-
pagination && paginationMessages ? (
|
|
238
|
+
hasProgressivePagination || (pagination && paginationMessages) ? (
|
|
144
239
|
<TablePagination
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
nextLabel={paginationMessages.nextPage}
|
|
240
|
+
errorLabel={paginationMessages?.error ?? ''}
|
|
241
|
+
loadingLabel={paginationMessages?.loading ?? ''}
|
|
242
|
+
nextLabel={paginationMessages?.nextPage ?? ''}
|
|
149
243
|
pagination={pagination}
|
|
150
|
-
|
|
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}>
|
|
@@ -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'] {
|
|
@@ -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}
|