@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.
- package/dist/components/settings-section.d.ts +3 -3
- package/dist/components/settings-section.js +3 -3
- package/dist/internal/settings-save.d.ts +3 -3
- package/dist/internal/settings-save.d.ts.map +1 -1
- package/dist/internal/settings-save.js +5 -7
- package/dist/showcase/specimens/compositions.js +2 -2
- package/dist/showcase/usage.generated.js +3 -3
- package/package.json +2 -2
|
@@ -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
|
|
37
|
-
* @words The title names the values ("Personal details"); the description says what changing them does, in one line; the hint says
|
|
38
|
-
* @access The title is the section's h2 and names the form that saves it. The
|
|
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
|
|
15
|
-
* @words The title names the values ("Personal details"); the description says what changing them does, in one line; the hint says
|
|
16
|
-
* @access The title is the section's h2 and names the form that saves it. The
|
|
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
|
-
/**
|
|
16
|
-
|
|
17
|
-
/**
|
|
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":"
|
|
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
|
|
56
|
+
function SaveFooter({ save, changed }) {
|
|
58
57
|
const { pending } = useFormStatus();
|
|
59
|
-
return (_jsxs(FooterStrip, { hint:
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
240
|
-
hint: "
|
|
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
|
|
234
|
-
words: 'The title names the values ("Personal details"); the description says what changing them does, in one line; the hint says
|
|
235
|
-
access: "The title is the section's h2 and names the form that saves it. The
|
|
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.
|
|
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.
|
|
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",
|