@antscorp/ui 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/README.md +26 -2
  2. package/dist/atoms/AddButton.d.ts +3 -6
  3. package/dist/atoms/Chip.d.ts +2 -5
  4. package/dist/atoms/Chip.js +1 -1
  5. package/dist/atoms/InfoTip.js +2 -0
  6. package/dist/atoms/LabeledDivider.d.ts +1 -1
  7. package/dist/atoms/PanelSearchInput.d.ts +1 -1
  8. package/dist/atoms/RemoveButton.d.ts +2 -5
  9. package/dist/atoms/TextAction.d.ts +5 -6
  10. package/dist/atoms/TruncatedOption.d.ts +1 -1
  11. package/dist/cli.js +248 -0
  12. package/dist/doc/schema.d.ts +34 -0
  13. package/dist/doc/schema.js +33 -0
  14. package/dist/docs.json +1 -0
  15. package/dist/index.d.ts +2 -0
  16. package/dist/index.js +53 -51
  17. package/dist/lib/iso.d.ts +2 -2
  18. package/dist/lib/iso.js +16 -8
  19. package/dist/lib/tokenEditorExtensions.js +1 -1
  20. package/dist/lib/useLoadingShown.d.ts +1 -1
  21. package/dist/molecules/AccentCard.d.ts +2 -3
  22. package/dist/molecules/AccentCard.js +2 -2
  23. package/dist/molecules/CheckRow.d.ts +3 -8
  24. package/dist/molecules/ChoiceTiles.d.ts +1 -1
  25. package/dist/molecules/ChoiceTiles.js +1 -1
  26. package/dist/molecules/CommitTextField.d.ts +1 -19
  27. package/dist/molecules/CommitTextField.js +21 -18
  28. package/dist/molecules/ConfirmDialog.d.ts +2 -7
  29. package/dist/molecules/DateTimeInput.d.ts +6 -3
  30. package/dist/molecules/DateTimeInput.js +82 -73
  31. package/dist/molecules/FieldGroup.d.ts +1 -1
  32. package/dist/molecules/FieldRow.d.ts +1 -1
  33. package/dist/molecules/MetaTable.d.ts +1 -1
  34. package/dist/molecules/PhoneFrame.d.ts +1 -1
  35. package/dist/molecules/RemovableRow.d.ts +1 -1
  36. package/dist/molecules/RemovableRow.js +32 -32
  37. package/dist/molecules/RevealRadioGroup.d.ts +2 -9
  38. package/dist/molecules/RevealSwitch.d.ts +2 -7
  39. package/dist/molecules/Select.d.ts +8 -24
  40. package/dist/molecules/Select.js +50 -44
  41. package/dist/molecules/SideLabelRow.d.ts +1 -1
  42. package/dist/molecules/SplitButton.d.ts +1 -1
  43. package/dist/molecules/StatusButton.d.ts +2 -7
  44. package/dist/molecules/TagSelect.d.ts +2 -2
  45. package/dist/molecules/TransferLayout.d.ts +1 -1
  46. package/dist/molecules/TreeOptions.d.ts +4 -3
  47. package/dist/molecules/TreeOptions.js +67 -44
  48. package/dist/organisms/HourOfDayRow.js +2 -2
  49. package/dist/organisms/HourOfWeekGrid.js +2 -2
  50. package/dist/organisms/RuleGroups.d.ts +3 -3
  51. package/dist/organisms/SearchableGroupedList.d.ts +1 -1
  52. package/dist/organisms/TransferPicker.d.ts +1 -1
  53. package/dist/organisms/TreeSearchPanel.js +50 -47
  54. package/dist/organisms/TreeSelect.d.ts +3 -3
  55. package/dist/ui.css +1 -1
  56. package/package.json +36 -19
  57. package/src/atoms/AddButton.tsx +3 -7
  58. package/src/atoms/Chip.tsx +7 -9
  59. package/src/atoms/InfoTip.tsx +2 -2
  60. package/src/atoms/LabeledDivider.tsx +1 -1
  61. package/src/atoms/PanelSearchInput.tsx +1 -1
  62. package/src/atoms/RemoveButton.tsx +2 -6
  63. package/src/atoms/TextAction.tsx +6 -8
  64. package/src/atoms/TruncatedOption.tsx +1 -1
  65. package/src/doc/schema.ts +67 -0
  66. package/src/index.ts +2 -0
  67. package/src/lib/iso.ts +15 -3
  68. package/src/lib/tokenEditorExtensions.ts +1 -1
  69. package/src/lib/useLoadMore.ts +1 -2
  70. package/src/lib/useLoadingShown.ts +1 -1
  71. package/src/molecules/AccentCard.tsx +11 -10
  72. package/src/molecules/CheckRow.tsx +3 -9
  73. package/src/molecules/ChoiceTiles.tsx +6 -4
  74. package/src/molecules/CommitTextField.tsx +15 -20
  75. package/src/molecules/ConfirmDialog.tsx +2 -8
  76. package/src/molecules/DateTimeInput.tsx +86 -68
  77. package/src/molecules/DurationInput.tsx +2 -2
  78. package/src/molecules/FieldGroup.tsx +1 -1
  79. package/src/molecules/FieldRow.tsx +1 -1
  80. package/src/molecules/FrequencyLimitRow.tsx +1 -1
  81. package/src/molecules/MetaTable.tsx +1 -1
  82. package/src/molecules/PhoneFrame.tsx +2 -2
  83. package/src/molecules/RemovableRow.tsx +3 -3
  84. package/src/molecules/RevealRadioGroup.tsx +5 -10
  85. package/src/molecules/RevealSwitch.tsx +2 -8
  86. package/src/molecules/Select.tsx +23 -23
  87. package/src/molecules/SideLabelRow.tsx +2 -2
  88. package/src/molecules/SplitButton.tsx +1 -1
  89. package/src/molecules/StatusButton.tsx +2 -8
  90. package/src/molecules/TagSelect.tsx +5 -5
  91. package/src/molecules/TransferLayout.tsx +2 -3
  92. package/src/molecules/TreeOptions.tsx +89 -65
  93. package/src/organisms/HourOfDayRow.tsx +5 -3
  94. package/src/organisms/HourOfWeekGrid.tsx +5 -3
  95. package/src/organisms/RuleGroups.tsx +3 -3
  96. package/src/organisms/SearchableGroupedList.tsx +2 -2
  97. package/src/organisms/TransferPicker.tsx +3 -3
  98. package/src/organisms/TreeSearchPanel.tsx +2 -2
  99. package/src/organisms/TreeSelect.tsx +4 -4
@@ -7,33 +7,31 @@ import { fitToMaxLength } from "../lib/fitToMaxLength";
7
7
  import { useDraft } from "../lib/useDraft";
8
8
 
9
9
  /** What `CommitTextField` takes. */
10
- export type CommitTextFieldProps = {
11
- /** The field's label; also its accessible name when hidden. */
12
- readonly label: string;
10
+ export type CommitTextFieldProps = Pick<
11
+ TextInputProps,
12
+ | "label"
13
+ | "isDisabled"
14
+ | "isReadOnly"
15
+ | "isLabelHidden"
16
+ | "placeholder"
17
+ | "description"
18
+ | "isRequired"
19
+ | "width"
20
+ | "controlXstyle"
21
+ | "containerXstyle"
22
+ > & {
13
23
  /** The committed text. A new value from outside (an agent, undo) replaces the draft. */
14
24
  readonly value: string;
15
25
  /** Called with the draft on blur or Enter, only when it differs from `value`. */
16
26
  readonly onCommit: (value: string) => void;
17
27
  /** The input's height, `md` by default. */
18
28
  readonly size?: "sm" | "md";
19
- /** Dims the field and blocks typing. */
20
- readonly isDisabled?: boolean;
21
- /** Shows the text, not dimmed, and blocks typing. */
22
- readonly isReadOnly?: boolean;
23
- /** Keeps the label for screen readers only. */
24
- readonly isLabelHidden?: boolean;
25
- /** Shown while the field is empty. */
26
- readonly placeholder?: string;
27
- /** Help under the label. */
28
- readonly description?: string;
29
29
  /** A problem with the value, already in the user's language. */
30
30
  readonly error?: string | undefined;
31
31
  /** A text area instead of a single line; Enter then adds a line. */
32
32
  readonly isMultiline?: boolean;
33
33
  /** For `isMultiline`: how many rows the text area shows, `4` by default. */
34
34
  readonly rows?: number;
35
- /** Marks the field as required. */
36
- readonly isRequired?: boolean;
37
35
  /**
38
36
  * `false` (default): after a commit the draft goes back to `value`, so a refused text does not
39
37
  * stay in the field. `true` keeps the typed text until `value` changes from outside.
@@ -43,10 +41,6 @@ export type CommitTextFieldProps = {
43
41
  readonly maxLength?: number;
44
42
  /** Fresh global pattern for whole tokens, counted as one character even in plain text. */
45
43
  readonly pattern?: () => RegExp;
46
- /** For a single line: the field's width, in pixels or any CSS length such as `"100%"`. */
47
- readonly width?: number | string;
48
- /** For a single line: StyleX styles for the input's box, e.g. to make it read as plain text. */
49
- readonly controlXstyle?: TextInputProps["controlXstyle"];
50
44
  };
51
45
 
52
46
  /**
@@ -86,6 +80,8 @@ export function CommitTextField(props: CommitTextFieldProps): ReactNode {
86
80
  isRequired: props.isRequired ?? false,
87
81
  ...(props.placeholder === undefined ? {} : { placeholder: props.placeholder }),
88
82
  ...(props.description === undefined ? {} : { description: props.description }),
83
+ ...(props.controlXstyle === undefined ? {} : { controlXstyle: props.controlXstyle }),
84
+ ...(props.containerXstyle === undefined ? {} : { containerXstyle: props.containerXstyle }),
89
85
  ...errorStatus(error),
90
86
  };
91
87
  if (isMultiline) return <TextArea {...common} rows={props.rows ?? 4} />;
@@ -94,7 +90,6 @@ export function CommitTextField(props: CommitTextFieldProps): ReactNode {
94
90
  {...common}
95
91
  onEnter={commitAndReset}
96
92
  {...(props.width === undefined ? {} : { width: props.width })}
97
- {...(props.controlXstyle === undefined ? {} : { controlXstyle: props.controlXstyle })}
98
93
  />
99
94
  );
100
95
  }
@@ -1,14 +1,8 @@
1
- import { AlertDialog } from "@astryxdesign/core/AlertDialog";
1
+ import { AlertDialog, type AlertDialogProps } from "@astryxdesign/core/AlertDialog";
2
2
  import type { ReactNode } from "react";
3
3
 
4
4
  /** What `ConfirmDialog` takes. */
5
- export type ConfirmDialogProps = {
6
- /** Open while a change waits for an answer. */
7
- readonly isOpen: boolean;
8
- /** The question, e.g. "Remove these rows?". */
9
- readonly title: string;
10
- /** What the change does. */
11
- readonly description: string;
5
+ export type ConfirmDialogProps = Pick<AlertDialogProps, "isOpen" | "title" | "description"> & {
12
6
  /** The texts of the two buttons; the host translates them. */
13
7
  readonly labels: {
14
8
  /** The confirming button, e.g. "Remove". */
@@ -1,7 +1,8 @@
1
1
  import { DateInput } from "@astryxdesign/core/DateInput";
2
2
  import { HStack } from "@astryxdesign/core/HStack";
3
3
  import { Text } from "@astryxdesign/core/Text";
4
- import { TimeInput } from "@astryxdesign/core/TimeInput";
4
+ import { TextInput } from "@astryxdesign/core/TextInput";
5
+ import { TimeInput, type TimeInputProps } from "@astryxdesign/core/TimeInput";
5
6
  import type { ReactNode } from "react";
6
7
 
7
8
  import { errorStatus } from "../lib/errorStatus";
@@ -9,93 +10,105 @@ import { dateValue, minDate, timeValue } from "../lib/iso";
9
10
  import { FieldGroup, type FieldGroupProps } from "./FieldGroup";
10
11
 
11
12
  /** What `DateTimeInput` takes. */
12
- export type DateTimeInputProps = Pick<FieldGroupProps, "isDisabled"> & {
13
- /** The group's label, e.g. "Start date"; it names the group and is not shown. */
14
- readonly label: string;
15
- /** The texts the inputs show; the host translates them. */
16
- readonly labels: {
17
- /** The date input's label: its accessible name in a group, shown with `frame="none"`. */
18
- readonly date: string;
19
- /** The time input's label: its accessible name in a group, shown with `frame="none"`. */
20
- readonly time: string;
21
- /** A time zone, or any note, shown beside the time. */
22
- readonly timezone?: string | undefined;
13
+ export type DateTimeInputProps = Pick<FieldGroupProps, "isDisabled"> &
14
+ Pick<TimeInputProps, "hourFormat"> & {
15
+ /** The group's label, e.g. "Start date"; it names the group and is not shown. */
16
+ readonly label: string;
17
+ /** The texts the inputs show; the host translates them. */
18
+ readonly labels: {
19
+ /** The date input's label: its accessible name in a group, shown with `frame="none"`. */
20
+ readonly date: string;
21
+ /** The time input's label: its accessible name in a group, shown with `frame="none"`. */
22
+ readonly time: string;
23
+ /** A time zone, or any note, shown beside the time. */
24
+ readonly timezone?: string | undefined;
25
+ };
26
+ /** The local date-time (`YYYY-MM-DD HH:mm:ss`); empty or partial while it is being filled in. */
27
+ readonly value: string;
28
+ /** The inputs' size, `md` by default. */
29
+ readonly size?: "sm" | "md" | undefined;
30
+ /** Shows readable date and time fields at full opacity, without clear or picker actions. */
31
+ readonly isReadOnly?: boolean | undefined;
32
+ /** A problem with the date-time, already in the user's language. */
33
+ readonly error?: string | undefined;
34
+ /** `false` picks a date only: the time input is left out and `onChange` gets `YYYY-MM-DD`, or empty. */
35
+ readonly hasTime?: boolean;
36
+ /**
37
+ * `bare` (default) puts both inputs, with hidden labels, in an unframed group that `label` names
38
+ * for assistive tech, for a host that shows the label itself. `none` draws no group: it shows
39
+ * `labels.date` and `labels.time` above the inputs and hangs `description` and `error` on the
40
+ * date input.
41
+ */
42
+ readonly frame?: "bare" | "none";
43
+ /** Help above the inputs, or under the date input with `frame="none"`. */
44
+ readonly description?: string | undefined;
45
+ /** Marks the date as required. */
46
+ readonly isRequired?: boolean;
47
+ /** The time input stays disabled until a date is picked. */
48
+ readonly isTimeLockedWithoutDate?: boolean;
49
+ /** The earliest valid calendar date (`YYYY-MM-DD`); an absent or invalid minimum leaves dates unconstrained. */
50
+ readonly min?: string | undefined;
51
+ /** Called with the new date-time; a part not picked yet is empty, a new date starts at 00:00:00. */
52
+ readonly onChange: (value: string) => void;
23
53
  };
24
- /** The local date-time (`YYYY-MM-DD HH:mm:ss`); empty or partial while it is being filled in. */
25
- readonly value: string;
26
- /** The inputs' size, `md` by default. */
27
- readonly size?: "sm" | "md" | undefined;
28
- /** Shows the date-time, not dimmed, and ignores changes. */
29
- readonly isReadOnly?: boolean | undefined;
30
- /** A problem with the date-time, already in the user's language. */
31
- readonly error?: string | undefined;
32
- /** `false` picks a date only: the time input is left out and `onChange` gets `YYYY-MM-DD`, or empty. */
33
- readonly hasTime?: boolean;
34
- /**
35
- * `bare` (default) puts both inputs, with hidden labels, in an unframed group that `label` names
36
- * for assistive tech, for a host that shows the label itself. `none` draws no group: it shows
37
- * `labels.date` and `labels.time` above the inputs and hangs `description` and `error` on the
38
- * date input.
39
- */
40
- readonly frame?: "bare" | "none";
41
- /** Help above the inputs, or under the date input with `frame="none"`. */
42
- readonly description?: string | undefined;
43
- /** Marks the date as required. */
44
- readonly isRequired?: boolean;
45
- /** The time input stays disabled until a date is picked. */
46
- readonly isTimeLockedWithoutDate?: boolean;
47
- /** The earliest date the calendar offers (`YYYY-MM-DD`), such as an end's start; any without it or with another text. */
48
- readonly min?: string | undefined;
49
- /** Called with the new date-time; a part not picked yet is empty, a new date starts at 00:00:00. */
50
- readonly onChange: (value: string) => void;
51
- };
52
54
 
53
55
  /**
54
56
  * A date and a time of day under one label. It composes `DateInput` and `TimeInput` rather than
55
57
  * using Astryx's `DateTimeInput`, which holds only a complete value: here a date or a time may be
56
58
  * set alone while the other is still empty.
59
+ * `hourFormat` defaults to `12h`; choose `24h` for a 24-hour clock. Calendar and clear text use
60
+ * the facade's `InternationalizationProvider`, alongside host-translated `label` and `labels`.
57
61
  */
58
62
  export function DateTimeInput(props: DateTimeInputProps): ReactNode {
59
63
  const { label, isDisabled = false, isReadOnly = false, error, hasTime = true, frame } = props;
60
64
  const size = props.size ?? "md";
61
- const onChange = (next: string): void => {
62
- if (!isReadOnly) props.onChange(next);
63
- };
64
65
  const isInline = frame === "none";
65
66
  const [date = "", time = ""] = props.value.split(" ");
66
- const dateInput = (
67
+ const dateProps = {
68
+ label: props.labels.date,
69
+ size,
70
+ isLabelHidden: !isInline,
71
+ isRequired: props.isRequired ?? false,
72
+ ...(isInline && props.description !== undefined ? { description: props.description } : {}),
73
+ ...(isInline ? errorStatus(error) : {}),
74
+ };
75
+ const dateInput = isReadOnly ? (
76
+ <TextInput {...dateProps} value={date} isReadOnly />
77
+ ) : (
67
78
  <DateInput
68
- label={props.labels.date}
69
- size={size}
70
- isLabelHidden={!isInline}
79
+ {...dateProps}
71
80
  isDisabled={isDisabled}
72
- isRequired={props.isRequired ?? false}
73
81
  {...dateValue(date)}
74
82
  {...minDate(props.min)}
75
83
  onChange={(next) => {
76
84
  // join turns a cleared date (undefined) into an empty one.
77
- onChange(hasTime ? [next, time === "" ? "00:00:00" : time].join(" ") : (next ?? ""));
78
- }}
79
- {...(isInline && props.description !== undefined ? { description: props.description } : {})}
80
- {...(isInline ? errorStatus(error) : {})}
81
- />
82
- );
83
- const timeInput = hasTime && (
84
- <TimeInput
85
- label={props.labels.time}
86
- size={size}
87
- isLabelHidden={!isInline}
88
- hourFormat="12h"
89
- hasSeconds
90
- width={size === "sm" ? 150 : 168}
91
- isDisabled={isDisabled || (props.isTimeLockedWithoutDate === true && date === "")}
92
- {...timeValue(time)}
93
- onChange={(next) => {
94
- // Astryx may hand back HH:mm; the stored time always has seconds.
95
- onChange([date, next?.length === 5 ? `${next}:00` : next].join(" "));
85
+ props.onChange(hasTime ? [next, time === "" ? "00:00:00" : time].join(" ") : (next ?? ""));
96
86
  }}
97
87
  />
98
88
  );
89
+ const timeProps = {
90
+ label: props.labels.time,
91
+ size,
92
+ isLabelHidden: !isInline,
93
+ width: size === "sm" ? 150 : 168,
94
+ };
95
+ const timeInput =
96
+ hasTime &&
97
+ (isReadOnly ? (
98
+ <TextInput {...timeProps} value={time} isReadOnly />
99
+ ) : (
100
+ <TimeInput
101
+ {...timeProps}
102
+ hourFormat={props.hourFormat ?? "12h"}
103
+ hasSeconds
104
+ isDisabled={isDisabled || (props.isTimeLockedWithoutDate === true && date === "")}
105
+ {...timeValue(time)}
106
+ onChange={(next) => {
107
+ // Astryx may hand back HH:mm; the stored time always has seconds.
108
+ props.onChange([date, next?.length === 5 ? `${next}:00` : next].join(" "));
109
+ }}
110
+ />
111
+ ));
99
112
  const zone = props.labels.timezone !== undefined && (
100
113
  <Text type="supporting">{props.labels.timezone}</Text>
101
114
  );
@@ -110,7 +123,12 @@ export function DateTimeInput(props: DateTimeInputProps): ReactNode {
110
123
  );
111
124
  }
112
125
  return (
113
- <FieldGroup label={label} description={props.description} isDisabled={isDisabled} error={error}>
126
+ <FieldGroup
127
+ label={label}
128
+ description={props.description}
129
+ isDisabled={isDisabled && !isReadOnly}
130
+ error={error}
131
+ >
114
132
  <HStack gap={2} wrap="wrap" align="center">
115
133
  {dateInput}
116
134
  {timeInput}
@@ -44,7 +44,7 @@ export function DurationInput(props: DurationInputProps): ReactNode {
44
44
  const size = props.size ?? "md";
45
45
  const duration = props.value ?? {};
46
46
  const isWholeDays = max.days !== undefined && duration.days === max.days;
47
- // As fe-app's: with any other part set, days stop one short, so they never reach a max that disables those parts.
47
+ // With any other part set, days stop one short, so they never reach a max that disables those parts.
48
48
  const hasParts = PARTS.some((part) => part !== "days" && (duration[part] ?? 0) > 0);
49
49
  const limits = { ...max, ...(max.days !== undefined && hasParts && { days: max.days - 1 }) };
50
50
  return (
@@ -55,7 +55,7 @@ export function DurationInput(props: DurationInputProps): ReactNode {
55
55
  <NumberInput
56
56
  label={labels[part]}
57
57
  isLabelHidden
58
- // fe-app's: room for three digits of days, two of the others.
58
+ // Room for three digits of days, two of the others.
59
59
  width={part === "days" ? 80 : 64}
60
60
  size={size}
61
61
  min={0}
@@ -18,7 +18,7 @@ export type FieldGroupProps = {
18
18
  readonly frame?: "none" | "card" | undefined;
19
19
  /** Help under the label. */
20
20
  readonly description?: string | undefined;
21
- /** Disables every field in the group, e.g. for a read-only form. */
21
+ /** Disables fields through the unframed fieldset; with `frame="card"`, disable children yourself. */
22
22
  readonly isDisabled?: boolean | undefined;
23
23
  /** A problem with the group as a whole, already in the user's language. */
24
24
  readonly error?: string | undefined;
@@ -8,7 +8,7 @@ import type { ReactNode } from "react";
8
8
  export type FieldRowProps = {
9
9
  /**
10
10
  * The row's caption, in a column at the start. Without it the column stays, empty, so the
11
- * content lines up with the captioned rows around it (as fe-app's empty title column); stacked,
11
+ * content lines up with the captioned rows around it; stacked,
12
12
  * nothing is drawn above the content.
13
13
  */
14
14
  readonly label?: string | undefined;
@@ -72,7 +72,7 @@ function Marked(props: {
72
72
  );
73
73
  }
74
74
 
75
- // Wide enough for a few digits and the stepper, as fe-app's number box.
75
+ // Wide enough for a few digits and the stepper.
76
76
  const TIMES_WIDTH = 80;
77
77
 
78
78
  /** "N time(s) per window" as one sentence, with a number more when the window is the `everyWindow`. */
@@ -47,7 +47,7 @@ export type MetaTableProps = {
47
47
  };
48
48
 
49
49
  /**
50
- * A read-only table of names and a short value each, as fe-app shows a pick in view mode: a name
50
+ * A read-only table of names and a short value each: a name
51
51
  * column (each name a link when it has an `href`, on one line with the whole name in a tooltip when
52
52
  * cut; a problem in the error tone with a ⚠ tip) and, with `labels.meta`, a value column at the
53
53
  * end. A row that `isLoading` shows shimmer bars in its cells, in the table's real columns; the
@@ -21,7 +21,7 @@ export type PhoneFrameProps = {
21
21
  };
22
22
 
23
23
  /**
24
- * A phone to preview content in, as antsomi-ui's `MobileFrameV3` with its `MobileBrandingBar`,
24
+ * A phone frame with a branding bar to preview content in,
25
25
  * drawn rather than pictured: a dark rounded device with a camera island, a status bar (clock,
26
26
  * signal, battery), a header with a back chevron, the avatar and the title, and the content under
27
27
  * it, scrolling when it is taller than the screen. As wide as its container, up to 290px, with a
@@ -77,7 +77,7 @@ export function PhoneFrame({ title, avatarSrc, labels, children }: PhoneFramePro
77
77
  const BARS = ["s", "m", "l", "xl"] as const;
78
78
 
79
79
  const styles = stylex.create({
80
- // antsomi-ui's medium frame: 290 × 600 at most. The bezel is black with a slate rim; no Astryx
80
+ // A medium frame: 290 × 600 at most. The bezel is black with a slate rim; no Astryx
81
81
  // surface is a device, so the antsomi palette draws it.
82
82
  device: {
83
83
  boxSizing: "border-box",
@@ -36,7 +36,7 @@ export type RemovableRowProps = {
36
36
  */
37
37
  readonly hoverContent?: ReactNode;
38
38
  /**
39
- * A bordered box around the row, as fe-app's picked items: its text on one line, the whole text
39
+ * A bordered box around the row: its text on one line, the whole text
40
40
  * in a tooltip when cut. As high as `RowSkeletons variant="framed"`.
41
41
  */
42
42
  readonly isFramed?: boolean | undefined;
@@ -67,7 +67,7 @@ export function RemovableRow(props: RemovableRowProps): ReactNode {
67
67
  <RemoveButton
68
68
  label={props.labels.remove}
69
69
  size={props.size}
70
- isDisabled={props.isDisabled}
70
+ isDisabled={props.isDisabled ?? false}
71
71
  isReadOnly={props.isReadOnly}
72
72
  onClick={props.onRemove}
73
73
  />
@@ -134,7 +134,7 @@ export function RemovableRow(props: RemovableRowProps): ReactNode {
134
134
  const styles = stylex.create({
135
135
  // The texts take the room the end content and the remove button leave, so they wrap.
136
136
  text: { minWidth: 0, flexGrow: 1 },
137
- // fe-app's picked item (UITargetSelection ColumnSelection WrapperLabelV2): a 1px platinum
137
+ // A picked item's frame: a 1px platinum
138
138
  // border, 5px corners and 8px padding (the `padding` step 2).
139
139
  frame: {
140
140
  borderWidth: borderVars["--border-width"],
@@ -1,4 +1,4 @@
1
- import { RadioList, RadioListItem } from "@astryxdesign/core/RadioList";
1
+ import { RadioList, RadioListItem, type RadioListProps } from "@astryxdesign/core/RadioList";
2
2
  import { spacingVars } from "@astryxdesign/core/theme/tokens.stylex";
3
3
  import { VStack } from "@astryxdesign/core/VStack";
4
4
  import * as stylex from "@stylexjs/stylex";
@@ -21,13 +21,10 @@ export type RevealRadioOption<V extends string> = {
21
21
  };
22
22
 
23
23
  /** What `RevealRadioGroup` takes. */
24
- export type RevealRadioGroupProps<V extends string> = {
25
- /** Names the group; also its accessible name when hidden. */
26
- readonly label: string;
27
- /** Keeps the label for screen readers only. */
28
- readonly isLabelHidden?: boolean | undefined;
29
- /** Lays the options out in a row instead of a column. */
30
- readonly orientation?: "vertical" | "horizontal" | undefined;
24
+ export type RevealRadioGroupProps<V extends string> = Pick<
25
+ RadioListProps,
26
+ "label" | "isLabelHidden" | "orientation" | "isDisabled"
27
+ > & {
31
28
  /** The choices, in order. */
32
29
  readonly options: readonly RevealRadioOption<V>[];
33
30
  /** The chosen option's value. */
@@ -36,8 +33,6 @@ export type RevealRadioGroupProps<V extends string> = {
36
33
  readonly onChange: (value: V) => void;
37
34
  /** The radios' size, `md` by default (Astryx's default; the theme draws it at 18px). */
38
35
  readonly size?: "sm" | "md" | undefined;
39
- /** Dims the radios and blocks them. */
40
- readonly isDisabled?: boolean | undefined;
41
36
  /** Shows the value, not dimmed, and ignores changes. */
42
37
  readonly isReadOnly?: boolean | undefined;
43
38
  /** The space between the list and the revealed content, `2` by default. */
@@ -1,19 +1,13 @@
1
- import { Switch } from "@astryxdesign/core/Switch";
1
+ import { Switch, type SwitchProps } from "@astryxdesign/core/Switch";
2
2
  import { VStack } from "@astryxdesign/core/VStack";
3
3
  import type { ReactNode } from "react";
4
4
 
5
5
  /** What `RevealSwitch` takes. */
6
- export type RevealSwitchProps = {
7
- /** What the switch turns on. */
8
- readonly label: string;
9
- /** Whether the switch is on. */
10
- readonly value: boolean;
6
+ export type RevealSwitchProps = Pick<SwitchProps, "label" | "isDisabled" | "value"> & {
11
7
  /** Called with the new state. */
12
8
  readonly onChange: (value: boolean) => void;
13
9
  /** The switch's size, `md` by default. */
14
10
  readonly size?: "sm" | "md" | undefined;
15
- /** Dims the switch and blocks it. */
16
- readonly isDisabled?: boolean | undefined;
17
11
  /** Shows the value, not dimmed, and ignores changes. */
18
12
  readonly isReadOnly?: boolean | undefined;
19
13
  /** What shows below the switch while it is on. */
@@ -2,6 +2,7 @@ import {
2
2
  Selector,
3
3
  type SelectorOptionData,
4
4
  type SelectorOptionType,
5
+ type SelectorProps,
5
6
  } from "@astryxdesign/core/Selector";
6
7
  import type { ReactNode } from "react";
7
8
 
@@ -13,13 +14,20 @@ import { useLoadingShown } from "../lib/useLoadingShown";
13
14
  export type SelectOption = SelectorOptionType;
14
15
 
15
16
  /** What `Select` takes. */
16
- export type SelectProps = {
17
- /** Names the control for assistive tech; shown above it unless `isLabelHidden`. */
18
- readonly label: string;
19
- /** Hides the label, as in a rule row. */
20
- readonly isLabelHidden?: boolean | undefined;
21
- /** A line under the label that explains the choice. */
22
- readonly description?: string | undefined;
17
+ export type SelectProps = Pick<
18
+ SelectorProps,
19
+ | "label"
20
+ | "isLabelHidden"
21
+ | "description"
22
+ | "placeholder"
23
+ | "hasSearch"
24
+ | "width"
25
+ | "isDisabled"
26
+ | "isReadOnly"
27
+ | "isRequired"
28
+ | "controlXstyle"
29
+ | "containerXstyle"
30
+ > & {
23
31
  /** The list, in order. */
24
32
  readonly options: readonly SelectOption[];
25
33
  /** The picked option's value; `null` or none shows the placeholder. */
@@ -28,22 +36,10 @@ export type SelectProps = {
28
36
  readonly onChange: (value: string) => void;
29
37
  /** Called by the clear button, which shows only when this is given and an option is picked. */
30
38
  readonly onClear?: (() => void) | undefined;
31
- /** Shown in the trigger while nothing is picked. */
32
- readonly placeholder?: string | undefined;
33
- /** A search box over the list, filtering it as it is typed. */
34
- readonly hasSearch?: boolean | undefined;
35
39
  /** The trigger's look: an underlined field (`input`, the default) or a bare text button (`ghost`). */
36
40
  readonly variant?: "input" | "ghost" | undefined;
37
- /** The trigger's size, `md` by default (30px, as fe-app's controls). */
38
- readonly size?: "sm" | "md" | undefined;
39
- /** The trigger's width, in pixels or any CSS length such as `"100%"`. */
40
- readonly width?: number | string | undefined;
41
- /** Dims the trigger and blocks it. */
42
- readonly isDisabled?: boolean | undefined;
43
- /** Shows the picked label without the chevron; the list does not open. */
44
- readonly isReadOnly?: boolean | undefined;
45
- /** Marks the field as required. */
46
- readonly isRequired?: boolean | undefined;
41
+ /** The trigger's size, md by default; the kit narrows the upstream sizes to sm and md. */
42
+ readonly size?: Extract<SelectorProps["size"], "sm" | "md"> | undefined;
47
43
  /**
48
44
  * The options are loading: a spinner in place of the chevron and the trigger busy from 300 ms
49
45
  * on, for at least 300 ms (`useLoadingShown`), and the list does not open meanwhile. The picked
@@ -53,7 +49,7 @@ export type SelectProps = {
53
49
  readonly isLoading?: boolean | undefined;
54
50
  /** A problem with the pick, already in the user's language, shown under the trigger. */
55
51
  readonly error?: string | undefined;
56
- /** The texts the view shows; the host translates them. Astryx's English stands in for a missing one. */
52
+ /** Host texts; missing strings use the facade's InternationalizationProvider locale (English by default). */
57
53
  readonly labels?:
58
54
  | {
59
55
  /** The search box's placeholder, such as "Search". */
@@ -69,9 +65,11 @@ export type SelectProps = {
69
65
  };
70
66
 
71
67
  /**
72
- * The kit's select, as antd's Select in fe-app, on Astryx's `Selector`: the list is as wide as the
68
+ * The kit's select on Astryx's `Selector`: the list is as wide as the
73
69
  * trigger (the theme) and a long option label ends in an ellipsis with the whole label in a
74
70
  * tooltip. Every select is one rather than Astryx's `Selector` (lint-enforced). It holds no state.
71
+ * Localize upstream search, clear and empty text with `InternationalizationProvider` from
72
+ * `@antscorp/ui/astryx/i18n`; `labels` overrides individual kit captions.
75
73
  */
76
74
  export function Select(props: SelectProps): ReactNode {
77
75
  const { labels = {}, onClear, onChange } = props;
@@ -106,6 +104,8 @@ export function Select(props: SelectProps): ReactNode {
106
104
  ...(props.isDisabled !== undefined && { isDisabled: props.isDisabled }),
107
105
  ...(props.isReadOnly !== undefined && !isBusy && { isReadOnly: props.isReadOnly }),
108
106
  ...(props.isRequired !== undefined && { isRequired: props.isRequired }),
107
+ ...(props.controlXstyle !== undefined && { controlXstyle: props.controlXstyle }),
108
+ ...(props.containerXstyle !== undefined && { containerXstyle: props.containerXstyle }),
109
109
  ...(labels.search !== undefined && { searchPlaceholder: labels.search }),
110
110
  ...(labels.noMatch !== undefined && { emptySearchText: labels.noMatch }),
111
111
  };
@@ -17,7 +17,7 @@ export type SideLabelRowProps = {
17
17
 
18
18
  /**
19
19
  * One row of a card: a narrow label column and the row's controls, padded as a card's rows are.
20
- * The label sits centred on the first line of controls (`md` high, fe-app's 30px); where the controls would get
20
+ * The label sits centred on the first line of controls (`md` high, the theme's 30px); where the controls would get
21
21
  * narrower than 240px they move under the label. It holds no state.
22
22
  */
23
23
  export function SideLabelRow({ label, labelWidth = 90, children }: SideLabelRowProps): ReactNode {
@@ -34,7 +34,7 @@ export function SideLabelRow({ label, labelWidth = 90, children }: SideLabelRowP
34
34
  }
35
35
 
36
36
  const styles = stylex.create({
37
- // fe-app's rows (UIPerformEventV2 DataSourceBlockContent): 10px above and below, between the
37
+ // Rows have 10px above and below, between the
38
38
  // spacing steps of 8px and 12px.
39
39
  row: { paddingBlock: 10 },
40
40
  // As high as an `md` control, so the text is centred on the first line of controls.
@@ -37,7 +37,7 @@ export type SplitButtonProps = {
37
37
  readonly isMainDisabled?: boolean | undefined;
38
38
  /** The main button shows a spinner and is announced as busy; the menu is not affected. */
39
39
  readonly isLoading?: boolean | undefined;
40
- /** Rounds its two ends into a pill, as fe-app's variant pills; the inner corners stay square. */
40
+ /** Rounds its two ends into a pill; the inner corners stay square. */
41
41
  readonly isRound?: boolean | undefined;
42
42
  /** The button's weight, `primary` by default. */
43
43
  readonly variant?: "primary" | "secondary" | undefined;
@@ -1,4 +1,4 @@
1
- import { Button } from "@astryxdesign/core/Button";
1
+ import { Button, type ButtonProps } from "@astryxdesign/core/Button";
2
2
  import { StatusDot } from "@astryxdesign/core/StatusDot";
3
3
  import type { ComponentProps, ReactNode } from "react";
4
4
 
@@ -6,9 +6,7 @@ import type { ComponentProps, ReactNode } from "react";
6
6
  export type StatusTone = ComponentProps<typeof StatusDot>["variant"];
7
7
 
8
8
  /** What `StatusButton` takes. */
9
- export type StatusButtonProps = {
10
- /** The button's text. */
11
- readonly label: string;
9
+ export type StatusButtonProps = Pick<ButtonProps, "label" | "icon" | "isDisabled"> & {
12
10
  /** The dot's tone: neutral, accent, success, warning or error. */
13
11
  readonly status: StatusTone;
14
12
  /** The texts the view shows; the host translates them. */
@@ -16,14 +14,10 @@ export type StatusButtonProps = {
16
14
  /** What the dot means, read out for it, e.g. "Connected". */
17
15
  readonly status: string;
18
16
  };
19
- /** A glyph before the text. */
20
- readonly icon?: ReactNode;
21
17
  /** Called when the button is pressed. */
22
18
  readonly onClick: () => void;
23
19
  /** The button's height, `md` by default. */
24
20
  readonly size?: "sm" | "md" | undefined;
25
- /** Dimmed and not pressable. */
26
- readonly isDisabled?: boolean | undefined;
27
21
  };
28
22
 
29
23
  /** A button whose dot shows a state beside its text, such as a connection. */
@@ -41,8 +41,8 @@ export type TagSelectProps<V extends string> = {
41
41
 
42
42
  /**
43
43
  * A small coloured tag showing the picked value; pressing it opens a popover with a heading and
44
- * every tag, wrapping, each picked by a press, which closes the popover. As antsomi-ui's
45
- * `SelectAssociatedTag` tag. Its state is whether the popover is open.
44
+ * every tag, wrapping, each picked by a press, which closes the popover.
45
+ * Its state is whether the popover is open.
46
46
  */
47
47
  export function TagSelect<V extends string>(props: TagSelectProps<V>): ReactNode {
48
48
  const { options, value } = props;
@@ -150,7 +150,7 @@ function Tag<V extends string>(props: TagProps<V>): ReactNode {
150
150
  }
151
151
 
152
152
  const styles = stylex.create({
153
- // antsomi-ui's SelectedTag (SelectAssociatedTag/styled.ts): 16px high, 3px corners and sides,
153
+ // The selected tag: 16px high, 3px corners and sides,
154
154
  // cut at 70px; no spacing or radius step is 3px.
155
155
  tag: {
156
156
  display: "inline-flex",
@@ -171,11 +171,11 @@ const styles = stylex.create({
171
171
  cursor: { default: "pointer", ":disabled": "default" },
172
172
  },
173
173
  text: { minWidth: 0 },
174
- // antsomi-ui's Popover inner padding (overlayInnerStyle 10), between the 8px and 12px steps.
174
+ // The popover's 10px inner padding, between the 8px and 12px steps.
175
175
  panel: { padding: 10 },
176
176
  });
177
177
 
178
- // antsomi-ui's tag fills (SelectAssociatedTag constants), from the antsomi palette.
178
+ // Tag fills from the Antsomi palette.
179
179
  const tones = stylex.create({
180
180
  cyan: { backgroundColor: "var(--ants-mh-cyan-500)" },
181
181
  blue: { backgroundColor: "var(--ants-mh-blue-500)" },