@colixsystems/widget-sdk 0.237.0 → 0.239.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.
- package/README.md +39 -4
- package/dist/checkbox-view.js +28 -16
- package/dist/contract.cjs +26 -12
- package/dist/contract.js +26 -12
- package/dist/file-upload-logic.js +178 -0
- package/dist/file-upload-view.js +321 -0
- package/dist/file-upload.js +62 -0
- package/dist/file-upload.native.js +11 -0
- package/dist/filepicker.js +11 -0
- package/dist/filepicker.native.js +10 -1
- package/dist/form-control-view.js +8 -1
- package/dist/index.d.ts +162 -16
- package/dist/index.js +2 -0
- package/dist/index.native.js +2 -0
- package/dist/list-tokens.js +54 -14
- package/dist/list-view.js +199 -82
- package/dist/list.js +7 -1
- package/dist/list.native.js +3 -2
- package/dist/primitives.js +1 -3
- package/dist/primitives.native.js +2 -4
- package/dist/property-schema.js +6 -0
- package/dist/select-logic.js +83 -0
- package/dist/select-view.js +165 -32
- package/dist/select.js +6 -1
- package/dist/select.native.js +5 -3
- package/package.json +1 -1
- package/src/contract.js +26 -12
package/dist/index.d.ts
CHANGED
|
@@ -39,6 +39,9 @@ export type WidgetPropertyType =
|
|
|
39
39
|
// REQ-WDG-PAGEREF: page picker that emits a bare AppPage UUID; a widget
|
|
40
40
|
// reads it and navigates via useNavigation().goTo(pageId, params).
|
|
41
41
|
| "pageRef"
|
|
42
|
+
// REQ-BLD-19: section picker for the widget's own page → bare node id; a
|
|
43
|
+
// widget scrolls to it via useNavigation().scrollToSection(id).
|
|
44
|
+
| "sectionRef"
|
|
42
45
|
// REQ-WDG-RICHTEXT: rich-text (HTML string) edited via a Tiptap editor.
|
|
43
46
|
| "richText"
|
|
44
47
|
// REQ-WDG-ASSET: file/asset picker → bare File UUID (Asset Manager picker).
|
|
@@ -841,6 +844,7 @@ export interface WidgetContext<TProps = unknown> {
|
|
|
841
844
|
navigation: {
|
|
842
845
|
goTo(pageId: string, params?: Record<string, string>): void;
|
|
843
846
|
goToStartPage(): void;
|
|
847
|
+
scrollToSection(sectionId: string): void;
|
|
844
848
|
goBack(): void;
|
|
845
849
|
push(pageId: string, params?: Record<string, unknown>): void;
|
|
846
850
|
replace(pageId: string, params?: Record<string, unknown>): void;
|
|
@@ -2210,12 +2214,15 @@ export function useFitScreenInsets(): FitScreenInsets;
|
|
|
2210
2214
|
* The host-provided navigation surface. `goTo(pageId, params?)` navigates
|
|
2211
2215
|
* to an internal app page; `goBack()` pops the stack. `goToStartPage()`
|
|
2212
2216
|
* opens the signed-in user's own start page, which can differ per user.
|
|
2217
|
+
* `scrollToSection(sectionId)` scrolls this page to one of its sections (the
|
|
2218
|
+
* id comes from a `sectionRef` prop); an unknown section is a no-op.
|
|
2213
2219
|
* Missing methods degrade to no-ops on the Studio canvas preview where no
|
|
2214
2220
|
* live router is mounted. For external URLs use the `Linking` primitive.
|
|
2215
2221
|
*/
|
|
2216
2222
|
export function useNavigation(): {
|
|
2217
2223
|
goTo(pageId: string, params?: Record<string, unknown>): void;
|
|
2218
2224
|
goToStartPage(): void;
|
|
2225
|
+
scrollToSection(sectionId: string): void;
|
|
2219
2226
|
goBack(): void;
|
|
2220
2227
|
push(pageId: string, params?: Record<string, unknown>): void;
|
|
2221
2228
|
replace(pageId: string, params?: Record<string, unknown>): void;
|
|
@@ -3711,8 +3718,18 @@ export interface ListRowTokens {
|
|
|
3711
3718
|
/**
|
|
3712
3719
|
* Themed list container. Spaces and insets its `<ListRow>` children the way the
|
|
3713
3720
|
* app's look says, and hands its overrides down to every row.
|
|
3721
|
+
*
|
|
3722
|
+
* @example
|
|
3723
|
+
* // A list of cards: the app's card look outside, its list look inside.
|
|
3724
|
+
* <List variant="card">
|
|
3725
|
+
* {orders.map((o) => <ListRow key={o.id} title={o.name} subtitle={o.status} onPress={() => open(o)} />)}
|
|
3726
|
+
* </List>
|
|
3714
3727
|
*/
|
|
3715
3728
|
export const List: (props: ListRowTokens & {
|
|
3729
|
+
/** "card" draws every row as its own card (Theme Settings > Cards). Defaults to "plain". */
|
|
3730
|
+
variant?: "plain" | "card";
|
|
3731
|
+
/** The widget's own row look, used only where Theme Settings > Lists leaves a value unset. */
|
|
3732
|
+
defaults?: ListRowTokens;
|
|
3716
3733
|
style?: any;
|
|
3717
3734
|
testID?: string;
|
|
3718
3735
|
children?: ReactNode;
|
|
@@ -3736,6 +3753,13 @@ export const ListRow: (props: ListRowTokens & {
|
|
|
3736
3753
|
trailing?: ReactNode;
|
|
3737
3754
|
/** Makes the row a button with a press state. */
|
|
3738
3755
|
onPress?: () => void;
|
|
3756
|
+
/**
|
|
3757
|
+
* The row's own buttons (remove, move, sign), after `trailing`. On a row
|
|
3758
|
+
* with `onPress` they stay outside its tap target, each its own button.
|
|
3759
|
+
*/
|
|
3760
|
+
actions?: ReactNode;
|
|
3761
|
+
/** As `<List defaults>`, for this row. */
|
|
3762
|
+
defaults?: ListRowTokens;
|
|
3739
3763
|
selected?: boolean;
|
|
3740
3764
|
disabled?: boolean;
|
|
3741
3765
|
/** Defaults to true; pass false on the last row. */
|
|
@@ -4059,22 +4083,10 @@ export interface SelectOption<V extends string | number = string | number> {
|
|
|
4059
4083
|
testID?: string;
|
|
4060
4084
|
}
|
|
4061
4085
|
|
|
4062
|
-
/**
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
* sheet painted from the `popover` scope, so no container clips them and a
|
|
4066
|
-
* long list scrolls. Each option has the option role; the list is a listbox
|
|
4067
|
-
* on the web only. Other props land on the trigger.
|
|
4068
|
-
*
|
|
4069
|
-
* @example
|
|
4070
|
-
* <Select label="Status" value={status} onValueChange={setStatus}
|
|
4071
|
-
* options={[{ value: "open", label: "Open" }, { value: "done", label: "Done" }]} />
|
|
4072
|
-
*/
|
|
4073
|
-
export const Select: <V extends string | number = string | number>(
|
|
4074
|
-
props: FormControlProps & FormFieldOverrides & {
|
|
4086
|
+
/** What every `<Select>` takes, in either mode. */
|
|
4087
|
+
export type SelectBaseProps<V extends string | number = string | number> = FormControlProps &
|
|
4088
|
+
FormFieldOverrides & {
|
|
4075
4089
|
options: Array<SelectOption<V>>;
|
|
4076
|
-
value?: V | null;
|
|
4077
|
-
onValueChange?: (value: V, option: SelectOption<V>) => void;
|
|
4078
4090
|
/** Shown while nothing is chosen. Default "Select". */
|
|
4079
4091
|
placeholder?: string;
|
|
4080
4092
|
/** The sheet's heading. Defaults to a text `label`. */
|
|
@@ -4086,7 +4098,46 @@ export const Select: <V extends string | number = string | number>(
|
|
|
4086
4098
|
style?: any;
|
|
4087
4099
|
containerStyle?: any;
|
|
4088
4100
|
[prop: string]: any;
|
|
4089
|
-
}
|
|
4101
|
+
};
|
|
4102
|
+
|
|
4103
|
+
/**
|
|
4104
|
+
* Themed picker for one value of a known list, or several with `multiple`.
|
|
4105
|
+
* The trigger is the input field, a button that opens a dialog; its options
|
|
4106
|
+
* open at screen level (through `Overlay`) in a sheet painted from the
|
|
4107
|
+
* `popover` scope, so no container clips them and a long list scrolls. Each
|
|
4108
|
+
* option has the option role; the list is a listbox on the web only
|
|
4109
|
+
* (multiselectable with `multiple`). Other props land on the trigger.
|
|
4110
|
+
*
|
|
4111
|
+
* With `multiple` each option carries a tick box and toggles a draft; the
|
|
4112
|
+
* sheet's Done button commits it through `onValuesChange`, and closing it any
|
|
4113
|
+
* other way (backdrop, Escape, Android back) keeps the previous choice. The
|
|
4114
|
+
* trigger shows the picked labels as small chips while they fit on its line,
|
|
4115
|
+
* else "N selected".
|
|
4116
|
+
*
|
|
4117
|
+
* @example
|
|
4118
|
+
* <Select label="Status" value={status} onValueChange={setStatus}
|
|
4119
|
+
* options={[{ value: "open", label: "Open" }, { value: "done", label: "Done" }]} />
|
|
4120
|
+
* <Select multiple label="Tags" values={tags} onValuesChange={setTags}
|
|
4121
|
+
* options={[{ value: "red", label: "Red" }, { value: "blue", label: "Blue" }]} />
|
|
4122
|
+
*/
|
|
4123
|
+
export const Select: <V extends string | number = string | number>(
|
|
4124
|
+
props:
|
|
4125
|
+
| (SelectBaseProps<V> & {
|
|
4126
|
+
multiple?: false;
|
|
4127
|
+
value?: V | null;
|
|
4128
|
+
onValueChange?: (value: V, option: SelectOption<V>) => void;
|
|
4129
|
+
})
|
|
4130
|
+
| (SelectBaseProps<V> & {
|
|
4131
|
+
multiple: true;
|
|
4132
|
+
/** The picked values; any order. */
|
|
4133
|
+
values?: V[] | null;
|
|
4134
|
+
/** Called on Done with the new values in listed order, only when they changed. */
|
|
4135
|
+
onValuesChange?: (values: V[], options: Array<SelectOption<V>>) => void;
|
|
4136
|
+
/** The trigger's text when the chips do not fit. Default `${count} selected`. */
|
|
4137
|
+
selectedCountText?: (count: number) => string;
|
|
4138
|
+
/** The sheet's confirm button. Default "Done". */
|
|
4139
|
+
doneText?: string;
|
|
4140
|
+
}),
|
|
4090
4141
|
) => any;
|
|
4091
4142
|
|
|
4092
4143
|
/**
|
|
@@ -4263,6 +4314,101 @@ export const ChipGroup: (props: {
|
|
|
4263
4314
|
testID?: string;
|
|
4264
4315
|
}) => any;
|
|
4265
4316
|
|
|
4317
|
+
/**
|
|
4318
|
+
* A file as `<FilePicker>` and `<FileUpload>` hand it over: the browser's
|
|
4319
|
+
* `File` on the web, expo-file-system's `File` on the Expo export. Pass it to
|
|
4320
|
+
* `upload(file)` from `useFilestoreUpload` as it is.
|
|
4321
|
+
*/
|
|
4322
|
+
export interface PickedFile {
|
|
4323
|
+
name: string;
|
|
4324
|
+
type?: string;
|
|
4325
|
+
size?: number;
|
|
4326
|
+
[key: string]: any;
|
|
4327
|
+
}
|
|
4328
|
+
|
|
4329
|
+
/**
|
|
4330
|
+
* The system file picker. Its children are the visible trigger. Web wraps a
|
|
4331
|
+
* hidden file input; native opens the OS document picker. `isSupported` is
|
|
4332
|
+
* false on a host with no picker. For an upload field use `<FileUpload>`.
|
|
4333
|
+
*/
|
|
4334
|
+
export const FilePicker: ((props: {
|
|
4335
|
+
/** An HTML accept value: "image/*", ".pdf", "application/pdf". */
|
|
4336
|
+
accept?: string;
|
|
4337
|
+
multiple?: boolean;
|
|
4338
|
+
disabled?: boolean;
|
|
4339
|
+
/** One file, or an array when `multiple`. */
|
|
4340
|
+
onPick?: (picked: PickedFile | PickedFile[]) => void;
|
|
4341
|
+
/** The picker's keyboard focus, for a trigger that draws its own ring. */
|
|
4342
|
+
onFocus?: () => void;
|
|
4343
|
+
onBlur?: () => void;
|
|
4344
|
+
accessibilityLabel?: string;
|
|
4345
|
+
/** The id of the note that describes the picker, such as a field's error. */
|
|
4346
|
+
"aria-describedby"?: string;
|
|
4347
|
+
"aria-invalid"?: boolean;
|
|
4348
|
+
children?: ReactNode;
|
|
4349
|
+
}) => any) & { isSupported: boolean };
|
|
4350
|
+
|
|
4351
|
+
/**
|
|
4352
|
+
* Themed file chooser on `<FilePicker>`: a button (the `button` scope) in the
|
|
4353
|
+
* input field (the `muted` scope), the chosen files with their size and a
|
|
4354
|
+
* remove control, and the form controls' `label` / `helper` / `error` /
|
|
4355
|
+
* `disabled`. On the web a file can also be dropped onto the field; native
|
|
4356
|
+
* has no file drop, so the button is the control there. A file `accept` rules
|
|
4357
|
+
* out is not added and shows an error. It uploads nothing: upload in
|
|
4358
|
+
* `onPick` with `useFilestoreUpload` and pass `uploading` while it runs, which
|
|
4359
|
+
* shows an indeterminate `<Spinner>` and locks the field.
|
|
4360
|
+
*
|
|
4361
|
+
* @example
|
|
4362
|
+
* const { upload, uploading } = useFilestoreUpload({ spaceType: "project" });
|
|
4363
|
+
* const [photos, setPhotos] = useState([]);
|
|
4364
|
+
* <FileUpload label="Photos" accept="image/*" multiple uploading={uploading}
|
|
4365
|
+
* files={photos} onFilesChange={setPhotos}
|
|
4366
|
+
* onPick={(picked) => Promise.all(picked.map((f) => upload(f))).then(() => setPhotos([]))} />
|
|
4367
|
+
*/
|
|
4368
|
+
export const FileUpload: (props: FormControlProps & {
|
|
4369
|
+
accept?: string;
|
|
4370
|
+
/** A new pick joins the list instead of replacing the file. Default false. */
|
|
4371
|
+
multiple?: boolean;
|
|
4372
|
+
/**
|
|
4373
|
+
* The listed files; pass with `onFilesChange` to own the list. A list the
|
|
4374
|
+
* field keeps itself still shows a file after its upload, so own it to clear it.
|
|
4375
|
+
*/
|
|
4376
|
+
files?: PickedFile[];
|
|
4377
|
+
/** After every pick, drop and removal. */
|
|
4378
|
+
onFilesChange?: (files: PickedFile[]) => void;
|
|
4379
|
+
/** Once per pick or drop, with only the new files (an array when `multiple`). */
|
|
4380
|
+
onPick?: (picked: PickedFile | PickedFile[]) => void;
|
|
4381
|
+
onRemove?: (file: PickedFile, index: number) => void;
|
|
4382
|
+
/**
|
|
4383
|
+
* Files that were not added: `"type"` for ones `accept` ruled out, `"count"`
|
|
4384
|
+
* for the files beyond the first dropped on a field without `multiple`.
|
|
4385
|
+
*/
|
|
4386
|
+
onReject?: (files: PickedFile[], reason: "type" | "count") => void;
|
|
4387
|
+
/** Shows the uploading state and locks the field. */
|
|
4388
|
+
uploading?: boolean;
|
|
4389
|
+
/** Default "Choose file", or "Choose files" when `multiple`. */
|
|
4390
|
+
buttonLabel?: string;
|
|
4391
|
+
/** Default "outlined". */
|
|
4392
|
+
buttonVariant?: "primary" | "secondary" | "outlined";
|
|
4393
|
+
/** Default "Uploading…". */
|
|
4394
|
+
uploadingLabel?: string;
|
|
4395
|
+
/** Shown while nothing is chosen, where no drop is possible. */
|
|
4396
|
+
emptyLabel?: string;
|
|
4397
|
+
/** Web only: the drop hint beside the button. */
|
|
4398
|
+
dropLabel?: string;
|
|
4399
|
+
/** The error a ruled-out file shows. */
|
|
4400
|
+
rejectedMessage?: string;
|
|
4401
|
+
/** Shown when several files reach a field without `multiple`. */
|
|
4402
|
+
singleFileMessage?: string;
|
|
4403
|
+
/** Replaces the helper on a host with no file picker. */
|
|
4404
|
+
unsupportedMessage?: string;
|
|
4405
|
+
/** Prefixed to each file's name. Defaults to the app's own "Remove". */
|
|
4406
|
+
removeAccessibilityLabel?: string;
|
|
4407
|
+
/** The field. */
|
|
4408
|
+
style?: any;
|
|
4409
|
+
containerStyle?: any;
|
|
4410
|
+
}) => any;
|
|
4411
|
+
|
|
4266
4412
|
/**
|
|
4267
4413
|
* Themed collapsible sections. The surface comes from the theme's `card`
|
|
4268
4414
|
* scope and the headers and dividers from its `list` scope, so every
|
package/dist/index.js
CHANGED
|
@@ -291,6 +291,8 @@ export { Modal } from "./modal.js";
|
|
|
291
291
|
export { ConfirmDialog } from "./confirm-dialog.js";
|
|
292
292
|
// Themed filter chips, tags and pills; follow the theme's `chip` scope.
|
|
293
293
|
export { Chip, ChipGroup } from "./chip.js";
|
|
294
|
+
// A themed file chooser on `<FilePicker>`; follows the theme's `muted` scope.
|
|
295
|
+
export { FileUpload } from "./file-upload.js";
|
|
294
296
|
// The semantic text tiers, shared with the built-in widgets.
|
|
295
297
|
export {
|
|
296
298
|
textRoleStyle,
|
package/dist/index.native.js
CHANGED
|
@@ -284,6 +284,8 @@ export { Modal } from "./modal.native.js";
|
|
|
284
284
|
export { ConfirmDialog } from "./confirm-dialog.native.js";
|
|
285
285
|
// Themed filter chips, tags and pills; follow the theme's `chip` scope.
|
|
286
286
|
export { Chip, ChipGroup } from "./chip.native.js";
|
|
287
|
+
// A themed file chooser on `<FilePicker>`; follows the theme's `muted` scope.
|
|
288
|
+
export { FileUpload } from "./file-upload.native.js";
|
|
287
289
|
// The semantic text tiers, shared with the built-in widgets.
|
|
288
290
|
export {
|
|
289
291
|
textRoleStyle,
|
package/dist/list-tokens.js
CHANGED
|
@@ -27,43 +27,60 @@ function isSet(value) {
|
|
|
27
27
|
return value !== undefined && value !== null && value !== "";
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
// The ways `<List>` can draw its rows: flush bands / tiles, or one card each.
|
|
31
|
+
export const LIST_VARIANTS = Object.freeze(["plain", "card"]);
|
|
32
|
+
|
|
33
|
+
/** A `variant` value as `<List>` reads it; anything unknown is "plain". */
|
|
34
|
+
export function listVariant(value) {
|
|
35
|
+
return LIST_VARIANTS.includes(value) ? value : "plain";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function pick(name, ...layers) {
|
|
39
|
+
for (const layer of layers) {
|
|
40
|
+
if (layer && isSet(layer[name])) return layer[name];
|
|
41
|
+
}
|
|
33
42
|
return undefined;
|
|
34
43
|
}
|
|
35
44
|
|
|
45
|
+
function boundedList(values) {
|
|
46
|
+
return values ? normaliseThemeComponents({ list: values }).list : undefined;
|
|
47
|
+
}
|
|
48
|
+
|
|
36
49
|
/**
|
|
37
50
|
* Resolve one list's row values: explicit override, then the theme's `list`
|
|
38
|
-
* scope, then the
|
|
39
|
-
* a hand-edited or planned value cannot
|
|
51
|
+
* scope, then the widget's own `defaults`, then the theme tokens. Every layer
|
|
52
|
+
* passes the contract's own bounds, so a hand-edited or planned value cannot
|
|
53
|
+
* reach a native style unvalidated.
|
|
40
54
|
* Unthemed, a row is the flush band with a 1px hairline the Data List drew;
|
|
41
55
|
* a `gap` makes separate tiles on the surface, outlined by the divider.
|
|
42
56
|
*
|
|
43
57
|
* @param {object} theme — the resolved workspace theme (`useTheme()` shape).
|
|
44
58
|
* @param {object} [overrides] — per-list values keyed by LIST_TOKEN_NAMES.
|
|
59
|
+
* @param {object} [defaults] — a widget's own row look, keyed the same way,
|
|
60
|
+
* used only where the theme's Lists settings leave a value unset.
|
|
45
61
|
*/
|
|
46
|
-
export function resolveListTokens(theme, overrides) {
|
|
62
|
+
export function resolveListTokens(theme, overrides, defaults) {
|
|
47
63
|
const base = theme && typeof theme === "object" ? theme : DEFAULT_THEME_TOKENS;
|
|
48
64
|
const fallback = DEFAULT_THEME_TOKENS;
|
|
49
65
|
const colors = base.colors || fallback.colors;
|
|
50
66
|
const spacing = base.spacing || fallback.spacing;
|
|
51
67
|
const scope = base.components && base.components.list;
|
|
52
|
-
const
|
|
68
|
+
const layers = [boundedList(overrides), scope, boundedList(defaults)];
|
|
69
|
+
const value = (name) => pick(name, ...layers);
|
|
53
70
|
|
|
54
|
-
const gap =
|
|
71
|
+
const gap = value("gap") ?? 0;
|
|
55
72
|
const separated = gap > 0;
|
|
56
|
-
const background =
|
|
73
|
+
const background = value("background") ?? (separated ? colors.surface : undefined);
|
|
57
74
|
return {
|
|
58
|
-
rowPadding:
|
|
59
|
-
rowMinHeight:
|
|
75
|
+
rowPadding: value("rowPadding") ?? spacing.md,
|
|
76
|
+
rowMinHeight: value("rowMinHeight") ?? LIST_ROW_MIN_HEIGHT,
|
|
60
77
|
gap,
|
|
61
78
|
separated,
|
|
62
79
|
// A flush band has square ends; a divider would curve into round ones.
|
|
63
|
-
radius: separated ?
|
|
80
|
+
radius: separated ? value("radius") ?? 0 : 0,
|
|
64
81
|
background,
|
|
65
|
-
dividerColor:
|
|
66
|
-
dividerWidth:
|
|
82
|
+
dividerColor: value("dividerColor") ?? colors.border,
|
|
83
|
+
dividerWidth: value("dividerWidth") ?? 1,
|
|
67
84
|
selectedBackground: colors.primarySoft || colors.surfaceMuted,
|
|
68
85
|
selectedAccent: colors.primary,
|
|
69
86
|
pressedBackground: mixHex(colors.onSurface, background || colors.surface, 0.94),
|
|
@@ -97,3 +114,26 @@ export function listRowFrame(tokens, spacing, selected = false) {
|
|
|
97
114
|
const inset = tokens.rowPadding + listSideEdgeWidth(tokens) - LIST_SELECTED_BAR_WIDTH;
|
|
98
115
|
return { ...frame, paddingLeft: Math.max(0, inset) };
|
|
99
116
|
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* The row frame of a card-style list. The card scope paints the surface
|
|
120
|
+
* (fill, border, radius, padding, shadow, gradient); the list scope keeps
|
|
121
|
+
* only what is inside it: the height and the slots' arrangement.
|
|
122
|
+
*
|
|
123
|
+
* @param {ReturnType<typeof resolveListTokens>} tokens
|
|
124
|
+
* @param {{ md: number }} spacing — the theme's spacing scale.
|
|
125
|
+
* @param {boolean} [pressable] — a tap target keeps the 44px floor.
|
|
126
|
+
*/
|
|
127
|
+
export function listCardRowFrame(tokens, spacing, pressable = false) {
|
|
128
|
+
return {
|
|
129
|
+
flexDirection: "row",
|
|
130
|
+
alignItems: "center",
|
|
131
|
+
gap: spacing.md,
|
|
132
|
+
minHeight: pressable ? Math.max(tokens.rowMinHeight, LIST_ROW_MIN_HEIGHT) : tokens.rowMinHeight,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** The space between the cards of a card-style list: the Lists gap, else `spacing.sm`. */
|
|
137
|
+
export function listCardGap(tokens, spacing) {
|
|
138
|
+
return tokens.gap > 0 ? tokens.gap : spacing.sm;
|
|
139
|
+
}
|