@colixsystems/widget-sdk 0.247.0 → 0.248.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 +53 -5
- package/dist/board-auto-scroll.js +59 -0
- package/dist/board-view.js +327 -0
- package/dist/contract.cjs +70 -2
- package/dist/contract.js +70 -2
- package/dist/data-table-logic.js +179 -0
- package/dist/data-table-view.js +388 -0
- package/dist/data-table.js +22 -0
- package/dist/data-table.native.js +14 -0
- package/dist/drag-kit-view.js +310 -0
- package/dist/file-upload-view.js +13 -2
- package/dist/filter-bar-logic.js +143 -0
- package/dist/filter-bar-view.js +381 -0
- package/dist/filter-bar.js +15 -0
- package/dist/filter-bar.native.js +13 -0
- package/dist/form-control-logic.js +20 -3
- package/dist/form-control-view.js +18 -0
- package/dist/form-layout-logic.js +92 -0
- package/dist/form-view.js +268 -0
- package/dist/form.js +15 -0
- package/dist/form.native.js +9 -0
- package/dist/index.d.ts +372 -5
- package/dist/index.js +8 -0
- package/dist/index.native.js +8 -0
- package/dist/menu-view.js +5 -2
- package/dist/people-picker-view.js +3 -0
- package/dist/radio-group-view.js +7 -3
- package/dist/select-view.js +7 -2
- package/dist/sortable-list-view.js +228 -0
- package/dist/sortable-logic.js +273 -0
- package/dist/sortable-tokens.js +120 -0
- package/dist/sortable-view.js +24 -0
- package/dist/sortable.js +39 -0
- package/dist/sortable.native.js +30 -0
- package/dist/text-field-view.js +7 -2
- package/package.json +1 -1
- package/src/contract.js +70 -2
package/dist/index.d.ts
CHANGED
|
@@ -2460,6 +2460,106 @@ export const Grid: (props: {
|
|
|
2460
2460
|
accessibilityLabel?: string;
|
|
2461
2461
|
}) => any;
|
|
2462
2462
|
|
|
2463
|
+
/**
|
|
2464
|
+
* `required` on a control that frames its own label (`<TextField>`,
|
|
2465
|
+
* `<Select>`, `<RadioGroup>`, `<PeoplePicker>`, `<FileUpload>`, `<FormField>`).
|
|
2466
|
+
*/
|
|
2467
|
+
export interface FieldRequiredProps {
|
|
2468
|
+
/**
|
|
2469
|
+
* Draws a mark after a text label and adds the app's "Required" to the
|
|
2470
|
+
* control's spoken name. It does not validate: pass `error` yourself.
|
|
2471
|
+
*/
|
|
2472
|
+
required?: boolean;
|
|
2473
|
+
}
|
|
2474
|
+
|
|
2475
|
+
/**
|
|
2476
|
+
* A form laid out in width tiers from its own measured width, capped near
|
|
2477
|
+
* 1000px and centred: its fields one to a line when narrow, two at medium and
|
|
2478
|
+
* `columns` (three for `"auto"`) when wide. A `<FormField>` or a control that
|
|
2479
|
+
* frames its own label (`<TextField>`, `<Select>`, `<RadioGroup>`,
|
|
2480
|
+
* `<PeoplePicker>`, `<FileUpload>`) is one field; a multiline field, a `<FormField span="full">`,
|
|
2481
|
+
* a `<FieldRow>`, a `<FormSection>`, a `<FormActions>` and anything else take
|
|
2482
|
+
* the whole line. Children are read directly, not through a fragment.
|
|
2483
|
+
*
|
|
2484
|
+
* @example
|
|
2485
|
+
* <Form>
|
|
2486
|
+
* <FormSection title="Your details">
|
|
2487
|
+
* <FieldRow>
|
|
2488
|
+
* <TextField label="First name" value={first} onChangeText={setFirst} />
|
|
2489
|
+
* <TextField label="Last name" value={last} onChangeText={setLast} />
|
|
2490
|
+
* </FieldRow>
|
|
2491
|
+
* <TextField label="Email" required value={email} onChangeText={setEmail} />
|
|
2492
|
+
* <FormField label="Colour"><SwatchPicker value={colour} onChange={setColour} /></FormField>
|
|
2493
|
+
* <TextField label="Notes" multiline value={notes} onChangeText={setNotes} />
|
|
2494
|
+
* </FormSection>
|
|
2495
|
+
* <FormActions>
|
|
2496
|
+
* <Button variant="secondary" title="Cancel" onPress={cancel} />
|
|
2497
|
+
* <Button title="Save" onPress={save} />
|
|
2498
|
+
* </FormActions>
|
|
2499
|
+
* </Form>
|
|
2500
|
+
*/
|
|
2501
|
+
export const Form: (props: {
|
|
2502
|
+
children?: ReactNode;
|
|
2503
|
+
/** The most fields to a line when wide, 1–3. Default "auto" (three). */
|
|
2504
|
+
columns?: "auto" | 1 | 2 | 3;
|
|
2505
|
+
/** The cap the form centres within. Default 1000; `null` for none. */
|
|
2506
|
+
maxWidth?: number | null;
|
|
2507
|
+
/** Between fields and lines; `"md"` when unset. */
|
|
2508
|
+
gap?: GridGap;
|
|
2509
|
+
/** The form's outer box; padding here stays out of the measured width. */
|
|
2510
|
+
style?: any;
|
|
2511
|
+
testID?: string;
|
|
2512
|
+
/** Names the form as one group to a screen reader. */
|
|
2513
|
+
accessibilityLabel?: string;
|
|
2514
|
+
}) => any;
|
|
2515
|
+
|
|
2516
|
+
/** Short related fields on one line (up to three); stacked when narrow. */
|
|
2517
|
+
export const FieldRow: (props: {
|
|
2518
|
+
children?: ReactNode;
|
|
2519
|
+
gap?: GridGap;
|
|
2520
|
+
style?: any;
|
|
2521
|
+
testID?: string;
|
|
2522
|
+
}) => any;
|
|
2523
|
+
|
|
2524
|
+
/** A titled group of fields, laid out in the same columns as its form. */
|
|
2525
|
+
export const FormSection: (props: {
|
|
2526
|
+
/** A heading for screen readers too. */
|
|
2527
|
+
title?: ReactNode;
|
|
2528
|
+
description?: ReactNode;
|
|
2529
|
+
children?: ReactNode;
|
|
2530
|
+
gap?: GridGap;
|
|
2531
|
+
style?: any;
|
|
2532
|
+
testID?: string;
|
|
2533
|
+
}) => any;
|
|
2534
|
+
|
|
2535
|
+
/**
|
|
2536
|
+
* The form's buttons in normal flow after its fields: the secondary first,
|
|
2537
|
+
* the primary last. A row at the trailing edge at medium and wide; a
|
|
2538
|
+
* full-width stack in the same order when narrow.
|
|
2539
|
+
*/
|
|
2540
|
+
export const FormActions: (props: {
|
|
2541
|
+
children?: ReactNode;
|
|
2542
|
+
style?: any;
|
|
2543
|
+
testID?: string;
|
|
2544
|
+
}) => any;
|
|
2545
|
+
|
|
2546
|
+
/**
|
|
2547
|
+
* A label, required mark, helper and error round any control. A control that
|
|
2548
|
+
* frames its own label is handed them instead (a prop it sets itself wins,
|
|
2549
|
+
* `disabled={false}` included), so one label is drawn; `testID` goes to that
|
|
2550
|
+
* control, or to a wrapper round it when it has its own. Any other child is
|
|
2551
|
+
* framed here: on the web it is named as a group described by the note; on a
|
|
2552
|
+
* device the one child takes the label (with "Required") as its name and the
|
|
2553
|
+
* note as its hint, unless it names itself.
|
|
2554
|
+
*/
|
|
2555
|
+
export const FormField: (props: FormControlProps & FieldRequiredProps & {
|
|
2556
|
+
children?: ReactNode;
|
|
2557
|
+
/** Take the form's whole line. */
|
|
2558
|
+
span?: "full";
|
|
2559
|
+
/** The frame round the control. */
|
|
2560
|
+
style?: any;
|
|
2561
|
+
}) => any;
|
|
2562
|
+
|
|
2463
2563
|
/** Pass-through options for `useGeolocation().getCurrentPosition(...)`. */
|
|
2464
2564
|
export interface GeolocationOptions {
|
|
2465
2565
|
enableHighAccuracy?: boolean;
|
|
@@ -3899,6 +3999,97 @@ export const EmptyState: (props: {
|
|
|
3899
3999
|
children?: ReactNode;
|
|
3900
4000
|
}) => any;
|
|
3901
4001
|
|
|
4002
|
+
/** One column of a `<DataTable>`. */
|
|
4003
|
+
export interface DataTableColumn<Row = Record<string, any>> {
|
|
4004
|
+
/** The record field the column shows, and the key its sort names. */
|
|
4005
|
+
key: string;
|
|
4006
|
+
/** The header label, and the label beside the value in the card layouts. Defaults to `key`. */
|
|
4007
|
+
label?: ReactNode;
|
|
4008
|
+
/** The column's share of the row width; the header uses the same. Defaults to 1. */
|
|
4009
|
+
flex?: number;
|
|
4010
|
+
align?: "left" | "center" | "right";
|
|
4011
|
+
/** The narrowest the column may get, in pixels. Defaults to 0. */
|
|
4012
|
+
minWidth?: number;
|
|
4013
|
+
/** Makes the header a sort button. */
|
|
4014
|
+
sortable?: boolean;
|
|
4015
|
+
/** The cell's content; a string or number is drawn as the cell's text. Return `undefined` for the default. */
|
|
4016
|
+
render?: (row: Row, column: DataTableColumn<Row>) => ReactNode;
|
|
4017
|
+
}
|
|
4018
|
+
|
|
4019
|
+
/** The order a `<DataTable>` shows: one column, ascending or descending. */
|
|
4020
|
+
export interface DataTableSort {
|
|
4021
|
+
key: string;
|
|
4022
|
+
direction: "asc" | "desc";
|
|
4023
|
+
}
|
|
4024
|
+
|
|
4025
|
+
/**
|
|
4026
|
+
* A list of records laid out for its own measured width, on the web Player and
|
|
4027
|
+
* the native export alike: stacked label/value cards when narrow, one card per
|
|
4028
|
+
* row at medium (the first column as the title, the rest on one line), and
|
|
4029
|
+
* aligned columns under a sticky header when wide. Its rows are `<List>` /
|
|
4030
|
+
* `<ListRow>` and its header `<ListHeader>`, styled by the theme's Lists
|
|
4031
|
+
* settings (and Cards for the card layouts); it adds no theme scope.
|
|
4032
|
+
*
|
|
4033
|
+
* Sorting: a sortable header is a 44px button named by its column and
|
|
4034
|
+
* described by its sort, and a change of sort is announced once, on both
|
|
4035
|
+
* hosts. With `onSortChange` the widget orders the rows itself (a server
|
|
4036
|
+
* sort: re-query with the new sort) and the table draws `rows` as given;
|
|
4037
|
+
* without it the table sorts them, a `Date` value as a date. `sort` makes the
|
|
4038
|
+
* order controlled, `defaultSort` sets where an uncontrolled table starts; a
|
|
4039
|
+
* controlled `sort` without `onSortChange` is display-only, its headers plain
|
|
4040
|
+
* labels. The card layouts offer the same sort from a menu above the cards.
|
|
4041
|
+
* The sticky header is filled with the rows' Lists background, else the
|
|
4042
|
+
* theme surface.
|
|
4043
|
+
*
|
|
4044
|
+
* @example
|
|
4045
|
+
* const [sort, setSort] = useState({ key: "name", direction: "asc" });
|
|
4046
|
+
* const query = useStableQuery(() => ({ sort: { field: sort.key, dir: sort.direction } }));
|
|
4047
|
+
* const { data, loading } = useDatastoreQuery(tableId, query);
|
|
4048
|
+
* <DataTable
|
|
4049
|
+
* columns={[
|
|
4050
|
+
* { key: "name", label: "Name", flex: 2, sortable: true },
|
|
4051
|
+
* { key: "status", label: "Status", render: (row) => <Badge label={row.status} /> },
|
|
4052
|
+
* { key: "total", label: "Total", align: "right", sortable: true },
|
|
4053
|
+
* ]}
|
|
4054
|
+
* rows={data}
|
|
4055
|
+
* loading={loading}
|
|
4056
|
+
* sort={sort}
|
|
4057
|
+
* onSortChange={setSort}
|
|
4058
|
+
* onRowPress={(row) => open(row.id)}
|
|
4059
|
+
* />
|
|
4060
|
+
*/
|
|
4061
|
+
export const DataTable: <Row = Record<string, any>>(
|
|
4062
|
+
props: ListRowTokens & {
|
|
4063
|
+
columns: Array<DataTableColumn<Row> | string>;
|
|
4064
|
+
rows: Row[];
|
|
4065
|
+
/** The field that keys each row. Defaults to "id". */
|
|
4066
|
+
keyField?: string;
|
|
4067
|
+
/** Every cell's content where its column has no `render`; return `undefined` for the default. */
|
|
4068
|
+
renderCell?: (row: Row, column: DataTableColumn<Row>) => ReactNode;
|
|
4069
|
+
/** Makes every row a button. */
|
|
4070
|
+
onRowPress?: (row: Row) => void;
|
|
4071
|
+
/** The order shown, controlled. */
|
|
4072
|
+
sort?: DataTableSort | null;
|
|
4073
|
+
/** Where an uncontrolled table's order starts. */
|
|
4074
|
+
defaultSort?: DataTableSort;
|
|
4075
|
+
/** Called with the new order; the widget then orders `rows` itself. */
|
|
4076
|
+
onSortChange?: (sort: DataTableSort) => void;
|
|
4077
|
+
/** The rows' scroll box height, in pixels. Defaults to 480. */
|
|
4078
|
+
maxHeight?: number;
|
|
4079
|
+
/** Placeholder rows while `rows` is still empty. */
|
|
4080
|
+
loading?: boolean;
|
|
4081
|
+
/** The empty state's title and message; the message defaults to "No results". */
|
|
4082
|
+
emptyTitle?: ReactNode;
|
|
4083
|
+
emptyMessage?: ReactNode;
|
|
4084
|
+
/** The words a sort is spoken with, in the widget's language. */
|
|
4085
|
+
sortLabels?: { sort?: string; ascending?: string; descending?: string };
|
|
4086
|
+
/** The table's outer box; padding here stays out of the measured width. */
|
|
4087
|
+
style?: any;
|
|
4088
|
+
testID?: string;
|
|
4089
|
+
accessibilityLabel?: string;
|
|
4090
|
+
},
|
|
4091
|
+
) => any;
|
|
4092
|
+
|
|
3902
4093
|
/**
|
|
3903
4094
|
* Themed status pill: a record's state (Active, Paid, Overdue) beside the
|
|
3904
4095
|
* thing it describes. The label always renders; the tone only tints it.
|
|
@@ -4192,7 +4383,7 @@ export interface FormFieldOverrides {
|
|
|
4192
4383
|
* @example
|
|
4193
4384
|
* <TextField label="Name" value={name} onChangeText={setName} placeholder="Your name" />
|
|
4194
4385
|
*/
|
|
4195
|
-
export const TextField: (props: FormControlProps & FormFieldOverrides & {
|
|
4386
|
+
export const TextField: (props: FormControlProps & FieldRequiredProps & FormFieldOverrides & {
|
|
4196
4387
|
multiline?: boolean;
|
|
4197
4388
|
style?: any;
|
|
4198
4389
|
/** The box around a labelled field. */
|
|
@@ -4283,6 +4474,7 @@ export interface SelectSearchProps<V extends string | number = string | number>
|
|
|
4283
4474
|
|
|
4284
4475
|
/** What every `<Select>` takes, in either mode. */
|
|
4285
4476
|
export type SelectBaseProps<V extends string | number = string | number> = FormControlProps &
|
|
4477
|
+
FieldRequiredProps &
|
|
4286
4478
|
FormFieldOverrides &
|
|
4287
4479
|
SelectSearchProps<V> & {
|
|
4288
4480
|
/**
|
|
@@ -4404,6 +4596,80 @@ export const PeoplePicker: (
|
|
|
4404
4596
|
),
|
|
4405
4597
|
) => any;
|
|
4406
4598
|
|
|
4599
|
+
/** One `<FilterBar>` facet: a dropdown of `options` under `key` in the value. */
|
|
4600
|
+
export interface FilterBarFacet<V extends string | number = string | number> {
|
|
4601
|
+
key: string;
|
|
4602
|
+
label: string;
|
|
4603
|
+
options: Array<SelectOption<V>>;
|
|
4604
|
+
/** Several values at once; the facet's value is then a list. */
|
|
4605
|
+
multiple?: boolean;
|
|
4606
|
+
/** Type to narrow the options, as `<Select searchable>`. */
|
|
4607
|
+
searchable?: boolean;
|
|
4608
|
+
}
|
|
4609
|
+
|
|
4610
|
+
/** The applied filters: each facet's value, or a list for a `multiple` one. */
|
|
4611
|
+
export type FilterBarValue = Record<string, string | number | Array<string | number>>;
|
|
4612
|
+
|
|
4613
|
+
/**
|
|
4614
|
+
* Search plus facet filters that lay themselves out for their own measured
|
|
4615
|
+
* width. Medium and wide: one toolbar row of a `<SearchField>`, a compact
|
|
4616
|
+
* `<Select>` per facet ("Status: Any") and the sort. Narrow, or a row whose
|
|
4617
|
+
* dropdowns do not fit: the search field and one "Filters · n" button opening
|
|
4618
|
+
* the facets in a `<Modal>` sheet, where nothing applies until Apply. At every
|
|
4619
|
+
* width each applied facet is a removable `<Chip>` beneath, with a "Clear all".
|
|
4620
|
+
*
|
|
4621
|
+
* `onChange` gets the whole next value once per edit, with a cleared facet's
|
|
4622
|
+
* key removed; `onQueryChange` gets the trimmed query once typing pauses
|
|
4623
|
+
* (`queryDebounceMs`, default 250) and at once on clear or submit. Both are
|
|
4624
|
+
* controlled, so seed `query` and `value` from route params or
|
|
4625
|
+
* `useWidgetRoute`. "Clear all" resets the facets and the query. The search
|
|
4626
|
+
* field shows only with `onQueryChange`. Copy props default to English.
|
|
4627
|
+
*
|
|
4628
|
+
* @example
|
|
4629
|
+
* <FilterBar query={q} onQueryChange={setQ} value={filters} onChange={setFilters}
|
|
4630
|
+
* facets={[{ key: "status", label: "Status", options: [{ value: "open", label: "Open" }] }]}
|
|
4631
|
+
* sort={{ options: [{ value: "new", label: "Newest" }], value: sortBy, onChange: setSortBy }} />
|
|
4632
|
+
*/
|
|
4633
|
+
export const FilterBar: (props: {
|
|
4634
|
+
query?: string;
|
|
4635
|
+
onQueryChange?: (query: string) => void;
|
|
4636
|
+
/** The pause, in ms, before typed text is reported. Default 250. */
|
|
4637
|
+
queryDebounceMs?: number;
|
|
4638
|
+
facets: FilterBarFacet[];
|
|
4639
|
+
value?: FilterBarValue | null;
|
|
4640
|
+
onChange?: (next: FilterBarValue) => void;
|
|
4641
|
+
/** One more dropdown; sorting is never shown as a chip. */
|
|
4642
|
+
sort?: {
|
|
4643
|
+
options: Array<SelectOption>;
|
|
4644
|
+
value?: string | number | null;
|
|
4645
|
+
onChange: (value: string | number) => void;
|
|
4646
|
+
/** Default "Sort". */
|
|
4647
|
+
label?: string;
|
|
4648
|
+
};
|
|
4649
|
+
/** Default "Search". */
|
|
4650
|
+
searchPlaceholder?: string;
|
|
4651
|
+
/** A facet with nothing picked. Default "Any". */
|
|
4652
|
+
anyText?: string;
|
|
4653
|
+
/** The narrow button and the sheet's title. Default "Filters". */
|
|
4654
|
+
filtersText?: string;
|
|
4655
|
+
/** Spoken after the button's name; `{count}` is the number applied. Default "{count} active". */
|
|
4656
|
+
activeCountText?: string | ((count: number) => string);
|
|
4657
|
+
/** Default "Clear all". */
|
|
4658
|
+
clearAllText?: string;
|
|
4659
|
+
/** Default "Apply". */
|
|
4660
|
+
applyText?: string;
|
|
4661
|
+
/** Empties the sheet's facets and puts its sort on the first option. Default "Clear". */
|
|
4662
|
+
clearText?: string;
|
|
4663
|
+
/** Names the chip group. Default "Active filters". */
|
|
4664
|
+
activeFiltersText?: string;
|
|
4665
|
+
/** A chip's remove control; `{label}` is the chip. Default "Remove {label}". */
|
|
4666
|
+
removeText?: string | ((label: string) => string);
|
|
4667
|
+
closeAccessibilityLabel?: string;
|
|
4668
|
+
/** The outer box; its padding is not counted into the measured width. */
|
|
4669
|
+
style?: any;
|
|
4670
|
+
testID?: string;
|
|
4671
|
+
}) => any;
|
|
4672
|
+
|
|
4407
4673
|
/**
|
|
4408
4674
|
* Space-key activation for a control whose role is not a button: on the web a
|
|
4409
4675
|
* `Pressable` answers Space only for a button. Native ignores `onKeyDown`.
|
|
@@ -4476,7 +4742,7 @@ export interface RadioOption<V extends string | number = string | number> {
|
|
|
4476
4742
|
* options={[{ value: "standard", label: "Standard" }, { value: "express", label: "Express" }]} />
|
|
4477
4743
|
*/
|
|
4478
4744
|
export const RadioGroup: <V extends string | number = string | number>(
|
|
4479
|
-
props: FormControlProps & {
|
|
4745
|
+
props: FormControlProps & FieldRequiredProps & {
|
|
4480
4746
|
options: Array<RadioOption<V>>;
|
|
4481
4747
|
value?: V | null;
|
|
4482
4748
|
/** Called when a different option is picked. */
|
|
@@ -4731,8 +4997,8 @@ export const FilePicker: ((props: {
|
|
|
4731
4997
|
/**
|
|
4732
4998
|
* Themed file chooser on `<FilePicker>`: a button (the `button` scope) in the
|
|
4733
4999
|
* input field (the `muted` scope), the chosen files with their size and a
|
|
4734
|
-
* remove control, and the form controls' `label` / `
|
|
4735
|
-
* `disabled`. On the web a file can also be dropped onto the field; native
|
|
5000
|
+
* remove control, and the form controls' `label` / `required` / `helper` /
|
|
5001
|
+
* `error` / `disabled`. On the web a file can also be dropped onto the field; native
|
|
4736
5002
|
* has no file drop, so the button is the control there. A file `accept` rules
|
|
4737
5003
|
* out is not added and shows an error. It uploads nothing: upload in
|
|
4738
5004
|
* `onPick` with `useFilestoreUpload` and pass `uploading` while it runs, which
|
|
@@ -4745,7 +5011,7 @@ export const FilePicker: ((props: {
|
|
|
4745
5011
|
* files={photos} onFilesChange={setPhotos}
|
|
4746
5012
|
* onPick={(picked) => Promise.all(picked.map((f) => upload(f))).then(() => setPhotos([]))} />
|
|
4747
5013
|
*/
|
|
4748
|
-
export const FileUpload: (props: FormControlProps & {
|
|
5014
|
+
export const FileUpload: (props: FormControlProps & FieldRequiredProps & {
|
|
4749
5015
|
accept?: string;
|
|
4750
5016
|
/** A new pick joins the list instead of replacing the file. Default false. */
|
|
4751
5017
|
multiple?: boolean;
|
|
@@ -4890,6 +5156,107 @@ export const Tooltip: (props: {
|
|
|
4890
5156
|
children: ReactNode;
|
|
4891
5157
|
}) => any;
|
|
4892
5158
|
|
|
5159
|
+
/**
|
|
5160
|
+
* The words `<SortableList>` and `<Board>` show and announce. `{item}`,
|
|
5161
|
+
* `{position}`, `{total}` and `{column}` are filled in; a blank entry keeps
|
|
5162
|
+
* the English default.
|
|
5163
|
+
*/
|
|
5164
|
+
export interface SortableLabels {
|
|
5165
|
+
/** The handle's name and its options' title. Default "Move {item}". */
|
|
5166
|
+
handle?: string;
|
|
5167
|
+
hint?: string;
|
|
5168
|
+
moveUp?: string;
|
|
5169
|
+
moveDown?: string;
|
|
5170
|
+
moveToTop?: string;
|
|
5171
|
+
moveToBottom?: string;
|
|
5172
|
+
/** Default "Move to {column}". */
|
|
5173
|
+
moveToColumn?: string;
|
|
5174
|
+
/** Default "{item} moved to position {position} of {total}". */
|
|
5175
|
+
moved?: string;
|
|
5176
|
+
/** Default "{item} moved to {column}, position {position}". */
|
|
5177
|
+
movedToColumn?: string;
|
|
5178
|
+
}
|
|
5179
|
+
|
|
5180
|
+
/**
|
|
5181
|
+
* A list the user reorders by dragging a row's grip handle, on both hosts.
|
|
5182
|
+
* The rows are `<ListRow>`s in the theme's `list` scope. A tap or Enter on
|
|
5183
|
+
* the handle opens its move options, the arrow keys move a focused handle on
|
|
5184
|
+
* the web, and a screen reader is offered the same moves as actions.
|
|
5185
|
+
* `onReorder` runs once per real change; a drag that changes nothing springs
|
|
5186
|
+
* back and calls nothing.
|
|
5187
|
+
*
|
|
5188
|
+
* @example
|
|
5189
|
+
* <SortableList
|
|
5190
|
+
* items={steps}
|
|
5191
|
+
* onReorder={(next) => setSteps(next)}
|
|
5192
|
+
* renderItem={({ item }) => <Text>{item.title}</Text>}
|
|
5193
|
+
* />
|
|
5194
|
+
*/
|
|
5195
|
+
export function SortableList<T>(props: {
|
|
5196
|
+
items: T[];
|
|
5197
|
+
onReorder?: (nextItems: T[], move: { from: number; to: number; item: T }) => void;
|
|
5198
|
+
/** A row's content; with none the item's title, name or label shows. */
|
|
5199
|
+
renderItem?: (row: { item: T; index: number; dragging: boolean }) => ReactNode;
|
|
5200
|
+
/** Default: the item's `id`, then `key`, then its position. */
|
|
5201
|
+
keyExtractor?: (item: T, index: number) => string | number;
|
|
5202
|
+
/** The name a move announces. Default: its `title`, `name` or `label`. */
|
|
5203
|
+
getItemLabel?: (item: T) => string;
|
|
5204
|
+
/** Read on every render; a row it refuses shows no handle. Default true. */
|
|
5205
|
+
canDrag?: boolean | ((item: T) => boolean);
|
|
5206
|
+
/** "card" draws each row as the app's card. Default "plain". */
|
|
5207
|
+
variant?: "plain" | "card";
|
|
5208
|
+
labels?: SortableLabels;
|
|
5209
|
+
style?: any;
|
|
5210
|
+
testID?: string;
|
|
5211
|
+
accessibilityLabel?: string;
|
|
5212
|
+
}): any;
|
|
5213
|
+
|
|
5214
|
+
/** One `<Board>` column: the key `getColumn` returns, and its heading. */
|
|
5215
|
+
export interface BoardColumn {
|
|
5216
|
+
key: string | number;
|
|
5217
|
+
title: string;
|
|
5218
|
+
}
|
|
5219
|
+
|
|
5220
|
+
/**
|
|
5221
|
+
* Cards the user moves between columns (a kanban, a pipeline) by dragging a
|
|
5222
|
+
* card's grip handle, on both hosts. Each card is the app's `<Card>`. The one
|
|
5223
|
+
* column a held card is over takes the theme's soft primary, with a line
|
|
5224
|
+
* where it will land; a narrow board scrolls its columns sideways, and by
|
|
5225
|
+
* itself while a card is held at its edge. Move options, arrow keys and
|
|
5226
|
+
* screen-reader actions move a card without dragging.
|
|
5227
|
+
*
|
|
5228
|
+
* @example
|
|
5229
|
+
* <Board
|
|
5230
|
+
* columns={[{ key: "todo", title: "To do" }, { key: "done", title: "Done" }]}
|
|
5231
|
+
* items={rows}
|
|
5232
|
+
* getColumn={(row) => row.status}
|
|
5233
|
+
* onMove={(row, { toColumn }) => move(row, toColumn)}
|
|
5234
|
+
* renderCard={({ item }) => <Text>{item.title}</Text>}
|
|
5235
|
+
* />
|
|
5236
|
+
*/
|
|
5237
|
+
export function Board<T>(props: {
|
|
5238
|
+
columns: BoardColumn[];
|
|
5239
|
+
items: T[];
|
|
5240
|
+
/** The key of the column an item belongs in; an unknown key hides it. */
|
|
5241
|
+
getColumn: (item: T) => string | number;
|
|
5242
|
+
/** `index` is its place in the target column. */
|
|
5243
|
+
onMove?: (item: T, move: { fromColumn: string | number; toColumn: string | number; index: number }) => void;
|
|
5244
|
+
/** A card's content; with none the item's title, name or label shows. */
|
|
5245
|
+
renderCard?: (card: { item: T; index: number; column: string | number; dragging: boolean }) => ReactNode;
|
|
5246
|
+
keyExtractor?: (item: T, index: number) => string | number;
|
|
5247
|
+
getItemLabel?: (item: T) => string;
|
|
5248
|
+
/** Read on every render; a card it refuses shows no handle. Default true. */
|
|
5249
|
+
canDrag?: boolean | ((item: T) => boolean);
|
|
5250
|
+
/** False when cards keep no order of their own. Default true. */
|
|
5251
|
+
reorderInColumn?: boolean;
|
|
5252
|
+
/** A column's width when a narrow board scrolls sideways. Default 280. */
|
|
5253
|
+
columnWidth?: number;
|
|
5254
|
+
labels?: SortableLabels;
|
|
5255
|
+
style?: any;
|
|
5256
|
+
testID?: string;
|
|
5257
|
+
accessibilityLabel?: string;
|
|
5258
|
+
}): any;
|
|
5259
|
+
|
|
4893
5260
|
/**
|
|
4894
5261
|
* Themed collapsible sections. The surface comes from the theme's `card`
|
|
4895
5262
|
* scope and the headers and dividers from its `list` scope, so every
|
package/dist/index.js
CHANGED
|
@@ -114,6 +114,8 @@ export {
|
|
|
114
114
|
} from "./container-width.js";
|
|
115
115
|
// Balanced lines of cells sized from the grid's own measured width.
|
|
116
116
|
export { Grid } from "./grid.js";
|
|
117
|
+
// A form laid out in width tiers: fields, paired rows, sections, actions.
|
|
118
|
+
export { Form, FieldRow, FormSection, FormActions, FormField } from "./form.js";
|
|
117
119
|
// REQ-WSDK-PLATFORM §6 — Tier A hooks. Each ships in a per-platform file
|
|
118
120
|
// (./clipboard.js / .native.js, ./toast.js / .native.js); index.js picks
|
|
119
121
|
// the web variant and index.native.js picks the native variant.
|
|
@@ -239,6 +241,8 @@ export {
|
|
|
239
241
|
export { insertAtCaret, pushRecentEmoji } from "./emoji-picker-tokens.js";
|
|
240
242
|
// Themed list building blocks; rows follow the theme's `list` scope.
|
|
241
243
|
export { List, ListRow, ListHeader, EmptyState } from "./list.js";
|
|
244
|
+
// A record list in three width tiers: stacked cards, cards, sticky-header columns.
|
|
245
|
+
export { DataTable } from "./data-table.js";
|
|
242
246
|
// Themed detail parts; the stock Badge/Avatar/Progress/Stat/Divider wrap these.
|
|
243
247
|
export { Badge, Avatar, Progress, Stat, Divider } from "./detail-parts.js";
|
|
244
248
|
export { initialsFor } from "./initials.js";
|
|
@@ -284,6 +288,8 @@ export { Select } from "./select.js";
|
|
|
284
288
|
export { SearchField } from "./search-field.js";
|
|
285
289
|
// A searchable `<Select>` of users, groups or one table's records.
|
|
286
290
|
export { PeoplePicker } from "./people-picker.js";
|
|
291
|
+
// Search plus facet dropdowns, one toolbar row or a sheet by width.
|
|
292
|
+
export { FilterBar } from "./filter-bar.js";
|
|
287
293
|
// Pick one of a short visible list; `muted` scope, or `tabs` when segmented.
|
|
288
294
|
export { RadioGroup } from "./radio-group.js";
|
|
289
295
|
// Space for a control whose role is not a button; a no-op on native.
|
|
@@ -307,6 +313,8 @@ export { chartColors } from "./chart-tokens.js";
|
|
|
307
313
|
export { FileUpload } from "./file-upload.js";
|
|
308
314
|
// Anchored actions, content and labels; follow the theme's `popover` scope.
|
|
309
315
|
export { Menu, Popover, Tooltip } from "./popover.js";
|
|
316
|
+
// Rows reordered and cards moved between columns, by drag or by move options.
|
|
317
|
+
export { SortableList, Board } from "./sortable.js";
|
|
310
318
|
// The semantic text tiers, shared with the built-in widgets.
|
|
311
319
|
export {
|
|
312
320
|
textRoleStyle,
|
package/dist/index.native.js
CHANGED
|
@@ -114,6 +114,8 @@ export {
|
|
|
114
114
|
} from "./container-width.js";
|
|
115
115
|
// Balanced lines of cells sized from the grid's own measured width.
|
|
116
116
|
export { Grid } from "./grid.native.js";
|
|
117
|
+
// A form laid out in width tiers: fields, paired rows, sections, actions.
|
|
118
|
+
export { Form, FieldRow, FormSection, FormActions, FormField } from "./form.native.js";
|
|
117
119
|
// REQ-WSDK-PLATFORM §6 — Tier A hooks (native variants).
|
|
118
120
|
export { useClipboard, ClipboardError } from "./clipboard.native.js";
|
|
119
121
|
export { useToast } from "./toast.native.js";
|
|
@@ -232,6 +234,8 @@ export {
|
|
|
232
234
|
export { insertAtCaret, pushRecentEmoji } from "./emoji-picker-tokens.js";
|
|
233
235
|
// Themed list building blocks; rows follow the theme's `list` scope.
|
|
234
236
|
export { List, ListRow, ListHeader, EmptyState } from "./list.native.js";
|
|
237
|
+
// A record list in three width tiers: stacked cards, cards, sticky-header columns.
|
|
238
|
+
export { DataTable } from "./data-table.native.js";
|
|
235
239
|
// Themed detail parts; the stock Badge/Avatar/Progress/Stat/Divider wrap these.
|
|
236
240
|
export { Badge, Avatar, Progress, Stat, Divider } from "./detail-parts.native.js";
|
|
237
241
|
export { initialsFor } from "./initials.js";
|
|
@@ -277,6 +281,8 @@ export { Select } from "./select.native.js";
|
|
|
277
281
|
export { SearchField } from "./search-field.native.js";
|
|
278
282
|
// A searchable `<Select>` of users, groups or one table's records.
|
|
279
283
|
export { PeoplePicker } from "./people-picker.native.js";
|
|
284
|
+
// Search plus facet dropdowns, one toolbar row or a sheet by width.
|
|
285
|
+
export { FilterBar } from "./filter-bar.native.js";
|
|
280
286
|
// Pick one of a short visible list; `muted` scope, or `tabs` when segmented.
|
|
281
287
|
export { RadioGroup } from "./radio-group.native.js";
|
|
282
288
|
// Space for a control whose role is not a button; a no-op on native.
|
|
@@ -300,6 +306,8 @@ export { chartColors } from "./chart-tokens.js";
|
|
|
300
306
|
export { FileUpload } from "./file-upload.native.js";
|
|
301
307
|
// Anchored actions, content and labels; follow the theme's `popover` scope.
|
|
302
308
|
export { Menu, Popover, Tooltip } from "./popover.native.js";
|
|
309
|
+
// Rows reordered and cards moved between columns, by drag or by move options.
|
|
310
|
+
export { SortableList, Board } from "./sortable.native.js";
|
|
303
311
|
// The semantic text tiers, shared with the built-in widgets.
|
|
304
312
|
export {
|
|
305
313
|
textRoleStyle,
|
package/dist/menu-view.js
CHANGED
|
@@ -140,8 +140,11 @@ export function makeMenu(rn, AnchoredSurface) {
|
|
|
140
140
|
null,
|
|
141
141
|
triggerWith(trigger, {
|
|
142
142
|
anchorRef,
|
|
143
|
-
|
|
144
|
-
|
|
143
|
+
// An empty menu never opens: expanded with nothing shown misleads a reader.
|
|
144
|
+
onOpen: () => {
|
|
145
|
+
if (list.length > 0) setOpen(true);
|
|
146
|
+
},
|
|
147
|
+
extraProps: { "aria-haspopup": "menu", "aria-expanded": open && list.length > 0 },
|
|
145
148
|
}),
|
|
146
149
|
h(
|
|
147
150
|
AnchoredSurface,
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
settleLookup,
|
|
32
32
|
unresolvedIds,
|
|
33
33
|
} from "./people-picker-logic.js";
|
|
34
|
+
import { DRAWS_FIELD_FRAME } from "./form-control-logic.js";
|
|
34
35
|
|
|
35
36
|
const h = React.createElement;
|
|
36
37
|
const NO_ROWS = Object.freeze([]);
|
|
@@ -243,5 +244,7 @@ export function makePeoplePicker(rn) {
|
|
|
243
244
|
});
|
|
244
245
|
|
|
245
246
|
PeoplePicker.displayName = "PeoplePicker";
|
|
247
|
+
// Its `<Select>` frames the label, so `required` passes straight through.
|
|
248
|
+
PeoplePicker[DRAWS_FIELD_FRAME] = true;
|
|
246
249
|
return PeoplePicker;
|
|
247
250
|
}
|
package/dist/radio-group-view.js
CHANGED
|
@@ -6,11 +6,12 @@
|
|
|
6
6
|
// an icon or a tab role. This one has the radio role and the app's look.
|
|
7
7
|
|
|
8
8
|
import React from "react";
|
|
9
|
-
import { useHostTheme } from "./hooks.js";
|
|
9
|
+
import { useHostSharedText, useHostTheme } from "./hooks.js";
|
|
10
10
|
import { DEFAULT_THEME_TOKENS } from "./_theme-tokens.js";
|
|
11
11
|
import { Icon } from "./icon.js";
|
|
12
12
|
import { CHECKBOX_SIZE, CONTROL_MIN_TARGET, focusHaloStyle } from "./form-control-tokens.js";
|
|
13
13
|
import {
|
|
14
|
+
DRAWS_FIELD_FRAME,
|
|
14
15
|
errorMessage,
|
|
15
16
|
hasNote,
|
|
16
17
|
isShown,
|
|
@@ -165,11 +166,13 @@ export function makeRadioGroup(rn) {
|
|
|
165
166
|
helper,
|
|
166
167
|
error,
|
|
167
168
|
disabled = false,
|
|
169
|
+
required = false,
|
|
168
170
|
accessibilityLabel,
|
|
169
171
|
style,
|
|
170
172
|
containerStyle,
|
|
171
173
|
...rest
|
|
172
174
|
} = props;
|
|
175
|
+
const requiredWord = useHostSharedText("required", "Required");
|
|
173
176
|
const theme = useHostTheme() || DEFAULT_THEME_TOKENS;
|
|
174
177
|
const t = resolveRadioGroupTokens(theme);
|
|
175
178
|
const kind = radioGroupVariant(variant);
|
|
@@ -236,7 +239,7 @@ export function makeRadioGroup(rn) {
|
|
|
236
239
|
...rest,
|
|
237
240
|
ref,
|
|
238
241
|
accessibilityRole: "radiogroup",
|
|
239
|
-
accessibilityLabel: spokenLabel(accessibilityLabel, label),
|
|
242
|
+
accessibilityLabel: spokenLabel(accessibilityLabel, label, required ? requiredWord : null),
|
|
240
243
|
"aria-disabled": disabled,
|
|
241
244
|
...(invalid ? { "aria-invalid": true } : null),
|
|
242
245
|
...(noted ? { "aria-describedby": noteId } : null),
|
|
@@ -252,11 +255,12 @@ export function makeRadioGroup(rn) {
|
|
|
252
255
|
if (!framed) return group;
|
|
253
256
|
return h(
|
|
254
257
|
FieldFrame,
|
|
255
|
-
{ t, label, helper, error: errorMessage(error), disabled, noteId, style: containerStyle },
|
|
258
|
+
{ t, label, helper, error: errorMessage(error), disabled, required, noteId, style: containerStyle },
|
|
256
259
|
group,
|
|
257
260
|
);
|
|
258
261
|
});
|
|
259
262
|
|
|
260
263
|
RadioGroup.displayName = "RadioGroup";
|
|
264
|
+
RadioGroup[DRAWS_FIELD_FRAME] = true;
|
|
261
265
|
return RadioGroup;
|
|
262
266
|
}
|
package/dist/select-view.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// it is held, or each pause in typing asks `onSearch` / async `options`.
|
|
11
11
|
|
|
12
12
|
import React from "react";
|
|
13
|
-
import { useHostTheme } from "./hooks.js";
|
|
13
|
+
import { useHostSharedText, useHostTheme } from "./hooks.js";
|
|
14
14
|
import { DEFAULT_THEME_TOKENS } from "./_theme-tokens.js";
|
|
15
15
|
import { Icon } from "./icon.js";
|
|
16
16
|
import { TEXT_FIELD_TOKEN_NAMES } from "./text-field-tokens.js";
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
selectedOption,
|
|
23
23
|
} from "./form-control-tokens.js";
|
|
24
24
|
import {
|
|
25
|
+
DRAWS_FIELD_FRAME,
|
|
25
26
|
errorMessage,
|
|
26
27
|
hasNote,
|
|
27
28
|
isShown,
|
|
@@ -347,6 +348,7 @@ export function makeSelect(rn) {
|
|
|
347
348
|
helper,
|
|
348
349
|
error,
|
|
349
350
|
disabled = false,
|
|
351
|
+
required = false,
|
|
350
352
|
accessibilityLabel,
|
|
351
353
|
closeAccessibilityLabel,
|
|
352
354
|
style,
|
|
@@ -374,6 +376,7 @@ export function makeSelect(rn) {
|
|
|
374
376
|
else rest[key] = own;
|
|
375
377
|
}
|
|
376
378
|
const t = resolveFormControlTokens(theme, overrides);
|
|
379
|
+
const requiredWord = useHostSharedText("required", "Required");
|
|
377
380
|
const [open, setOpen] = React.useState(false);
|
|
378
381
|
// Null until the person ticks something: an untouched sheet follows `values`.
|
|
379
382
|
const [ownDraft, setDraft] = React.useState(null);
|
|
@@ -406,7 +409,7 @@ export function makeSelect(rn) {
|
|
|
406
409
|
const draft = ownDraft === null ? valueList(values) : ownDraft;
|
|
407
410
|
const invalid = isShown(error);
|
|
408
411
|
const sheetTitle = isShown(title) ? title : isText(label) ? label : undefined;
|
|
409
|
-
const name = spokenLabel(accessibilityLabel, label);
|
|
412
|
+
const name = spokenLabel(accessibilityLabel, label, required ? requiredWord : null);
|
|
410
413
|
const shown = current ? current.label : placeholder;
|
|
411
414
|
const spokenChoice = multiple
|
|
412
415
|
? picked.length > 0
|
|
@@ -453,6 +456,7 @@ export function makeSelect(rn) {
|
|
|
453
456
|
helper,
|
|
454
457
|
error: errorMessage(error),
|
|
455
458
|
disabled,
|
|
459
|
+
required,
|
|
456
460
|
noteId,
|
|
457
461
|
onLabelPress: () => focusNode(triggerRef.current),
|
|
458
462
|
style: containerStyle,
|
|
@@ -572,6 +576,7 @@ export function makeSelect(rn) {
|
|
|
572
576
|
});
|
|
573
577
|
|
|
574
578
|
Select.displayName = "Select";
|
|
579
|
+
Select[DRAWS_FIELD_FRAME] = true;
|
|
575
580
|
return Select;
|
|
576
581
|
}
|
|
577
582
|
|