@nadicodeai/ui 12.1.100 → 12.1.101

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.
@@ -33,9 +33,9 @@ export type SettingsSectionProps = Omit<React.ComponentProps<"section">, "title"
33
33
  * @family Compositions
34
34
  * @when A few related values a person changes and saves together, on a SettingsPage or as a section of an ObjectPage: Personal details, Company identity, Notifications.
35
35
  * @instead Read-only values are a SettingsFactsSection; Theme and Language, which apply on change, live in the account menu; a record's edit in a list is a RowEditForm.
36
- * @behaviour With `save`: Save stays disabled until a field differs from what is saved, says `pendingLabel` while saving with Cancel held beside it, then the footer shows the ok check and `savedLabel` until the next change. A failure keeps every value and shows under its field until that field changes, or above the footer until the next save. A `cancel` that runs work of its own says its `pendingLabel` while `pending`. Without `save`, the footer holds `footerHint` and `footer`, and is left out when both are.
37
- * @words The title names the values ("Personal details"); the description says what changing them does, in one line; the hint says where the save applies ("Saved for every device you sign in on."); Save, Saving and Saved come from the product's catalogue.
38
- * @access The title is the section's h2 and names the form that saves it. The footer's hint and saved line are one status, announced politely; a failure under a field marks the control invalid and describes it.
36
+ * @behaviour With `save`: Save stays disabled until a field differs from what is saved, says `pendingLabel` while saving with Cancel held beside it, then calls `saved`, which the product answers with its own toast; the footer never shows a saved state. A failure keeps every value and shows under its field until that field changes, or above the footer until the next save. A `cancel` that runs work of its own says its `pendingLabel` while `pending`. Without `save`, the footer holds `footerHint` and `footer`, and is left out when both are.
37
+ * @words The title names the values ("Personal details"); the description says what changing them does, in one line; the hint is optional and says what is specific to this card: a limit ("Please use 32 characters at maximum."), what a value is for, the plan a feature needs, or a "Learn more" link, never one sentence every card repeats; Save and Saving come from the product's catalogue, and the saved toast from its notices.
38
+ * @access The title is the section's h2 and names the form that saves it. The saved toast is announced by the product's toast region; a failure under a field marks the control invalid and describes it.
39
39
  */
40
40
  declare function SettingsSection({ title, description, actions, columns, children, className, save, footer, footerHint, ...props }: SettingsSectionProps): React.JSX.Element;
41
41
  /**
@@ -11,9 +11,9 @@ import { Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLab
11
11
  * @family Compositions
12
12
  * @when A few related values a person changes and saves together, on a SettingsPage or as a section of an ObjectPage: Personal details, Company identity, Notifications.
13
13
  * @instead Read-only values are a SettingsFactsSection; Theme and Language, which apply on change, live in the account menu; a record's edit in a list is a RowEditForm.
14
- * @behaviour With `save`: Save stays disabled until a field differs from what is saved, says `pendingLabel` while saving with Cancel held beside it, then the footer shows the ok check and `savedLabel` until the next change. A failure keeps every value and shows under its field until that field changes, or above the footer until the next save. A `cancel` that runs work of its own says its `pendingLabel` while `pending`. Without `save`, the footer holds `footerHint` and `footer`, and is left out when both are.
15
- * @words The title names the values ("Personal details"); the description says what changing them does, in one line; the hint says where the save applies ("Saved for every device you sign in on."); Save, Saving and Saved come from the product's catalogue.
16
- * @access The title is the section's h2 and names the form that saves it. The footer's hint and saved line are one status, announced politely; a failure under a field marks the control invalid and describes it.
14
+ * @behaviour With `save`: Save stays disabled until a field differs from what is saved, says `pendingLabel` while saving with Cancel held beside it, then calls `saved`, which the product answers with its own toast; the footer never shows a saved state. A failure keeps every value and shows under its field until that field changes, or above the footer until the next save. A `cancel` that runs work of its own says its `pendingLabel` while `pending`. Without `save`, the footer holds `footerHint` and `footer`, and is left out when both are.
15
+ * @words The title names the values ("Personal details"); the description says what changing them does, in one line; the hint is optional and says what is specific to this card: a limit ("Please use 32 characters at maximum."), what a value is for, the plan a feature needs, or a "Learn more" link, never one sentence every card repeats; Save and Saving come from the product's catalogue, and the saved toast from its notices.
16
+ * @access The title is the section's h2 and names the form that saves it. The saved toast is announced by the product's toast region; a failure under a field marks the control invalid and describes it.
17
17
  */
18
18
  function SettingsSection({ title, description, actions, columns = 1, children, className, save, footer, footerHint, ...props }) {
19
19
  const titleId = React.useId();
@@ -12,9 +12,9 @@ export type SettingsSave = {
12
12
  label: string;
13
13
  /** "Saving" */
14
14
  pendingLabel: string;
15
- /** "Saved" */
16
- savedLabel: string;
17
- /** The footer's left side until a save: "Saved for every device you sign in on." */
15
+ /** Called once a save resolves null: the product announces it through its own toast. */
16
+ saved: () => void;
17
+ /** What is specific to this card, on the footer's left: a limit, what a value is for, a plan it needs, a link. Absent: nothing. */
18
18
  hint?: ReactNode;
19
19
  /**
20
20
  * An in-place edit of an object's section closes without saving. A cancel that runs work of its own, such as
@@ -1 +1 @@
1
- {"version":3,"file":"settings-save.d.ts","sourceRoot":"","sources":["../../src/internal/settings-save.tsx"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,SAAS,EAMf,MAAM,OAAO,CAAC;AAOf,OAAO,EAAoB,KAAK,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAOtF,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,eAAe,CAAC;IACzB,2FAA2F;IAC3F,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,+DAA+D;IAC/D,MAAM,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAC;IACpE,aAAa;IACb,KAAK,EAAE,MAAM,CAAC;IACd,eAAe;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,oFAAoF;IACpF,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;;OAGG;IACH,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,IAAI,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3F;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAsHF,wBAAgB,gBAAgB,CAAC,EAC/B,IAAI,EACJ,OAAO,EACP,QAAQ,GACT,EAAE;IACD,IAAI,EAAE,YAAY,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,SAAS,CAAC;CACrB,+BAoFA;AAED,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,EAAE,EAAE;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,sCA2B1D"}
1
+ {"version":3,"file":"settings-save.d.ts","sourceRoot":"","sources":["../../src/internal/settings-save.tsx"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,SAAS,EAMf,MAAM,OAAO,CAAC;AAOf,OAAO,EAAoB,KAAK,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAOtF,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,eAAe,CAAC;IACzB,2FAA2F;IAC3F,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,+DAA+D;IAC/D,MAAM,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAC;IACpE,aAAa;IACb,KAAK,EAAE,MAAM,CAAC;IACd,eAAe;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,wFAAwF;IACxF,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,mIAAmI;IACnI,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;;OAGG;IACH,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,IAAI,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3F;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAgGF,wBAAgB,gBAAgB,CAAC,EAC/B,IAAI,EACJ,OAAO,EACP,QAAQ,GACT,EAAE;IACD,IAAI,EAAE,YAAY,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,SAAS,CAAC;CACrB,+BAkFA;AAED,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,EAAE,EAAE;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,sCA2B1D"}
@@ -1,6 +1,5 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { CircleCheck } from "lucide-react";
4
3
  import { createContext, use, useCallback, useEffect, useRef, useState, } from "react";
5
4
  import { useFormStatus } from "react-dom";
6
5
  import { Button } from "../components/button.js";
@@ -54,9 +53,9 @@ function editableValues(form) {
54
53
  ? [name, [value.name, value.size, value.type, value.lastModified]]
55
54
  : [name, value]));
56
55
  }
57
- function SaveFooter({ save, changed, saved, }) {
56
+ function SaveFooter({ save, changed }) {
58
57
  const { pending } = useFormStatus();
59
- return (_jsxs(FooterStrip, { hint: _jsx("div", { role: "status", children: saved ? (_jsxs("span", { className: "inline-flex items-center gap-1.5 text-success", children: [_jsx(CircleCheck, { "aria-hidden": "true", className: "size-4" }), save.savedLabel] })) : (save.hint) }), hintSlot: "settings-section-hint", children: [save.cancel ? (_jsx(Button, { "aria-busy": save.cancel.pending || undefined, disabled: pending || save.cancel.pending, onClick: save.cancel.onCancel, type: "button", variant: "outline", children: _jsx(PendingLabel, { pending: save.cancel.pending ?? false, pendingLabel: save.cancel.pendingLabel ?? save.cancel.label, children: save.cancel.label }) })) : null, _jsx(FormSubmitButton, { submit: {
58
+ return (_jsxs(FooterStrip, { hint: save.hint, hintSlot: "settings-section-hint", children: [save.cancel ? (_jsx(Button, { "aria-busy": save.cancel.pending || undefined, disabled: pending || save.cancel.pending, onClick: save.cancel.onCancel, type: "button", variant: "outline", children: _jsx(PendingLabel, { pending: save.cancel.pending ?? false, pendingLabel: save.cancel.pendingLabel ?? save.cancel.label, children: save.cancel.label }) })) : null, _jsx(FormSubmitButton, { submit: {
60
59
  label: save.label,
61
60
  pendingLabel: save.pendingLabel,
62
61
  pending,
@@ -66,7 +65,6 @@ function SaveFooter({ save, changed, saved, }) {
66
65
  export function SettingsSaveForm({ save, titleId, children, }) {
67
66
  const [fieldsChanged, setFieldsChanged] = useState(false);
68
67
  const [failure, setFailure] = useState(null);
69
- const [saved, setSaved] = useState(false);
70
68
  const [claimedFields, setClaimedFields] = useState(() => new Set());
71
69
  const submittedValues = useRef(null);
72
70
  const keepValuesOnReset = useRef(false);
@@ -103,20 +101,20 @@ export function SettingsSaveForm({ save, titleId, children, }) {
103
101
  const result = await save.action(formData);
104
102
  keepValuesOnReset.current = result !== null;
105
103
  setFailure(result);
106
- setSaved(result === null);
104
+ if (result === null)
105
+ save.saved();
107
106
  }, onChange: (event) => {
108
107
  const edited = event.target;
109
108
  if (edited instanceof Element &&
110
109
  failure?.field !== undefined &&
111
110
  edited.id === failure.field)
112
111
  setFailure(null);
113
- setSaved(false);
114
112
  setFieldsChanged(hasChangedFields(event.currentTarget));
115
113
  }, onSubmit: (event) => {
116
114
  setFailure(null);
117
115
  keepValuesOnReset.current = false;
118
116
  submittedValues.current = editableValues(event.currentTarget);
119
- }, children: [_jsx("fieldset", { className: "contents", disabled: !hydrated, children: children }), sectionFailure ? (_jsx(CardContent, { children: _jsx(MessageNotice, { message: sectionFailure.message, placement: "action" }) })) : null, _jsx(SaveFooter, { changed: changed, save: save, saved: saved && !changed })] }) }));
117
+ }, children: [_jsx("fieldset", { className: "contents", disabled: !hydrated, children: children }), sectionFailure ? (_jsx(CardContent, { children: _jsx(MessageNotice, { message: sectionFailure.message, placement: "action" }) })) : null, _jsx(SaveFooter, { changed: changed, save: save })] }) }));
120
118
  }
121
119
  export function SettingsFieldFailure({ id }) {
122
120
  const context = use(SettingsSaveContext);
@@ -236,8 +236,8 @@ function SettingsSectionSpecimen() {
236
236
  action: async () => null,
237
237
  label: "Save",
238
238
  pendingLabel: "Saving",
239
- savedLabel: "Saved",
240
- hint: "Saved for every device you sign in on.",
239
+ saved: () => undefined,
240
+ hint: "Please use 32 characters at maximum.",
241
241
  }, title: "Sender", children: _jsx(SettingsField, { id: "showcase-sender-name", label: "Sender name", children: ({ controlProps }) => (_jsx(Input, { ...controlProps, defaultValue: "Acme Quotes", name: "senderName" })) }) }));
242
242
  }
243
243
  export const COMPOSITIONS = {
@@ -230,9 +230,9 @@ export const USAGE = [
230
230
  summary: "A card of related settings that says what changing them does and saves them from its own footer.",
231
231
  when: "A few related values a person changes and saves together, on a SettingsPage or as a section of an ObjectPage: Personal details, Company identity, Notifications.",
232
232
  instead: "Read-only values are a SettingsFactsSection; Theme and Language, which apply on change, live in the account menu; a record's edit in a list is a RowEditForm.",
233
- behaviour: "With `save`: Save stays disabled until a field differs from what is saved, says `pendingLabel` while saving with Cancel held beside it, then the footer shows the ok check and `savedLabel` until the next change. A failure keeps every value and shows under its field until that field changes, or above the footer until the next save. A `cancel` that runs work of its own says its `pendingLabel` while `pending`. Without `save`, the footer holds `footerHint` and `footer`, and is left out when both are.",
234
- words: 'The title names the values ("Personal details"); the description says what changing them does, in one line; the hint says where the save applies ("Saved for every device you sign in on."); Save, Saving and Saved come from the product\'s catalogue.',
235
- access: "The title is the section's h2 and names the form that saves it. The footer's hint and saved line are one status, announced politely; a failure under a field marks the control invalid and describes it.",
233
+ behaviour: "With `save`: Save stays disabled until a field differs from what is saved, says `pendingLabel` while saving with Cancel held beside it, then calls `saved`, which the product answers with its own toast; the footer never shows a saved state. A failure keeps every value and shows under its field until that field changes, or above the footer until the next save. A `cancel` that runs work of its own says its `pendingLabel` while `pending`. Without `save`, the footer holds `footerHint` and `footer`, and is left out when both are.",
234
+ words: 'The title names the values ("Personal details"); the description says what changing them does, in one line; the hint is optional and says what is specific to this card: a limit ("Please use 32 characters at maximum."), what a value is for, the plan a feature needs, or a "Learn more" link, never one sentence every card repeats; Save and Saving come from the product\'s catalogue, and the saved toast from its notices.',
235
+ access: "The title is the section's h2 and names the form that saves it. The saved toast is announced by the product's toast region; a failure under a field marks the control invalid and describes it.",
236
236
  },
237
237
  {
238
238
  name: "Button",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nadicodeai/ui",
3
- "version": "12.1.100",
3
+ "version": "12.1.101",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -80,7 +80,7 @@
80
80
  "@base-ui/react": "^1.9.0",
81
81
  "@iconify-icons/logos": "^2.0.2",
82
82
  "@iconify/react": "^6.0.2",
83
- "@nadicodeai/design-system": "12.1.100",
83
+ "@nadicodeai/design-system": "12.1.101",
84
84
  "@tanstack/react-table": "^9.2.8",
85
85
  "class-variance-authority": "^0.7.1",
86
86
  "clsx": "^2.1.1",