@parallelworks/ui 0.51.0 → 0.52.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.
Files changed (227) hide show
  1. package/README.md +155 -14
  2. package/dist/ai/agent/toolStrings.d.ts +2 -0
  3. package/dist/ai/agent/toolStrings.js +20 -18
  4. package/dist/ai/ai.css +12 -0
  5. package/dist/ai/components/AllocationSelector.js +5 -1
  6. package/dist/ai/components/AttachmentManager.js +51 -42
  7. package/dist/ai/components/AttachmentUpload.d.ts +4 -2
  8. package/dist/ai/components/AttachmentUpload.js +9 -9
  9. package/dist/ai/components/BlockedGroupBanner.js +8 -14
  10. package/dist/ai/components/BranchNavigator.js +11 -12
  11. package/dist/ai/components/ChatEmptyState.js +12 -19
  12. package/dist/ai/components/ChatInput.d.ts +4 -3
  13. package/dist/ai/components/ChatInput.js +27 -13
  14. package/dist/ai/components/ChatMessage.js +45 -121
  15. package/dist/ai/components/ChatMessageList.d.ts +5 -2
  16. package/dist/ai/components/ChatMessageList.js +89 -51
  17. package/dist/ai/components/ChatSidebar.js +11 -7
  18. package/dist/ai/components/ChatThread.js +1 -13
  19. package/dist/ai/components/DragOverlay.js +3 -3
  20. package/dist/ai/components/MessageAttachments.d.ts +10 -0
  21. package/dist/ai/components/MessageAttachments.js +78 -0
  22. package/dist/ai/components/ModelSelector.js +3 -3
  23. package/dist/ai/components/PastedText.js +2 -2
  24. package/dist/ai/components/ProviderIssueBanner.js +19 -23
  25. package/dist/ai/components/ShareDialog.js +2 -2
  26. package/dist/ai/components/agent/ActivityLine.d.ts +0 -2
  27. package/dist/ai/components/agent/ActivityLine.js +8 -51
  28. package/dist/ai/components/agent/AgentMessageParts.js +114 -53
  29. package/dist/ai/core/ChatProvider.d.ts +16 -5
  30. package/dist/ai/core/ChatProvider.js +41 -8
  31. package/dist/ai/core/config.js +4 -3
  32. package/dist/ai/index.d.ts +1 -1
  33. package/dist/ai/index.js +3 -3
  34. package/dist/ai/strings.d.ts +29 -3
  35. package/dist/ai/strings.js +81 -26
  36. package/dist/ai/types.d.ts +4 -0
  37. package/dist/ai/ui/Notice.d.ts +2 -2
  38. package/dist/ai/ui/Notice.js +29 -62
  39. package/dist/ai/ui/sidebar.d.ts +5 -3
  40. package/dist/ai/ui/sidebar.js +21 -9
  41. package/dist/components/ActionMenu.d.ts +1 -0
  42. package/dist/components/ActionMenu.js +6 -1
  43. package/dist/components/AppLink.d.ts +26 -0
  44. package/dist/components/AppLink.js +51 -0
  45. package/dist/components/Avatar.d.ts +2 -1
  46. package/dist/components/Avatar.js +4 -2
  47. package/dist/components/Button.d.ts +15 -5
  48. package/dist/components/Button.js +47 -40
  49. package/dist/components/Callout.d.ts +31 -7
  50. package/dist/components/Callout.js +50 -19
  51. package/dist/components/Card.d.ts +11 -6
  52. package/dist/components/Card.js +14 -8
  53. package/dist/components/Checkbox.js +2 -1
  54. package/dist/components/ColorInput.js +5 -3
  55. package/dist/components/Combobox.js +49 -19
  56. package/dist/components/ConfirmModal.d.ts +19 -8
  57. package/dist/components/ConfirmModal.js +31 -53
  58. package/dist/components/CopyButton.d.ts +38 -0
  59. package/dist/components/CopyButton.js +68 -0
  60. package/dist/components/CreateModal.js +118 -111
  61. package/dist/components/DatePicker.js +12 -8
  62. package/dist/components/DescriptionList.d.ts +25 -5
  63. package/dist/components/DescriptionList.js +56 -22
  64. package/dist/components/Disclosure.js +1 -1
  65. package/dist/components/Drawer.js +10 -16
  66. package/dist/components/Field.d.ts +15 -5
  67. package/dist/components/Field.js +40 -8
  68. package/dist/components/IconButton.d.ts +14 -5
  69. package/dist/components/IconButton.js +25 -14
  70. package/dist/components/InfoTip.d.ts +4 -1
  71. package/dist/components/InfoTip.js +2 -1
  72. package/dist/components/Input.d.ts +14 -8
  73. package/dist/components/Input.js +46 -31
  74. package/dist/components/Loader.js +1 -1
  75. package/dist/components/MenuEntries.d.ts +4 -0
  76. package/dist/components/MenuEntries.js +11 -12
  77. package/dist/components/Meter.js +5 -4
  78. package/dist/components/Modal.d.ts +28 -4
  79. package/dist/components/Modal.js +37 -14
  80. package/dist/components/Overview.d.ts +20 -12
  81. package/dist/components/Overview.js +14 -9
  82. package/dist/components/PageHeader.d.ts +47 -65
  83. package/dist/components/PageHeader.js +275 -153
  84. package/dist/components/PageHeaderAction.d.ts +67 -0
  85. package/dist/components/PageHeaderAction.js +107 -0
  86. package/dist/components/Pagination.js +6 -16
  87. package/dist/components/Popover.js +3 -2
  88. package/dist/components/Provider.d.ts +77 -19
  89. package/dist/components/Provider.js +139 -130
  90. package/dist/components/Radio.js +2 -1
  91. package/dist/components/RefreshButton.js +7 -8
  92. package/dist/components/RequiredMark.js +1 -1
  93. package/dist/components/Section.js +1 -1
  94. package/dist/components/Select.d.ts +4 -9
  95. package/dist/components/Select.js +24 -11
  96. package/dist/components/SettingModeOption.d.ts +1 -1
  97. package/dist/components/SettingModeOption.js +7 -1
  98. package/dist/components/SettingsCard.d.ts +5 -3
  99. package/dist/components/SettingsCard.js +27 -20
  100. package/dist/components/Slider.js +1 -1
  101. package/dist/components/StatusBadge.js +2 -2
  102. package/dist/components/Switch.js +5 -4
  103. package/dist/components/Table.d.ts +30 -20
  104. package/dist/components/Table.js +50 -73
  105. package/dist/components/Tabs.d.ts +3 -7
  106. package/dist/components/Tabs.js +6 -10
  107. package/dist/components/ThemeEditor.js +18 -8
  108. package/dist/components/Toast.d.ts +11 -3
  109. package/dist/components/Toast.js +45 -20
  110. package/dist/components/buttonClasses.js +1 -1
  111. package/dist/components/comboboxItems.d.ts +4 -0
  112. package/dist/components/comboboxItems.js +6 -1
  113. package/dist/components/dismiss.d.ts +12 -0
  114. package/dist/components/dismiss.js +19 -0
  115. package/dist/components/fieldBox.d.ts +4 -0
  116. package/dist/components/fieldBox.js +7 -3
  117. package/dist/components/floating.d.ts +3 -3
  118. package/dist/components/floating.js +1 -1
  119. package/dist/components/markdownCodeBreaks.js +3 -4
  120. package/dist/components/pageHeaderStore.d.ts +41 -0
  121. package/dist/components/pageHeaderStore.js +67 -0
  122. package/dist/components/pathBreaks.d.ts +8 -0
  123. package/dist/components/pathBreaks.js +28 -0
  124. package/dist/components/segmented.js +1 -1
  125. package/dist/components/shortcutKey.d.ts +2 -1
  126. package/dist/components/shortcutKey.js +19 -2
  127. package/dist/components/splitter.js +1 -1
  128. package/dist/components/useCssIsDark.js +40 -18
  129. package/dist/components/useTheme.js +9 -10
  130. package/dist/dev.d.ts +9 -0
  131. package/dist/dev.js +14 -0
  132. package/dist/editor/Editor.d.ts +3 -2
  133. package/dist/editor/Editor.js +11 -2
  134. package/dist/editor/EditorField.d.ts +2 -1
  135. package/dist/editor/EditorField.js +11 -29
  136. package/dist/editor/FormCodePanel.d.ts +7 -3
  137. package/dist/editor/FormCodePanel.js +14 -7
  138. package/dist/editor/Monaco.d.ts +14 -5
  139. package/dist/editor/Monaco.js +163 -112
  140. package/dist/editor/index.d.ts +5 -5
  141. package/dist/editor/index.js +3 -3
  142. package/dist/editor/themes.d.ts +8 -5
  143. package/dist/editor/themes.js +58 -30
  144. package/dist/editor/yaml.d.ts +6 -2
  145. package/dist/editor/yaml.js +11 -2
  146. package/dist/file-explorer/FileExplorer.js +143 -124
  147. package/dist/file-explorer/FileExplorerContext.js +1 -1
  148. package/dist/file-explorer/FilePreview/StreamedBadge.js +3 -1
  149. package/dist/file-explorer/FilePreview/Viewer.js +2 -2
  150. package/dist/file-explorer/FilePreview/index.js +4 -4
  151. package/dist/file-explorer/FileRow.js +1 -1
  152. package/dist/file-explorer/FileRowsWindow.js +34 -3
  153. package/dist/file-explorer/FileTable.js +3 -1
  154. package/dist/file-explorer/TreeView.js +37 -14
  155. package/dist/file-explorer/helpers.d.ts +2 -1
  156. package/dist/file-explorer/helpers.js +21 -20
  157. package/dist/file-explorer/toasts.js +27 -19
  158. package/dist/file-explorer/treeRows.d.ts +6 -0
  159. package/dist/file-explorer/treeRows.js +19 -1
  160. package/dist/file-explorer/useNearEnd.js +3 -6
  161. package/dist/form/Fields/CheckboxGroupField.js +12 -5
  162. package/dist/form/Fields/DurationField.js +1 -1
  163. package/dist/form/Fields/LabelField.js +1 -1
  164. package/dist/form/Fields/ObjectField.d.ts +1 -1
  165. package/dist/form/Fields/ObjectField.js +3 -2
  166. package/dist/form/Fields/StringField.js +16 -29
  167. package/dist/form/Fields/TextAreaField.js +3 -4
  168. package/dist/form/Form.js +3 -8
  169. package/dist/form/Wizard/WizardNavigation.js +0 -3
  170. package/dist/form/Wizard/utils.js +1 -1
  171. package/dist/form/fieldFrame.d.ts +1 -1
  172. package/dist/form/fieldFrame.js +2 -2
  173. package/dist/form/types/fieldComponentTypes.d.ts +2 -1
  174. package/dist/graph/AnnotationBanner.d.ts +1 -2
  175. package/dist/graph/AnnotationBanner.js +30 -32
  176. package/dist/graph/Connectors.js +4 -1
  177. package/dist/graph/DependencyGraph.js +2 -2
  178. package/dist/graph/GraphEditor.js +11 -9
  179. package/dist/graph/GraphEditorDialogs.js +1 -1
  180. package/dist/graph/GraphNode.js +7 -4
  181. package/dist/graph/InputDialog.js +1 -1
  182. package/dist/graph/InputsEditor.js +14 -11
  183. package/dist/graph/MatrixGroup.js +9 -3
  184. package/dist/graph/Reveal.js +18 -10
  185. package/dist/graph/ShortcutsButton.d.ts +11 -2
  186. package/dist/graph/ShortcutsButton.js +14 -3
  187. package/dist/graph/TreeView.js +35 -18
  188. package/dist/graph/WorkflowSettingsDialog.js +1 -1
  189. package/dist/graph/editorFields.js +1 -1
  190. package/dist/graph/editorRows.js +16 -10
  191. package/dist/graph/editorStrings.d.ts +7 -0
  192. package/dist/graph/editorStrings.js +8 -1
  193. package/dist/graph/inputRefs.js +1 -1
  194. package/dist/graph/settingsViews.js +11 -18
  195. package/dist/graph/stepWith.js +1 -1
  196. package/dist/icons.d.ts +2 -0
  197. package/dist/icons.js +4 -2
  198. package/dist/index.d.ts +12 -12
  199. package/dist/index.js +16 -16
  200. package/dist/list/ActionToolbar.d.ts +42 -6
  201. package/dist/list/ActionToolbar.js +297 -101
  202. package/dist/list/ListPage.d.ts +15 -23
  203. package/dist/list/ListPage.js +42 -43
  204. package/dist/list/ListTable.d.ts +32 -7
  205. package/dist/list/ListTable.js +48 -12
  206. package/dist/list/ListViewControls.js +1 -1
  207. package/dist/list/UserHoverCard.js +7 -15
  208. package/dist/list/index.d.ts +2 -2
  209. package/dist/list/index.js +2 -2
  210. package/dist/list/listSearch.js +12 -10
  211. package/dist/list/listView.d.ts +10 -1
  212. package/dist/list/listView.js +15 -1
  213. package/dist/logviewer/LogViewer.d.ts +4 -2
  214. package/dist/logviewer/LogViewer.js +87 -69
  215. package/dist/logviewer/logviewer.css +3 -2
  216. package/dist/reducedMotion.d.ts +5 -0
  217. package/dist/reducedMotion.js +20 -0
  218. package/dist/safeUrl.d.ts +1 -1
  219. package/dist/safeUrl.js +4 -2
  220. package/dist/styles.css +1 -1
  221. package/package.json +1 -1
  222. package/dist/components/Breadcrumbs.d.ts +0 -37
  223. package/dist/components/Breadcrumbs.js +0 -90
  224. package/dist/components/CopyToClipboard.d.ts +0 -17
  225. package/dist/components/CopyToClipboard.js +0 -66
  226. package/dist/components/Sort.d.ts +0 -9
  227. package/dist/components/Sort.js +0 -42
@@ -1,28 +1,66 @@
1
1
  import { cx } from "../cx.js";
2
- import { CheckIcon, CopyIcon } from "../icons.js";
3
2
  import { useStrings } from "./Provider.js";
4
- import { useCopy } from "./useCopy.js";
5
- import { IconButton } from "./IconButton.js";
6
3
  import { InfoTip } from "./InfoTip.js";
4
+ import { CopyButton } from "./CopyButton.js";
5
+ import { withPathBreaks } from "./pathBreaks.js";
7
6
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
- import { useId, useRef } from "react";
7
+ import { createContext, useContext, useId, useRef } from "react";
9
8
  //#region src/components/DescriptionList.tsx
10
9
  var variantClasses = {
11
10
  plain: "*:py-1",
12
11
  divided: "divide-y divide-(--theme-border) *:py-2",
13
12
  card: "panel divide-y divide-(--theme-border) px-4 *:py-2"
14
13
  };
14
+ var columns = (stacked) => cx("grid grid-cols-[minmax(0,1fr)]", !stacked && "@sm/dl:grid-cols-[fit-content(40%)_minmax(0,1fr)] @sm/dl:gap-x-6");
15
+ var InGroup = createContext(false);
16
+ var titleClassName = "col-span-full m-0 mb-1 text-sm font-semibold text-(--theme-app)";
15
17
  /** Names and their values, such as the details of a record in a drawer or a card. The
16
18
  * label column is as wide as its longest label, up to two fifths of the list, so a
17
19
  * long label wraps rather than squeezing the values; each label sits over its value
18
- * when the list is narrow. */
19
- function DescriptionList({ variant = "plain", stacked = false, className, children, ...props }) {
20
+ * when the list is narrow. Lists side by side in one panel share their label column
21
+ * inside a `DescriptionListGroup`. */
22
+ function DescriptionList({ variant = "plain", title, headingLevel = 3, stacked = false, className, children, ...props }) {
23
+ const grouped = useContext(InGroup);
24
+ const titleId = useId();
25
+ const Heading = `h${headingLevel}`;
26
+ const list = /* @__PURE__ */ jsx("dl", {
27
+ ...props,
28
+ className: cx("m-0 text-sm", grouped ? "col-span-full grid grid-cols-subgrid" : columns(stacked), variantClasses[variant]),
29
+ children
30
+ });
31
+ const named = title !== void 0 ? {
32
+ role: "group",
33
+ "aria-labelledby": titleId
34
+ } : {};
35
+ const heading = title !== void 0 && /* @__PURE__ */ jsx(Heading, {
36
+ id: titleId,
37
+ className: titleClassName,
38
+ children: title
39
+ });
40
+ if (grouped) return /* @__PURE__ */ jsxs("div", {
41
+ ...named,
42
+ className: cx("col-span-full grid grid-cols-subgrid", className),
43
+ children: [heading, list]
44
+ });
45
+ return /* @__PURE__ */ jsxs("div", {
46
+ ...named,
47
+ className: cx("@container/dl min-w-0", className),
48
+ children: [heading, list]
49
+ });
50
+ }
51
+ /** Description lists in one panel, such as a record's Overview, Actor and Target, that
52
+ * share one label column: it fits the longest label across all of them, so their
53
+ * values start at one edge down the panel. Each list keeps its own title and variant;
54
+ * all of them stack together when the panel is narrow. */
55
+ function DescriptionListGroup({ stacked = false, className, children }) {
20
56
  return /* @__PURE__ */ jsx("div", {
21
57
  className: cx("@container/dl min-w-0", className),
22
- children: /* @__PURE__ */ jsx("dl", {
23
- ...props,
24
- className: cx("m-0 grid grid-cols-[minmax(0,1fr)] text-sm", !stacked && "@sm/dl:grid-cols-[fit-content(40%)_minmax(0,1fr)] @sm/dl:gap-x-6", variantClasses[variant]),
25
- children
58
+ children: /* @__PURE__ */ jsx("div", {
59
+ className: cx(columns(stacked), "gap-y-6"),
60
+ children: /* @__PURE__ */ jsx(InGroup, {
61
+ value: true,
62
+ children
63
+ })
26
64
  })
27
65
  });
28
66
  }
@@ -46,7 +84,7 @@ function DescriptionListItem({ label, children, tooltip, mono = false, copy = fa
46
84
  children: isEmpty(children) ? /* @__PURE__ */ jsx(EmptyValue, {}) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
47
85
  "data-value": true,
48
86
  className: cx("min-w-0 leading-5 wrap-anywhere", mono && "font-mono text-[0.8125rem]"),
49
- children
87
+ children: withPathBreaks(children)
50
88
  }), copy !== false && /* @__PURE__ */ jsx(CopyValue, {
51
89
  text: copy === true ? void 0 : copy,
52
90
  label,
@@ -70,25 +108,21 @@ function EmptyValue() {
70
108
  });
71
109
  }
72
110
  function CopyValue({ text, label, labelId }) {
73
- const { common: t } = useStrings();
74
- const { copy, copied } = useCopy();
75
111
  const buttonId = useId();
76
112
  const button = useRef(null);
77
- const onClick = () => {
113
+ const shown = () => {
78
114
  const value = button.current?.closest("dd")?.querySelector("[data-value]");
79
- const shown = value?.innerText ?? value?.textContent;
80
- copy(text ?? shown ?? "", typeof label === "string" ? { label } : void 0);
115
+ return value?.innerText ?? value?.textContent ?? "";
81
116
  };
82
- return /* @__PURE__ */ jsx(IconButton, {
117
+ return /* @__PURE__ */ jsx(CopyButton, {
83
118
  ref: button,
84
119
  id: buttonId,
85
120
  size: "xs",
86
- label: copied ? t.copied : t.copy,
87
- "aria-labelledby": `${buttonId} ${labelId}`,
88
- icon: copied ? /* @__PURE__ */ jsx(CheckIcon, {}) : /* @__PURE__ */ jsx(CopyIcon, {}),
89
- onClick,
121
+ text: text ?? shown,
122
+ label: typeof label === "string" ? label : void 0,
123
+ "aria-labelledby": typeof label === "string" ? void 0 : `${buttonId} ${labelId}`,
90
124
  className: "-my-0.5"
91
125
  });
92
126
  }
93
127
  //#endregion
94
- export { DescriptionListItem, DescriptionList as default };
128
+ export { DescriptionListGroup, DescriptionListItem, DescriptionList as default };
@@ -17,7 +17,7 @@ function Disclosure({ summary, open, onOpenChange, defaultOpen = false, disabled
17
17
  disabled,
18
18
  className,
19
19
  children: [/* @__PURE__ */ jsxs(Collapsible.Trigger, {
20
- className: cx("group/disclosure flex min-w-0 items-start gap-2 text-left", disabled ? "cursor-default" : "cursor-pointer", summaryClassName),
20
+ className: cx("group/disclosure flex min-w-0 items-start gap-2 rounded-sm text-left focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--theme-focus)", disabled ? "cursor-default" : "cursor-pointer", summaryClassName),
21
21
  children: [summary, !disabled && /* @__PURE__ */ jsx(ChevronRightIcon, {
22
22
  "aria-hidden": "true",
23
23
  className: "mt-1 h-3.5 w-3.5 shrink-0 opacity-70 transition-transform group-data-panel-open/disclosure:rotate-90 motion-reduce:transition-none"
@@ -1,7 +1,8 @@
1
1
  import { cx } from "../cx.js";
2
2
  import { CloseIcon } from "../icons.js";
3
- import { pressedToast } from "./Toast.js";
4
3
  import { useStrings } from "./Provider.js";
4
+ import { IconButton } from "./IconButton.js";
5
+ import { closeHandler } from "./dismiss.js";
5
6
  import { useSplitter } from "./splitter.js";
6
7
  import { jsx, jsxs } from "react/jsx-runtime";
7
8
  import { useLayoutEffect, useRef, useState } from "react";
@@ -30,14 +31,7 @@ function Drawer$1({ open, onClose, title, description, footer, side = "right", w
30
31
  });
31
32
  const header = title !== void 0 || description !== void 0 || closeButton;
32
33
  const area = contained ? "absolute inset-0" : "fixed inset-x-0 bottom-0 top-(--app-inset-top,0px)";
33
- const onOpenChange = (next, details) => {
34
- if (next) return;
35
- if (!dismissible && details.reason === "escape-key" || details.reason === "outside-press" && pressedToast(details.event)) {
36
- details.cancel();
37
- return;
38
- }
39
- onClose();
40
- };
34
+ const onOpenChange = closeHandler(dismissible, onClose);
41
35
  return /* @__PURE__ */ jsxs(Drawer.Root, {
42
36
  open,
43
37
  onOpenChange,
@@ -70,24 +64,24 @@ function Drawer$1({ open, onClose, title, description, footer, side = "right", w
70
64
  children: [/* @__PURE__ */ jsxs("div", {
71
65
  className: "min-w-0 flex-1",
72
66
  children: [title !== void 0 && /* @__PURE__ */ jsx(Drawer.Title, {
73
- className: "truncate text-sm font-semibold",
67
+ className: "m-0 text-sm font-semibold wrap-break-word",
74
68
  children: title
75
69
  }), description !== void 0 && /* @__PURE__ */ jsx(Drawer.Description, {
76
70
  className: "mt-0.5 text-xs text-(--theme-muted-text-color)",
77
71
  children: description
78
72
  })]
79
- }), closeButton && /* @__PURE__ */ jsx(Drawer.Close, {
80
- "aria-label": common.close,
81
- className: "-m-1 shrink-0 cursor-pointer rounded-md p-1 text-(--theme-muted-text-color) transition-colors hover:bg-(--theme-muted-panel-bg) hover:text-(--theme-app)",
82
- children: /* @__PURE__ */ jsx(CloseIcon, { className: "h-4 w-4" })
83
- })]
73
+ }), closeButton && /* @__PURE__ */ jsx(Drawer.Close, { render: /* @__PURE__ */ jsx(IconButton, {
74
+ icon: /* @__PURE__ */ jsx(CloseIcon, {}),
75
+ label: common.close,
76
+ className: "-my-1 -me-1.5"
77
+ }) })]
84
78
  }),
85
79
  /* @__PURE__ */ jsx("div", {
86
80
  className: cx("relative flex min-h-0 flex-1 flex-col overflow-y-auto", padded && "p-4", className),
87
81
  children
88
82
  }),
89
83
  footer && /* @__PURE__ */ jsx("div", {
90
- className: "flex shrink-0 justify-end gap-2 border-t border-(--theme-border) px-4 py-3",
84
+ className: cx("flex shrink-0 items-center justify-end gap-2 border-t border-(--theme-border) px-5 py-3", "px-4"),
91
85
  children: footer
92
86
  })
93
87
  ]
@@ -17,11 +17,20 @@ export declare const FieldRowContext: import("react").Context<boolean>;
17
17
  /** The surrounding Field's control props, for a control drawn further down than the Field's
18
18
  * own children. Null outside a Field. */
19
19
  export declare function useFieldControl(): FieldControlProps | null;
20
+ /** The label's id, for a control that isn't a Base UI one and so isn't named by the label's
21
+ * `for`, such as a button that opens a picker. */
22
+ export declare function useFieldLabelId(): string | undefined;
20
23
  export type FieldLayout = 'stacked' | 'inline';
21
24
  export interface FieldProps {
22
25
  label: ReactNode;
23
- /** Beside the label and outside it, so it stays out of the control's name: an InfoTip, a count. */
26
+ /** Beside the label and outside it, so it stays out of the control's name, such as a count. For
27
+ * help text, use `help`. */
24
28
  hint?: ReactNode;
29
+ /** More about the field, behind a ? beside the label that opens on hover or press. It is read
30
+ * with the control as part of its description, so the ? stays out of the Tab order, and it shows
31
+ * under the control while the control has keyboard focus. `[text](url)` links and line breaks
32
+ * show as such. */
33
+ help?: string | undefined;
25
34
  /** Buttons at the far end of the label's row that act on the field, such as one that
26
35
  * switches its control to a typed expression. */
27
36
  actions?: ReactNode;
@@ -47,9 +56,10 @@ export interface FieldProps {
47
56
  /** The control's id; one is made up when left out. */
48
57
  id?: string | undefined;
49
58
  className?: string | undefined;
50
- /** The control, or a function that places the control props on it. A Base UI control, such
51
- * as Select, Combobox, NumberInput, Checkbox or Switch, needs only the `id`. A value shown
52
- * but not edited goes in a `FieldValue`. */
59
+ /** The control, or a function that places the control props on it. Any control of this
60
+ * package, such as Input, Select, Combobox, NumberInput, Switch or DatePicker, takes its label,
61
+ * description and invalid state from the Field on its own; a native element needs the
62
+ * function. A value shown but not edited goes in a `FieldValue`. */
53
63
  children: ReactNode | ((control: FieldControlProps) => ReactNode);
54
64
  }
55
65
  /** An error in the look a Field gives its own, for a control outside a Field, or for a part of
@@ -61,7 +71,7 @@ export declare function ErrorText({ id, children }: {
61
71
  }): import("react").JSX.Element | null;
62
72
  /** A control with its label, help and error, tied to it for assistive technology. One frame
63
73
  * for every field, in a form or standing alone. */
64
- export declare function Field({ label, hint, actions, description, error, invalid, required, disabled, layout, compact, size, group, id: ownId, className, children, }: FieldProps): import("react").JSX.Element;
74
+ export declare function Field({ label, hint, help, actions, description, error, invalid, required, disabled, layout, compact, size, group, id: ownId, className, children, }: FieldProps): import("react").JSX.Element;
65
75
  /** One checkbox or radio and its label inside a Field, such as an option of a group. A bare
66
76
  * `<label>` there would take the Field's label as its own, so every option read as the field. */
67
77
  export declare function FieldOption({ disabled, className, children, }: {
@@ -1,6 +1,7 @@
1
1
  import { cx } from "../cx.js";
2
2
  import { EyeIcon, EyeOffIcon, WarningIcon } from "../icons.js";
3
3
  import { useStrings } from "./Provider.js";
4
+ import { InfoTip, formatInfoTipContent } from "./InfoTip.js";
4
5
  import { RequiredMark } from "./RequiredMark.js";
5
6
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
7
  import { createContext, use, useId } from "react";
@@ -15,7 +16,12 @@ var FieldRowContext = createContext(false);
15
16
  /** The surrounding Field's control props, for a control drawn further down than the Field's
16
17
  * own children. Null outside a Field. */
17
18
  function useFieldControl() {
18
- return use(ControlContext);
19
+ return use(ControlContext)?.control ?? null;
20
+ }
21
+ /** The label's id, for a control that isn't a Base UI one and so isn't named by the label's
22
+ * `for`, such as a button that opens a picker. */
23
+ function useFieldLabelId() {
24
+ return use(ControlContext)?.labelId;
19
25
  }
20
26
  var labelSizes = {
21
27
  sm: {
@@ -48,14 +54,19 @@ function ErrorText({ id, children }) {
48
54
  }
49
55
  /** A control with its label, help and error, tied to it for assistive technology. One frame
50
56
  * for every field, in a form or standing alone. */
51
- function Field$1({ label, hint, actions, description, error, invalid, required = false, disabled = false, layout = "stacked", compact = false, size = "md", group = false, id: ownId, className, children }) {
57
+ function Field$1({ label, hint, help, actions, description, error, invalid, required = false, disabled = false, layout = "stacked", compact = false, size = "md", group = false, id: ownId, className, children }) {
52
58
  const uid = useId();
53
59
  const id = ownId ?? `${uid}control`;
54
60
  const labelId = `${uid}label`;
55
61
  const descriptionId = description ? `${uid}description` : void 0;
62
+ const helpId = help ? `${uid}help` : void 0;
56
63
  const errorId = error ? `${uid}error` : void 0;
57
64
  const isInvalid = Boolean(error) || Boolean(invalid);
58
- const describedBy = [descriptionId, errorId].filter(Boolean).join(" ");
65
+ const describedBy = [
66
+ descriptionId,
67
+ helpId,
68
+ errorId
69
+ ].filter(Boolean).join(" ");
59
70
  const control = {
60
71
  id,
61
72
  ...group && { "aria-labelledby": labelId },
@@ -63,26 +74,39 @@ function Field$1({ label, hint, actions, description, error, invalid, required =
63
74
  ...isInvalid && { "aria-invalid": true },
64
75
  ...required && { "aria-required": true }
65
76
  };
77
+ const controlContext = {
78
+ control,
79
+ labelId
80
+ };
66
81
  const inline = layout === "inline";
67
82
  const inRow = use(FieldRowContext) && !inline && !group;
68
83
  const labelSize = labelSizes[size];
84
+ const helpTip = help && /* @__PURE__ */ jsx(InfoTip, {
85
+ text: help,
86
+ tabIndex: -1
87
+ });
69
88
  const labelBox = cx(labelClassName, labelSize.text, inline && cx(labelSize.inlineHeight, "shrink-0", compact ? "mr-2 w-fit" : "w-52 pr-3"), inRow && "row-start-1 self-end");
70
89
  const labelContent = /* @__PURE__ */ jsxs(Fragment, { children: [
71
90
  /* @__PURE__ */ jsxs("span", { children: [label, required && /* @__PURE__ */ jsx(RequiredMark, {})] }),
91
+ helpTip,
72
92
  hint,
73
93
  actions && /* @__PURE__ */ jsx("span", {
74
94
  className: actionsClassName,
75
95
  children: actions
76
96
  })
77
97
  ] });
98
+ const placed = typeof children === "function" ? children(control) : children;
78
99
  const body = /* @__PURE__ */ jsxs("div", {
79
100
  className: cx("flex w-full min-w-0 flex-col gap-1", inRow && "row-start-2"),
80
101
  children: [
81
102
  /* @__PURE__ */ jsx(FieldRowContext, {
82
103
  value: false,
83
104
  children: /* @__PURE__ */ jsx(ControlContext, {
84
- value: control,
85
- children: typeof children === "function" ? children(control) : children
105
+ value: controlContext,
106
+ children: inline ? /* @__PURE__ */ jsx("div", {
107
+ className: cx("grid items-center", labelSize.inlineHeight),
108
+ children: placed
109
+ }) : placed
86
110
  })
87
111
  }),
88
112
  description && /* @__PURE__ */ jsx(Field.Description, {
@@ -90,6 +114,11 @@ function Field$1({ label, hint, actions, description, error, invalid, required =
90
114
  className: "text-xs leading-relaxed text-(--theme-muted-text-color)",
91
115
  children: description
92
116
  }),
117
+ help && /* @__PURE__ */ jsx(Field.Description, {
118
+ id: helpId,
119
+ className: "hidden text-xs leading-relaxed text-(--theme-muted-text-color) group-has-[:focus-visible]/field:block [&_a]:underline",
120
+ children: formatInfoTipContent(help)
121
+ }),
93
122
  error && /* @__PURE__ */ jsxs(Field.Error, {
94
123
  id: errorId,
95
124
  match: true,
@@ -102,7 +131,7 @@ function Field$1({ label, hint, actions, description, error, invalid, required =
102
131
  })
103
132
  ]
104
133
  });
105
- const frame = inRow ? cx("contents", className) : cx("flex w-full", inline ? "items-start" : cx("flex-col", labelSize.stackGap), className);
134
+ const frame = inRow ? cx("group/field contents", className) : cx("group/field flex w-full", inline ? "items-start" : cx("flex-col", labelSize.stackGap), className);
106
135
  if (group) return /* @__PURE__ */ jsx(Field.Root, {
107
136
  invalid: isInvalid,
108
137
  disabled,
@@ -128,11 +157,12 @@ function Field$1({ label, hint, actions, description, error, invalid, required =
128
157
  className: labelBox,
129
158
  children: [
130
159
  /* @__PURE__ */ jsxs(Field.Label, {
131
- htmlFor: id,
160
+ ...typeof children === "function" || ownId !== void 0 ? { htmlFor: id } : {},
132
161
  id: labelId,
133
162
  "data-field-label": true,
134
163
  children: [label, required && /* @__PURE__ */ jsx(RequiredMark, {})]
135
164
  }),
165
+ helpTip,
136
166
  hint,
137
167
  actions && /* @__PURE__ */ jsx("span", {
138
168
  className: actionsClassName,
@@ -177,8 +207,10 @@ function RevealToggle({ revealed, onRevealedChange, disabled }) {
177
207
  * DescriptionList. */
178
208
  function FieldValue({ size = "md", className, children, ...props }) {
179
209
  const control = useFieldControl();
210
+ const labelId = useFieldLabelId();
180
211
  return /* @__PURE__ */ jsx("output", {
181
212
  id: control?.id,
213
+ "aria-labelledby": labelId,
182
214
  "aria-describedby": control?.["aria-describedby"],
183
215
  ...props,
184
216
  className: cx("flex min-w-0 items-center break-words text-(--theme-app)", size === "sm" ? "min-h-7 text-xs" : "min-h-9 text-sm", className),
@@ -186,4 +218,4 @@ function FieldValue({ size = "md", className, children, ...props }) {
186
218
  });
187
219
  }
188
220
  //#endregion
189
- export { ErrorText, Field$1 as Field, FieldOption, FieldRowContext, FieldValue, RevealToggle, useFieldControl };
221
+ export { ErrorText, Field$1 as Field, FieldOption, FieldRowContext, FieldValue, RevealToggle, useFieldControl, useFieldLabelId };
@@ -1,6 +1,7 @@
1
1
  import { type ComponentPropsWithRef, type ReactNode } from 'react';
2
+ import { type AppLinkProps } from './AppLink.js';
2
3
  import { type ButtonSize, type ButtonVariant } from './buttonClasses.js';
3
- export interface IconButtonProps extends ComponentPropsWithRef<'button'> {
4
+ interface IconButtonOwnProps {
4
5
  icon: ReactNode;
5
6
  /** The accessible name, also shown as the tooltip. */
6
7
  label: string;
@@ -8,13 +9,21 @@ export interface IconButtonProps extends ComponentPropsWithRef<'button'> {
8
9
  size?: ButtonSize;
9
10
  /** A toggle's state: set, it reads as pressed (`aria-pressed`). */
10
11
  pressed?: boolean | undefined;
11
- /** A dot in the corner, such as for filters narrowing what the page shows. */
12
+ /** A dot in the corner, such as for filters narrowing what the page shows. It
13
+ * is only seen, so say what it means in `label` too, such as "Filters (2 on)". */
12
14
  dot?: boolean;
13
15
  /** A single key, such as F, that presses the button from anywhere on the page
14
16
  * outside a text field. Also shown in the tooltip. */
15
17
  shortcut?: string | undefined;
16
18
  }
17
- /** A button that shows only an icon, named by its tooltip. It can be a popup's
18
- * trigger: it passes the trigger's props and ref on, and hides its tooltip while
19
- * the popup is open. */
19
+ /** With `href` it is a link that looks like the button, for navigation. */
20
+ export type IconButtonProps = IconButtonOwnProps & ((ComponentPropsWithRef<'button'> & {
21
+ href?: undefined;
22
+ }) | (Omit<AppLinkProps, 'href'> & {
23
+ href: string;
24
+ }));
25
+ /** A button that shows only an icon, named by its tooltip, or with `href` a
26
+ * link that looks like one. It can be a popup's trigger: it passes the
27
+ * trigger's props and ref on, and hides its tooltip while the popup is open. */
20
28
  export declare function IconButton({ icon, label, variant, size, pressed, dot, shortcut, className, ref, ...props }: IconButtonProps): import("react").JSX.Element;
29
+ export {};
@@ -1,37 +1,48 @@
1
1
  import { cx } from "../cx.js";
2
2
  import { buttonClasses } from "./buttonClasses.js";
3
3
  import { Tooltip } from "./Tooltip.js";
4
+ import { AppLink } from "./AppLink.js";
4
5
  import { useShortcutKey } from "./shortcutKey.js";
5
- import { jsx, jsxs } from "react/jsx-runtime";
6
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
7
  import { useCallback, useRef } from "react";
7
8
  //#region src/components/IconButton.tsx
8
- /** A button that shows only an icon, named by its tooltip. It can be a popup's
9
- * trigger: it passes the trigger's props and ref on, and hides its tooltip while
10
- * the popup is open. */
9
+ /** A button that shows only an icon, named by its tooltip, or with `href` a
10
+ * link that looks like one. It can be a popup's trigger: it passes the
11
+ * trigger's props and ref on, and hides its tooltip while the popup is open. */
11
12
  function IconButton({ icon, label, variant = "ghost", size = "sm", pressed, dot = false, shortcut, className, ref, ...props }) {
12
13
  const button = useRef(null);
13
14
  const setButton = useCallback((node) => {
14
15
  button.current = node;
15
- if (typeof ref === "function") ref(node);
16
- else if (ref) ref.current = node;
16
+ const forward = ref;
17
+ if (typeof forward === "function") forward(node);
18
+ else if (forward) forward.current = node;
17
19
  }, [ref]);
20
+ const shared = {
21
+ "aria-label": label,
22
+ className: cx(buttonClasses({
23
+ variant,
24
+ size,
25
+ iconOnly: true
26
+ }), className)
27
+ };
28
+ const content = /* @__PURE__ */ jsxs(Fragment, { children: [icon, dot && /* @__PURE__ */ jsx("span", { className: "absolute right-0 top-0 h-1.5 w-1.5 rounded-full bg-(--theme-link)" })] });
18
29
  useShortcutKey(shortcut, () => button.current?.click());
19
30
  return /* @__PURE__ */ jsx(Tooltip, {
20
31
  content: label,
21
32
  shortcut: shortcut?.toUpperCase(),
22
33
  disabled: props["aria-expanded"] === true && !!props["aria-haspopup"],
23
- children: /* @__PURE__ */ jsxs("button", {
34
+ children: props.href !== void 0 ? /* @__PURE__ */ jsx(AppLink, {
35
+ ...shared,
36
+ ref: setButton,
37
+ ...props,
38
+ children: content
39
+ }) : /* @__PURE__ */ jsx("button", {
24
40
  type: "button",
25
- "aria-label": label,
26
41
  "aria-pressed": pressed,
42
+ ...shared,
27
43
  ref: setButton,
28
- className: cx(buttonClasses({
29
- variant,
30
- size,
31
- iconOnly: true
32
- }), className),
33
44
  ...props,
34
- children: [icon, dot && /* @__PURE__ */ jsx("span", { className: "absolute right-0 top-0 h-1.5 w-1.5 rounded-full bg-(--theme-link)" })]
45
+ children: content
35
46
  })
36
47
  });
37
48
  }
@@ -8,8 +8,11 @@ interface InfoTipProps {
8
8
  className?: string | undefined;
9
9
  /** Replaces the info icon as the trigger, such as a status label the text explains. */
10
10
  children?: ReactNode;
11
+ /** -1 takes the trigger out of the Tab order, for help that is also read another way, such as
12
+ * a Field's, which its control is described by. */
13
+ tabIndex?: number | undefined;
11
14
  }
12
15
  /** Extra detail behind an info trigger. A popover that also opens on hover, so
13
16
  * touch and screen reader users can reach it and its links. */
14
- export declare function InfoTip({ text, side, className, children }: InfoTipProps): import("react").JSX.Element | null;
17
+ export declare function InfoTip({ text, side, className, children, tabIndex }: InfoTipProps): import("react").JSX.Element | null;
15
18
  export {};
@@ -30,7 +30,7 @@ function formatInfoTipContent(content) {
30
30
  }
31
31
  /** Extra detail behind an info trigger. A popover that also opens on hover, so
32
32
  * touch and screen reader users can reach it and its links. */
33
- function InfoTip({ text, side = "top", className, children }) {
33
+ function InfoTip({ text, side = "top", className, children, tabIndex }) {
34
34
  const t = useStrings().common;
35
35
  if (!text) return children ? /* @__PURE__ */ jsx("span", {
36
36
  className,
@@ -42,6 +42,7 @@ function InfoTip({ text, side = "top", className, children }) {
42
42
  side,
43
43
  trigger: /* @__PURE__ */ jsx("button", {
44
44
  type: "button",
45
+ tabIndex,
45
46
  "aria-label": children ? void 0 : t.moreInfo,
46
47
  className: cx("inline-flex items-center cursor-help", !children && "tooltip", className),
47
48
  children: children ?? /* @__PURE__ */ jsx(HelpIcon, { "aria-hidden": "true" })
@@ -1,30 +1,36 @@
1
- import type { InputHTMLAttributes, ReactNode, TextareaHTMLAttributes } from 'react';
1
+ import type { InputHTMLAttributes, ReactNode, Ref, TextareaHTMLAttributes } from 'react';
2
2
  import { type FieldControlProps } from './Field.js';
3
3
  import { type FieldBoxSize } from './fieldBox.js';
4
4
  interface FramedProps {
5
- /** Without a label the box stands alone; name it with `aria-label`. */
5
+ /** Without a label the box stands alone: name it with `aria-label`, or place it in a Field. */
6
6
  label?: ReactNode;
7
7
  /** Beside the label and outside it, so it stays out of the field's name. */
8
8
  hint?: ReactNode;
9
+ /** More about the field, behind a ? beside the label; see Field's `help`. */
10
+ help?: string | undefined;
9
11
  /** Help under the field. */
10
12
  description?: ReactNode;
11
13
  error?: ReactNode;
12
14
  mono?: boolean;
13
- /** `sm` for dense places such as toolbars and table cells. */
15
+ /** `sm` for dense places such as toolbars and table cells. With a label, the label matches. */
14
16
  size?: FieldBoxSize | undefined;
15
17
  }
16
18
  export interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size'>, FramedProps {
19
+ ref?: Ref<HTMLInputElement> | undefined;
17
20
  }
18
21
  export interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement>, FramedProps {
22
+ ref?: Ref<HTMLTextAreaElement> | undefined;
19
23
  }
20
24
  /** The Field around a control when it has a label; the control alone when it doesn't. */
21
- export declare function framed({ label, hint, description, error }: Omit<FramedProps, 'mono' | 'size'>, { id, required, disabled, }: {
25
+ export declare function framed({ label, hint, help, description, error, size }: Omit<FramedProps, 'mono'>, { id, required, disabled, }: {
22
26
  id?: string | undefined;
23
27
  required?: boolean | undefined;
24
28
  disabled?: boolean | undefined;
25
29
  }, control: (props: Partial<FieldControlProps>) => ReactNode): string | number | bigint | boolean | import("react").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | Iterable<ReactNode> | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | import("react").ReactPortal | null | undefined> | null | undefined;
26
- /** A single-line text field. `type="password"` adds a button that shows what was typed. */
27
- export declare const Input: import("react").ForwardRefExoticComponent<InputProps & import("react").RefAttributes<HTMLInputElement>>;
28
- /** A multi-line text field that grows by hand. */
29
- export declare const Textarea: import("react").ForwardRefExoticComponent<TextareaProps & import("react").RefAttributes<HTMLTextAreaElement>>;
30
+ /** A single-line text field. `type="password"` adds a button that shows what was typed. Placed in
31
+ * a Field, it takes the Field's label, description and invalid state. */
32
+ export declare function Input({ label, hint, help, description, error, className, mono, size, type, ref, ...props }: InputProps): string | number | bigint | boolean | import("react").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | Iterable<ReactNode> | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | import("react").ReactPortal | null | undefined> | null | undefined;
33
+ /** A multi-line text field that grows by hand. Placed in a Field, it takes the Field's label,
34
+ * description and invalid state. */
35
+ export declare function Textarea({ label, hint, help, description, error, className, mono, size, ref, ...props }: TextareaProps): string | number | bigint | boolean | import("react").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | Iterable<ReactNode> | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | import("react").ReactPortal | null | undefined> | null | undefined;
30
36
  export {};