@duro-app/ui 0.31.0 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,8 +3,25 @@ import {colors} from '@duro-app/tokens/tokens/colors.css'
3
3
  import {spacing, radii} from '@duro-app/tokens/tokens/spacing.css'
4
4
  import {typography} from '@duro-app/tokens/tokens/typography.css'
5
5
 
6
+ // Container-query thresholds. Picked for typical 5-7 column admin tables —
7
+ // see Table.stories.tsx "Responsive" for the visual reasoning. Defined
8
+ // once here so it's clear they're a design-system constant, not a magic
9
+ // number sprinkled across files.
10
+ const COMPACT_BP = '720px'
11
+ const STACK_BP = '440px'
12
+
6
13
  export const styles = css.create({
7
- // Root — the single grid container for the entire table
14
+ // Outer wrapper that hosts the @container query. Wraps SortChip + Root
15
+ // + Pagination so all three react to the same width. Root itself does
16
+ // NOT carry containerType — keeping a single query target per Table.
17
+ container: {
18
+ containerType: 'inline-size',
19
+ display: 'flex',
20
+ flexDirection: 'column',
21
+ gap: spacing.sm,
22
+ },
23
+
24
+ // Root — the single grid container for the table itself
8
25
  root: {
9
26
  display: 'grid',
10
27
  borderWidth: 1,
@@ -17,9 +34,40 @@ export const styles = css.create({
17
34
  color: colors.text,
18
35
  },
19
36
 
20
- // Header group
37
+ // Stack-mode override: the Root's grid collapses to a single column so
38
+ // each Row spans full width and stacks vertically. Applied AFTER the
39
+ // dynamic gridColumns(template) so this rule wins inside the @container.
40
+ rootResponsive: {
41
+ gridTemplateColumns: {
42
+ default: null,
43
+ [`@container (max-width: ${STACK_BP})`]: '1fr',
44
+ },
45
+ // The card-list look needs the outer border to disappear in stack
46
+ // mode — each row paints its own border.
47
+ borderWidth: {
48
+ default: 1,
49
+ [`@container (max-width: ${STACK_BP})`]: 0,
50
+ },
51
+ backgroundColor: {
52
+ default: colors.bgCard,
53
+ [`@container (max-width: ${STACK_BP})`]: 'transparent',
54
+ },
55
+ overflow: {
56
+ default: 'hidden',
57
+ [`@container (max-width: ${STACK_BP})`]: 'visible',
58
+ },
59
+ rowGap: {
60
+ default: 0,
61
+ [`@container (max-width: ${STACK_BP})`]: spacing.sm,
62
+ },
63
+ },
64
+
65
+ // Header group — hidden entirely in stack mode
21
66
  header: {
22
- display: 'grid',
67
+ display: {
68
+ default: 'grid',
69
+ [`@container (max-width: ${STACK_BP})`]: 'none',
70
+ },
23
71
  gridColumn: '1 / -1',
24
72
  gridTemplateColumns: 'subgrid',
25
73
  backgroundColor: colors.bgCard,
@@ -32,43 +80,148 @@ export const styles = css.create({
32
80
  gridTemplateColumns: 'subgrid',
33
81
  },
34
82
 
35
- // Row — spans all columns, uses subgrid to share parent tracks
83
+ // Row — spans all columns. In stack mode its template flips to 1fr so
84
+ // children stack as grid items in a single column (we keep grid context
85
+ // so cells with `gridColumn` overrides still compute).
36
86
  row: {
37
87
  display: 'grid',
38
88
  gridColumn: '1 / -1',
39
- gridTemplateColumns: 'subgrid',
40
- borderBottomWidth: 1,
89
+ gridTemplateColumns: {
90
+ default: 'subgrid',
91
+ [`@container (max-width: ${STACK_BP})`]: '1fr',
92
+ },
93
+ borderBottomWidth: {
94
+ default: 1,
95
+ [`@container (max-width: ${STACK_BP})`]: 0,
96
+ },
41
97
  borderBottomStyle: 'solid',
42
98
  borderBottomColor: colors.border,
43
99
  },
44
100
  bodyRow: {
45
101
  backgroundColor: {
46
102
  default: 'transparent',
103
+ [`@container (max-width: ${STACK_BP})`]: colors.bgCard,
47
104
  ':hover': colors.bgCardHover,
48
105
  },
49
106
  transitionProperty: 'background-color',
50
107
  transitionDuration: '150ms',
51
108
  transitionTimingFunction: 'ease',
109
+ // Stack-mode card chrome
110
+ padding: {
111
+ default: 0,
112
+ [`@container (max-width: ${STACK_BP})`]: spacing.sm,
113
+ },
114
+ borderRadius: {
115
+ default: 0,
116
+ [`@container (max-width: ${STACK_BP})`]: radii.sm,
117
+ },
118
+ borderWidth: {
119
+ default: 0,
120
+ [`@container (max-width: ${STACK_BP})`]: 1,
121
+ },
122
+ borderStyle: 'solid',
123
+ borderColor: colors.border,
52
124
  },
53
125
 
54
- // Header cell
126
+ // Header cell — keeps headers on a single line in compact mode
55
127
  headerCell: {
56
128
  fontWeight: typography.fontWeightSemibold,
57
129
  color: colors.textMuted,
58
130
  textAlign: 'start',
131
+ whiteSpace: {
132
+ default: 'normal',
133
+ [`@container (max-width: ${COMPACT_BP})`]: 'nowrap',
134
+ },
59
135
  },
60
136
 
61
- // Body cell
137
+ // Body cell. In compact mode: nowrap + truncate. In stack mode:
138
+ // becomes a 2-column grid for label / value.
62
139
  cell: {
63
140
  color: colors.text,
64
- display: 'flex',
141
+ display: {
142
+ default: 'flex',
143
+ [`@container (max-width: ${STACK_BP})`]: 'grid',
144
+ },
65
145
  alignItems: 'center',
146
+ minWidth: {
147
+ default: 'auto',
148
+ [`@container (max-width: ${COMPACT_BP})`]: 0,
149
+ },
150
+ overflow: {
151
+ default: 'visible',
152
+ [`@container (max-width: ${COMPACT_BP})`]: 'hidden',
153
+ },
154
+ textOverflow: {
155
+ default: 'clip',
156
+ [`@container (max-width: ${COMPACT_BP})`]: 'ellipsis',
157
+ },
158
+ whiteSpace: {
159
+ default: 'normal',
160
+ [`@container (max-width: ${COMPACT_BP})`]: 'nowrap',
161
+ [`@container (max-width: ${STACK_BP})`]: 'normal',
162
+ },
163
+ gridTemplateColumns: {
164
+ default: null,
165
+ [`@container (max-width: ${STACK_BP})`]: '1fr 2fr',
166
+ },
167
+ gap: {
168
+ default: 0,
169
+ [`@container (max-width: ${STACK_BP})`]: spacing.sm,
170
+ },
171
+ },
172
+
173
+ // Label rendered as a real <span> inside each cell. Display:none in
174
+ // default/compact, shown only in stack — matches StyleX guidance to
175
+ // prefer real elements over ::before/::after for non-shadow-DOM cases.
176
+ cellLabel: {
177
+ display: {
178
+ default: 'none',
179
+ [`@container (max-width: ${STACK_BP})`]: 'block',
180
+ },
181
+ color: colors.textMuted,
182
+ fontWeight: typography.fontWeightMedium,
183
+ fontSize: typography.fontSizeXs,
184
+ },
185
+
186
+ // Actions cell. In default/compact behaves like any cell. In stack
187
+ // mode: full-width footer of the card, right-aligned, with a top
188
+ // separator. Label span is suppressed in JSX (Cell does not render it
189
+ // when isActions=true).
190
+ cellActions: {
191
+ gridTemplateColumns: {
192
+ default: null,
193
+ [`@container (max-width: ${STACK_BP})`]: '1fr',
194
+ },
195
+ justifyContent: {
196
+ default: null,
197
+ [`@container (max-width: ${STACK_BP})`]: 'flex-end',
198
+ },
199
+ marginTop: {
200
+ default: 0,
201
+ [`@container (max-width: ${STACK_BP})`]: spacing.sm,
202
+ },
203
+ paddingTop: {
204
+ default: null,
205
+ [`@container (max-width: ${STACK_BP})`]: spacing.sm,
206
+ },
207
+ borderTopWidth: {
208
+ default: 0,
209
+ [`@container (max-width: ${STACK_BP})`]: 1,
210
+ },
211
+ borderTopStyle: 'solid',
212
+ borderTopColor: colors.border,
66
213
  },
67
214
 
68
215
  // Size: sm
69
216
  cellSm: {
70
- paddingTop: spacing.xs,
71
- paddingBottom: spacing.xs,
217
+ paddingTop: {
218
+ default: spacing.xs,
219
+ [`@container (max-width: ${COMPACT_BP})`]: spacing.xs,
220
+ },
221
+ paddingBottom: {
222
+ default: spacing.xs,
223
+ [`@container (max-width: ${COMPACT_BP})`]: spacing.xs,
224
+ },
72
225
  paddingLeft: spacing.sm,
73
226
  paddingRight: spacing.sm,
74
227
  fontSize: typography.fontSizeXs,
@@ -77,22 +230,33 @@ export const styles = css.create({
77
230
  cellMd: {
78
231
  paddingTop: spacing.sm,
79
232
  paddingBottom: spacing.sm,
80
- paddingLeft: spacing.md,
81
- paddingRight: spacing.md,
233
+ paddingLeft: {
234
+ default: spacing.md,
235
+ [`@container (max-width: ${COMPACT_BP})`]: spacing.sm,
236
+ },
237
+ paddingRight: {
238
+ default: spacing.md,
239
+ [`@container (max-width: ${COMPACT_BP})`]: spacing.sm,
240
+ },
82
241
  fontSize: typography.fontSizeSm,
83
242
  },
84
243
 
85
- // Variant: striped (even body rows)
244
+ // Variant: striped (even body rows). Suppressed in stack — cards have
245
+ // their own surface and zebra-striping reads as a bug there.
86
246
  stripedEven: {
87
247
  backgroundColor: {
88
248
  default: colors.bgCardHover,
249
+ [`@container (max-width: ${STACK_BP})`]: colors.bgCard,
89
250
  ':hover': colors.bgCardHover,
90
251
  },
91
252
  },
92
253
 
93
- // Variant: bordered (cells get side borders)
254
+ // Variant: bordered (cells get side borders). Suppressed in stack.
94
255
  borderedCell: {
95
- borderRightWidth: 1,
256
+ borderRightWidth: {
257
+ default: 1,
258
+ [`@container (max-width: ${STACK_BP})`]: 0,
259
+ },
96
260
  borderRightStyle: 'solid',
97
261
  borderRightColor: colors.border,
98
262
  },
@@ -100,6 +264,15 @@ export const styles = css.create({
100
264
  borderRightWidth: 0,
101
265
  },
102
266
 
267
+ // SortChip — visible only in stack mode
268
+ sortChip: {
269
+ display: {
270
+ default: 'none',
271
+ [`@container (max-width: ${STACK_BP})`]: 'inline-flex',
272
+ },
273
+ alignSelf: 'flex-start',
274
+ },
275
+
103
276
  // Dynamic: grid columns applied on Root
104
277
  gridColumns: (template: string) => ({
105
278
  gridTemplateColumns: template,