@mindlogic-ai/logician-ui 5.0.0-alpha.4 → 5.0.0-alpha.6

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.
@@ -15,5 +15,5 @@ import { FormModalFooterProps } from './FormModalFooter.types';
15
15
  * the logician `ModalFooter`, so callers pin it (`position="sticky"` /
16
16
  * `flexShrink={0}`) or zero its padding (`px={0}`) per modal.
17
17
  */
18
- export declare const FormModalFooter: ({ onDelete, deleteLabel, onCancel, cancelLabel, submitLabel, submitColorPalette, isSubmitting, submitDisabled, onSubmit, bordered, ...rest }: FormModalFooterProps) => import("react/jsx-runtime").JSX.Element;
18
+ export declare const FormModalFooter: ({ onDelete, deleteLabel, onCancel, cancelLabel, submitLabel, submitColorPalette, isSubmitting, submitLoadingText, submitDisabled, onSubmit, bordered, ...rest }: FormModalFooterProps) => import("react/jsx-runtime").JSX.Element;
19
19
  //# sourceMappingURL=FormModalFooter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"FormModalFooter.d.ts","sourceRoot":"","sources":["../../../../src/components/Form/FormModalFooter/FormModalFooter.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAE/D;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,eAAe,GAAI,8IAY7B,oBAAoB,4CA+CtB,CAAC"}
1
+ {"version":3,"file":"FormModalFooter.d.ts","sourceRoot":"","sources":["../../../../src/components/Form/FormModalFooter/FormModalFooter.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAE/D;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,eAAe,GAAI,iKAa7B,oBAAoB,4CAgDtB,CAAC"}
@@ -40,7 +40,7 @@ require('../../Typography/Text.js');
40
40
  * the logician `ModalFooter`, so callers pin it (`position="sticky"` /
41
41
  * `flexShrink={0}`) or zero its padding (`px={0}`) per modal.
42
42
  */
43
- const FormModalFooter = ({ onDelete, deleteLabel, onCancel, cancelLabel, submitLabel, submitColorPalette = 'primary', isSubmitting = false, submitDisabled = false, onSubmit, bordered = false, ...rest }) => {
43
+ const FormModalFooter = ({ onDelete, deleteLabel, onCancel, cancelLabel, submitLabel, submitColorPalette = 'primary', isSubmitting = false, submitLoadingText, submitDisabled = false, onSubmit, bordered = false, ...rest }) => {
44
44
  return (jsxRuntime.jsxs(ModalFooter.ModalFooter, { justifyContent: "flex-end", gap: 2, ...(bordered
45
45
  ? {
46
46
  bg: 'bg.surface',
@@ -53,7 +53,7 @@ const FormModalFooter = ({ onDelete, deleteLabel, onCancel, cancelLabel, submitL
53
53
  , {
54
54
  // 5.0 has no colour axis: the deprecated `submitColorPalette` only
55
55
  // picks between the two filled roles.
56
- variant: submitColorPalette === 'danger' ? 'danger' : 'primary', type: onSubmit ? 'button' : 'submit', onClick: onSubmit, loading: isSubmitting, disabled: isSubmitting || submitDisabled, children: submitLabel })] }));
56
+ variant: submitColorPalette === 'danger' ? 'danger' : 'primary', type: onSubmit ? 'button' : 'submit', onClick: onSubmit, loading: isSubmitting, loadingText: submitLoadingText, disabled: isSubmitting || submitDisabled, children: submitLabel })] }));
57
57
  };
58
58
 
59
59
  exports.FormModalFooter = FormModalFooter;
@@ -1 +1 @@
1
- {"version":3,"file":"FormModalFooter.js","sources":["../../../../src/components/Form/FormModalFooter/FormModalFooter.tsx"],"sourcesContent":[null],"names":["_jsxs","ModalFooter","Button","_jsx","TrashIcon"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAMA;;;;;;;;;;;;;;;AAeG;AACI,MAAM,eAAe,GAAG,CAAC,EAC9B,QAAQ,EACR,WAAW,EACX,QAAQ,EACR,WAAW,EACX,WAAW,EACX,kBAAkB,GAAG,SAAS,EAC9B,YAAY,GAAG,KAAK,EACpB,cAAc,GAAG,KAAK,EACtB,QAAQ,EACR,QAAQ,GAAG,KAAK,EAChB,GAAG,IAAI,EACc,KAAI;AACzB,IAAA,QACEA,eAAA,CAACC,uBAAW,EAAA,EACV,cAAc,EAAC,UAAU,EACzB,GAAG,EAAE,CAAC,EAAA,IACD;AACH,cAAE;AACE,gBAAA,EAAE,EAAE,YAAY;AAChB,gBAAA,SAAS,EAAE,WAAW;AACtB,gBAAA,WAAW,EAAE,eAAe;AAC7B;AACH,cAAE,EAAE,CAAC,EAAA,GACH,IAAI,EAAA,QAAA,EAAA,CAEP,QAAQ,KACPD,eAAA,CAACE,aAAM,EAAA,EACL,OAAO,EAAC,QAAQ,EAChB,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,QAAQ,EACjB,QAAQ,EAAE,YAAY,EACtB,EAAE,EAAC,MAAM,EAAA,QAAA,EAAA,CAETC,cAAA,CAACC,eAAS,EAAA,EAAC,OAAO,EAAC,IAAI,EAAA,CAAG,EACzB,WAAW,CAAA,EAAA,CACL,CACV,EACDD,cAAA,CAACD,aAAM,EAAA,EACL,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,QAAQ,EACjB,QAAQ,EAAE,YAAY,EAAA,QAAA,EAErB,WAAW,EAAA,CACL,EACTC,eAACD;;;;;;AAGC,gBAAA,OAAO,EAAE,kBAAkB,KAAK,QAAQ,GAAG,QAAQ,GAAG,SAAS,EAC/D,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,EACpC,OAAO,EAAE,QAAQ,EACjB,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,YAAY,IAAI,cAAc,EAAA,QAAA,EAEvC,WAAW,EAAA,CACL,CAAA,EAAA,CACG;AAElB;;;;"}
1
+ {"version":3,"file":"FormModalFooter.js","sources":["../../../../src/components/Form/FormModalFooter/FormModalFooter.tsx"],"sourcesContent":[null],"names":["_jsxs","ModalFooter","Button","_jsx","TrashIcon"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAMA;;;;;;;;;;;;;;;AAeG;AACI,MAAM,eAAe,GAAG,CAAC,EAC9B,QAAQ,EACR,WAAW,EACX,QAAQ,EACR,WAAW,EACX,WAAW,EACX,kBAAkB,GAAG,SAAS,EAC9B,YAAY,GAAG,KAAK,EACpB,iBAAiB,EACjB,cAAc,GAAG,KAAK,EACtB,QAAQ,EACR,QAAQ,GAAG,KAAK,EAChB,GAAG,IAAI,EACc,KAAI;AACzB,IAAA,QACEA,eAAA,CAACC,uBAAW,EAAA,EACV,cAAc,EAAC,UAAU,EACzB,GAAG,EAAE,CAAC,EAAA,IACD;AACH,cAAE;AACE,gBAAA,EAAE,EAAE,YAAY;AAChB,gBAAA,SAAS,EAAE,WAAW;AACtB,gBAAA,WAAW,EAAE,eAAe;AAC7B;AACH,cAAE,EAAE,CAAC,EAAA,GACH,IAAI,EAAA,QAAA,EAAA,CAEP,QAAQ,KACPD,eAAA,CAACE,aAAM,EAAA,EACL,OAAO,EAAC,QAAQ,EAChB,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,QAAQ,EACjB,QAAQ,EAAE,YAAY,EACtB,EAAE,EAAC,MAAM,EAAA,QAAA,EAAA,CAETC,cAAA,CAACC,eAAS,EAAA,EAAC,OAAO,EAAC,IAAI,EAAA,CAAG,EACzB,WAAW,CAAA,EAAA,CACL,CACV,EACDD,cAAA,CAACD,aAAM,EAAA,EACL,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,QAAQ,EACjB,QAAQ,EAAE,YAAY,EAAA,QAAA,EAErB,WAAW,EAAA,CACL,EACTC,eAACD;;;;;;gBAGC,OAAO,EAAE,kBAAkB,KAAK,QAAQ,GAAG,QAAQ,GAAG,SAAS,EAC/D,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,EACpC,OAAO,EAAE,QAAQ,EACjB,OAAO,EAAE,YAAY,EACrB,WAAW,EAAE,iBAAiB,EAC9B,QAAQ,EAAE,YAAY,IAAI,cAAc,YAEvC,WAAW,EAAA,CACL,CAAA,EAAA,CACG;AAElB;;;;"}
@@ -38,7 +38,7 @@ import '../../Typography/Text.mjs';
38
38
  * the logician `ModalFooter`, so callers pin it (`position="sticky"` /
39
39
  * `flexShrink={0}`) or zero its padding (`px={0}`) per modal.
40
40
  */
41
- const FormModalFooter = ({ onDelete, deleteLabel, onCancel, cancelLabel, submitLabel, submitColorPalette = 'primary', isSubmitting = false, submitDisabled = false, onSubmit, bordered = false, ...rest }) => {
41
+ const FormModalFooter = ({ onDelete, deleteLabel, onCancel, cancelLabel, submitLabel, submitColorPalette = 'primary', isSubmitting = false, submitLoadingText, submitDisabled = false, onSubmit, bordered = false, ...rest }) => {
42
42
  return (jsxs(ModalFooter, { justifyContent: "flex-end", gap: 2, ...(bordered
43
43
  ? {
44
44
  bg: 'bg.surface',
@@ -51,7 +51,7 @@ const FormModalFooter = ({ onDelete, deleteLabel, onCancel, cancelLabel, submitL
51
51
  , {
52
52
  // 5.0 has no colour axis: the deprecated `submitColorPalette` only
53
53
  // picks between the two filled roles.
54
- variant: submitColorPalette === 'danger' ? 'danger' : 'primary', type: onSubmit ? 'button' : 'submit', onClick: onSubmit, loading: isSubmitting, disabled: isSubmitting || submitDisabled, children: submitLabel })] }));
54
+ variant: submitColorPalette === 'danger' ? 'danger' : 'primary', type: onSubmit ? 'button' : 'submit', onClick: onSubmit, loading: isSubmitting, loadingText: submitLoadingText, disabled: isSubmitting || submitDisabled, children: submitLabel })] }));
55
55
  };
56
56
 
57
57
  export { FormModalFooter };
@@ -1 +1 @@
1
- {"version":3,"file":"FormModalFooter.mjs","sources":["../../../../src/components/Form/FormModalFooter/FormModalFooter.tsx"],"sourcesContent":[null],"names":["_jsxs","_jsx"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAMA;;;;;;;;;;;;;;;AAeG;AACI,MAAM,eAAe,GAAG,CAAC,EAC9B,QAAQ,EACR,WAAW,EACX,QAAQ,EACR,WAAW,EACX,WAAW,EACX,kBAAkB,GAAG,SAAS,EAC9B,YAAY,GAAG,KAAK,EACpB,cAAc,GAAG,KAAK,EACtB,QAAQ,EACR,QAAQ,GAAG,KAAK,EAChB,GAAG,IAAI,EACc,KAAI;AACzB,IAAA,QACEA,IAAA,CAAC,WAAW,EAAA,EACV,cAAc,EAAC,UAAU,EACzB,GAAG,EAAE,CAAC,EAAA,IACD;AACH,cAAE;AACE,gBAAA,EAAE,EAAE,YAAY;AAChB,gBAAA,SAAS,EAAE,WAAW;AACtB,gBAAA,WAAW,EAAE,eAAe;AAC7B;AACH,cAAE,EAAE,CAAC,EAAA,GACH,IAAI,EAAA,QAAA,EAAA,CAEP,QAAQ,KACPA,IAAA,CAAC,MAAM,EAAA,EACL,OAAO,EAAC,QAAQ,EAChB,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,QAAQ,EACjB,QAAQ,EAAE,YAAY,EACtB,EAAE,EAAC,MAAM,EAAA,QAAA,EAAA,CAETC,GAAA,CAAC,SAAS,EAAA,EAAC,OAAO,EAAC,IAAI,EAAA,CAAG,EACzB,WAAW,CAAA,EAAA,CACL,CACV,EACDA,GAAA,CAAC,MAAM,EAAA,EACL,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,QAAQ,EACjB,QAAQ,EAAE,YAAY,EAAA,QAAA,EAErB,WAAW,EAAA,CACL,EACTA,IAAC;;;;;;AAGC,gBAAA,OAAO,EAAE,kBAAkB,KAAK,QAAQ,GAAG,QAAQ,GAAG,SAAS,EAC/D,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,EACpC,OAAO,EAAE,QAAQ,EACjB,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,YAAY,IAAI,cAAc,EAAA,QAAA,EAEvC,WAAW,EAAA,CACL,CAAA,EAAA,CACG;AAElB;;;;"}
1
+ {"version":3,"file":"FormModalFooter.mjs","sources":["../../../../src/components/Form/FormModalFooter/FormModalFooter.tsx"],"sourcesContent":[null],"names":["_jsxs","_jsx"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAMA;;;;;;;;;;;;;;;AAeG;AACI,MAAM,eAAe,GAAG,CAAC,EAC9B,QAAQ,EACR,WAAW,EACX,QAAQ,EACR,WAAW,EACX,WAAW,EACX,kBAAkB,GAAG,SAAS,EAC9B,YAAY,GAAG,KAAK,EACpB,iBAAiB,EACjB,cAAc,GAAG,KAAK,EACtB,QAAQ,EACR,QAAQ,GAAG,KAAK,EAChB,GAAG,IAAI,EACc,KAAI;AACzB,IAAA,QACEA,IAAA,CAAC,WAAW,EAAA,EACV,cAAc,EAAC,UAAU,EACzB,GAAG,EAAE,CAAC,EAAA,IACD;AACH,cAAE;AACE,gBAAA,EAAE,EAAE,YAAY;AAChB,gBAAA,SAAS,EAAE,WAAW;AACtB,gBAAA,WAAW,EAAE,eAAe;AAC7B;AACH,cAAE,EAAE,CAAC,EAAA,GACH,IAAI,EAAA,QAAA,EAAA,CAEP,QAAQ,KACPA,IAAA,CAAC,MAAM,EAAA,EACL,OAAO,EAAC,QAAQ,EAChB,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,QAAQ,EACjB,QAAQ,EAAE,YAAY,EACtB,EAAE,EAAC,MAAM,EAAA,QAAA,EAAA,CAETC,GAAA,CAAC,SAAS,EAAA,EAAC,OAAO,EAAC,IAAI,EAAA,CAAG,EACzB,WAAW,CAAA,EAAA,CACL,CACV,EACDA,GAAA,CAAC,MAAM,EAAA,EACL,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,QAAQ,EACjB,QAAQ,EAAE,YAAY,EAAA,QAAA,EAErB,WAAW,EAAA,CACL,EACTA,IAAC;;;;;;gBAGC,OAAO,EAAE,kBAAkB,KAAK,QAAQ,GAAG,QAAQ,GAAG,SAAS,EAC/D,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,EACpC,OAAO,EAAE,QAAQ,EACjB,OAAO,EAAE,YAAY,EACrB,WAAW,EAAE,iBAAiB,EAC9B,QAAQ,EAAE,YAAY,IAAI,cAAc,YAEvC,WAAW,EAAA,CACL,CAAA,EAAA,CACG;AAElB;;;;"}
@@ -29,6 +29,13 @@ export interface FormModalFooterProps extends Omit<ModalFooterProps, 'children'
29
29
  * modal can't be dismissed or re-submitted mid-request.
30
30
  */
31
31
  isSubmitting?: boolean;
32
+ /**
33
+ * What the submit button says while `isSubmitting`, as a short present-tense
34
+ * state ("Saving", "저장 중"). Forwarded to the Button's `loadingText`, so the
35
+ * busy button keeps a name instead of becoming a bare spinner (Button usage:
36
+ * `loading` needs `loadingText`). The app owns the copy, like every label here.
37
+ */
38
+ submitLoadingText?: ReactNode;
32
39
  /**
33
40
  * Extra disable condition for the primary button beyond `isSubmitting` — a
34
41
  * pristine/invalid gate (e.g. `!name.trim()`).
@@ -1 +1 @@
1
- {"version":3,"file":"FormModalFooter.types.d.ts","sourceRoot":"","sources":["../../../../src/components/Form/FormModalFooter/FormModalFooter.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAElD,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,KAAK,gBAAgB,GAAG,cAAc,CAAC,OAAO,WAAW,CAAC,CAAC;AAE3D,MAAM,WAAW,oBAAqB,SAAQ,IAAI,CAChD,gBAAgB,EAChB,UAAU,GAAG,UAAU,GAAG,OAAO,CAClC;IACC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,+EAA+E;IAC/E,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,8BAA8B;IAC9B,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB;;;OAGG;IACH,WAAW,EAAE,SAAS,CAAC;IACvB,mCAAmC;IACnC,WAAW,EAAE,SAAS,CAAC;IACvB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB"}
1
+ {"version":3,"file":"FormModalFooter.types.d.ts","sourceRoot":"","sources":["../../../../src/components/Form/FormModalFooter/FormModalFooter.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAElD,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,KAAK,gBAAgB,GAAG,cAAc,CAAC,OAAO,WAAW,CAAC,CAAC;AAE3D,MAAM,WAAW,oBAAqB,SAAQ,IAAI,CAChD,gBAAgB,EAChB,UAAU,GAAG,UAAU,GAAG,OAAO,CAClC;IACC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,+EAA+E;IAC/E,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,8BAA8B;IAC9B,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB;;;OAGG;IACH,WAAW,EAAE,SAAS,CAAC;IACvB,mCAAmC;IACnC,WAAW,EAAE,SAAS,CAAC;IACvB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,SAAS,CAAC;IAC9B;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindlogic-ai/logician-ui",
3
- "version": "5.0.0-alpha.4",
3
+ "version": "5.0.0-alpha.6",
4
4
  "description": "A comprehensive React design system built on Chakra UI",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -121,7 +121,8 @@ What these parts do for you:
121
121
  - `FormModalFooter` keeps the submit a `type="submit"` button inside a
122
122
  `<form>`, so Enter submits; pass `onSubmit` only for a portalled or
123
123
  form-less modal. `isSubmitting` disables every button so the modal cannot be
124
- dismissed mid-request.
124
+ dismissed mid-request. Pair it with `submitLoadingText` ("Saving") so the
125
+ busy submit button still says what it is doing.
125
126
  - A collapsed `FormSection` with `hasError` swaps its chevron for a danger
126
127
  glyph, so hidden errors are not missed.
127
128
 
@@ -172,6 +173,7 @@ top rule, `FormSection` `divider` uses `border.subtle`, and `FieldError` uses
172
173
  - `FormModalFooter`는 `<form>` 안에서 제출 버튼을 `type="submit"`으로 두어 Enter로
173
174
  제출되게 합니다. `onSubmit`은 포털로 그려지거나 `<form>`이 없는 모달에서만 넘깁니다.
174
175
  `isSubmitting`이면 모든 버튼이 비활성이 되어 요청 중에 모달이 닫히지 않습니다.
176
+ `submitLoadingText`("저장 중")를 함께 넘기면 요청 중인 제출 버튼도 무엇을 하는지 말합니다.
175
177
  - `hasError`인 `FormSection`이 접혀 있으면 펼침 화살표 대신 위험 표시가 보여 숨은
176
178
  오류를 놓치지 않습니다.
177
179
 
@@ -50,7 +50,7 @@ export const WithDelete: Story = {
50
50
 
51
51
  /** Mid-request: spinner on submit, every button disabled. */
52
52
  export const Submitting: Story = {
53
- args: { isSubmitting: true },
53
+ args: { isSubmitting: true, submitLoadingText: 'Saving' },
54
54
  };
55
55
 
56
56
  /** Gated submit (e.g. a required field is empty). */
@@ -20,12 +20,14 @@ vi.mock('@/components/Button', () => ({
20
20
  type,
21
21
  disabled,
22
22
  loading,
23
+ loadingText,
23
24
  }: {
24
25
  children?: ReactNode;
25
26
  onClick?: () => void;
26
27
  type?: 'button' | 'submit';
27
28
  disabled?: boolean;
28
29
  loading?: boolean;
30
+ loadingText?: ReactNode;
29
31
  }) => (
30
32
  <button
31
33
  type={type}
@@ -33,7 +35,7 @@ vi.mock('@/components/Button', () => ({
33
35
  disabled={disabled}
34
36
  data-loading={loading ? 'true' : 'false'}
35
37
  >
36
- {children}
38
+ {loading && loadingText ? loadingText : children}
37
39
  </button>
38
40
  ),
39
41
  }));
@@ -139,6 +141,22 @@ describe('FormModalFooter', () => {
139
141
  expect(submit).toHaveAttribute('data-loading', 'true');
140
142
  });
141
143
 
144
+ it('names the busy submit button with submitLoadingText', () => {
145
+ render(
146
+ <FormModalFooter
147
+ onCancel={noop}
148
+ onSubmit={noop}
149
+ cancelLabel="Cancel"
150
+ submitLabel="Save"
151
+ submitLoadingText="Saving"
152
+ isSubmitting
153
+ />
154
+ );
155
+ const submit = screen.getByRole('button', { name: 'Saving' });
156
+ expect(submit).toBeDisabled();
157
+ expect(submit).toHaveAttribute('data-loading', 'true');
158
+ });
159
+
142
160
  it('disables only the submit button when submitDisabled is set', () => {
143
161
  render(
144
162
  <FormModalFooter
@@ -28,6 +28,7 @@ export const FormModalFooter = ({
28
28
  submitLabel,
29
29
  submitColorPalette = 'primary',
30
30
  isSubmitting = false,
31
+ submitLoadingText,
31
32
  submitDisabled = false,
32
33
  onSubmit,
33
34
  bordered = false,
@@ -73,6 +74,7 @@ export const FormModalFooter = ({
73
74
  type={onSubmit ? 'button' : 'submit'}
74
75
  onClick={onSubmit}
75
76
  loading={isSubmitting}
77
+ loadingText={submitLoadingText}
76
78
  disabled={isSubmitting || submitDisabled}
77
79
  >
78
80
  {submitLabel}
@@ -35,6 +35,13 @@ export interface FormModalFooterProps extends Omit<
35
35
  * modal can't be dismissed or re-submitted mid-request.
36
36
  */
37
37
  isSubmitting?: boolean;
38
+ /**
39
+ * What the submit button says while `isSubmitting`, as a short present-tense
40
+ * state ("Saving", "저장 중"). Forwarded to the Button's `loadingText`, so the
41
+ * busy button keeps a name instead of becoming a bare spinner (Button usage:
42
+ * `loading` needs `loadingText`). The app owns the copy, like every label here.
43
+ */
44
+ submitLoadingText?: ReactNode;
38
45
  /**
39
46
  * Extra disable condition for the primary button beyond `isSubmitting` — a
40
47
  * pristine/invalid gate (e.g. `!name.trim()`).