dishui 0.0.105 → 0.0.108
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/.release-verified.json +2 -2
- package/dist/components/ChoicePicker/index.js +8 -6
- package/dist/components/ChoicePicker/index.js.map +1 -1
- package/dist/components/Column/index.d.ts +3 -2
- package/dist/components/Column/index.js +5 -17
- package/dist/components/Column/index.js.map +1 -1
- package/dist/components/ConfirmDialog/index.d.ts +20 -0
- package/dist/components/ConfirmDialog/index.js +84 -0
- package/dist/components/ConfirmDialog/index.js.map +1 -0
- package/dist/components/DateTimeInput/index.js +28 -26
- package/dist/components/DateTimeInput/index.js.map +1 -1
- package/dist/components/Input/index.js +1 -1
- package/dist/components/Input/index.js.map +1 -1
- package/dist/components/MultipleChoice/index.js +2 -1
- package/dist/components/MultipleChoice/index.js.map +1 -1
- package/dist/components/Row/index.d.ts +3 -2
- package/dist/components/Row/index.js +5 -17
- package/dist/components/Row/index.js.map +1 -1
- package/dist/components/Tab/index.js +80 -0
- package/dist/components/Tab/index.js.map +1 -0
- package/dist/components/Tabs/index.js +28 -33
- package/dist/components/Tabs/index.js.map +1 -1
- package/dist/components/Tabs/tabNavigation.d.ts +2 -0
- package/dist/components/Tabs/tabNavigation.js +14 -0
- package/dist/components/Tabs/tabNavigation.js.map +1 -0
- package/dist/components/pro/CodeEditor/editors/MarkdownEditor.d.ts +1 -1
- package/dist/components/pro/CodeEditor/editors/MarkdownEditor.js +25 -26
- package/dist/components/pro/CodeEditor/editors/MarkdownEditor.js.map +1 -1
- package/dist/components/pro/FileExplorer/Breadcrumb.js +8 -11
- package/dist/components/pro/FileExplorer/Breadcrumb.js.map +1 -1
- package/dist/components/pro/Flow/Flow.js +184 -182
- package/dist/components/pro/Flow/Flow.js.map +1 -1
- package/dist/components/pro/Flow/archify/convert.js +26 -26
- package/dist/components/pro/Flow/archify/convert.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useLayoutActions.js +13 -11
- package/dist/components/pro/Flow/hooks/useLayoutActions.js.map +1 -1
- package/dist/components/pro/RichTextEditor/AIRewrite.d.ts +1 -0
- package/dist/components/pro/RichTextEditor/AIRewrite.js +109 -88
- package/dist/components/pro/RichTextEditor/AIRewrite.js.map +1 -1
- package/dist/components/pro/RichTextEditor/BubbleToolbar.js +73 -83
- package/dist/components/pro/RichTextEditor/BubbleToolbar.js.map +1 -1
- package/dist/components/pro/RichTextEditor/RichTextEditor.js +133 -132
- package/dist/components/pro/RichTextEditor/RichTextEditor.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/ColorPicker.d.ts +2 -0
- package/dist/components/pro/RichTextEditor/Toolbar/ColorPicker.js +73 -49
- package/dist/components/pro/RichTextEditor/Toolbar/ColorPicker.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/SelectDropdown.d.ts +1 -0
- package/dist/components/pro/RichTextEditor/Toolbar/SelectDropdown.js +12 -14
- package/dist/components/pro/RichTextEditor/Toolbar/SelectDropdown.js.map +1 -1
- package/dist/components/pro/RichTextEditor/extensions/CodeBlockView.js +113 -113
- package/dist/components/pro/RichTextEditor/extensions/CodeBlockView.js.map +1 -1
- package/dist/components/pro/RichTextEditor/types.d.ts +2 -0
- package/dist/components/pro/RichTextEditor/types.js.map +1 -1
- package/dist/css.d.ts +1 -0
- package/dist/dishui.css +1 -1
- package/dist/index-nostyled.d.ts +2 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +113 -111
- package/dist/layouts/Sidebar.js +201 -107
- package/dist/layouts/Sidebar.js.map +1 -1
- package/dist/utils/flexLayout.d.ts +5 -0
- package/dist/utils/flexLayout.js +19 -0
- package/dist/utils/flexLayout.js.map +1 -0
- package/package.json +63 -57
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { cn as e } from "../../utils/cn.js";
|
|
2
|
-
import { useCallback as t,
|
|
2
|
+
import { useCallback as t, useId as n, useState as r } from "react";
|
|
3
3
|
import { jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
4
4
|
//#region src/components/ChoicePicker/index.tsx
|
|
5
5
|
var o = {
|
|
@@ -31,16 +31,18 @@ var o = {
|
|
|
31
31
|
}, u = ({ options: u, value: d, defaultValue: f, onChange: p, appearance: m = "list", orientation: h = "vertical", variant: g = "primary", size: _ = "sm", disabled: v = !1, label: y, name: b, className: x = "", style: S }) => {
|
|
32
32
|
let [C, w] = r(f), T = d !== void 0, E = T ? d : C, D = t((e) => {
|
|
33
33
|
T || w(e), p?.(e);
|
|
34
|
-
}, [T, p]), O = n(
|
|
34
|
+
}, [T, p]), O = n(), k = b ?? `choicepicker-${O}`, A = `${O}-label`, j = h === "horizontal" ? "flex flex-wrap items-center" : "flex flex-col";
|
|
35
35
|
return /* @__PURE__ */ a("div", {
|
|
36
36
|
className: e("w-full", x),
|
|
37
37
|
style: S,
|
|
38
|
-
role: "radiogroup",
|
|
38
|
+
role: m === "chip" ? "group" : "radiogroup",
|
|
39
|
+
"aria-labelledby": y == null ? void 0 : A,
|
|
39
40
|
children: [y !== void 0 && /* @__PURE__ */ i("div", {
|
|
41
|
+
id: A,
|
|
40
42
|
className: "mb-1.5 text-sm font-medium text-base-content/80",
|
|
41
43
|
children: y
|
|
42
44
|
}), m === "chip" ? /* @__PURE__ */ i("div", {
|
|
43
|
-
className: e(
|
|
45
|
+
className: e(j, "gap-2"),
|
|
44
46
|
children: u.map((t) => {
|
|
45
47
|
let n = E === t.value;
|
|
46
48
|
return /* @__PURE__ */ i("button", {
|
|
@@ -53,7 +55,7 @@ var o = {
|
|
|
53
55
|
}, String(t.value));
|
|
54
56
|
})
|
|
55
57
|
}) : /* @__PURE__ */ i("div", {
|
|
56
|
-
className: e(
|
|
58
|
+
className: e(j, "gap-2.5"),
|
|
57
59
|
children: u.map((t) => {
|
|
58
60
|
let n = E === t.value;
|
|
59
61
|
return /* @__PURE__ */ a("label", {
|
|
@@ -61,7 +63,7 @@ var o = {
|
|
|
61
63
|
children: [/* @__PURE__ */ i("input", {
|
|
62
64
|
type: "radio",
|
|
63
65
|
className: e("radio", g !== "default" && s[g], l[_]),
|
|
64
|
-
name:
|
|
66
|
+
name: k,
|
|
65
67
|
value: String(t.value),
|
|
66
68
|
checked: n,
|
|
67
69
|
disabled: v || t.disabled,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/ChoicePicker/index.tsx"],"sourcesContent":["import React, { useCallback,
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/ChoicePicker/index.tsx"],"sourcesContent":["import React, { useCallback, useId, useState } from 'react';\nimport { cn } from '../../utils/cn';\n\nexport type ChoicePickerOption = {\n /** Value submitted for this option. */\n value: string | number;\n /** Display label. */\n label: React.ReactNode;\n /** Disable this option only. */\n disabled?: boolean;\n};\n\nexport type ChoicePickerVariant =\n | 'default'\n | 'primary'\n | 'secondary'\n | 'accent'\n | 'info'\n | 'success'\n | 'warning'\n | 'error';\n\nexport type ChoicePickerProps = {\n /** Available options. */\n options: ChoicePickerOption[];\n /** Controlled selected value. */\n value?: string | number;\n /** Uncontrolled initial value. */\n defaultValue?: string | number;\n /** Change handler with the selected value. */\n onChange?: (value: string | number) => void;\n /** `list` renders radio rows; `chip` renders a button group. */\n appearance?: 'list' | 'chip';\n /** Layout of list rows or chip wrapping. */\n orientation?: 'vertical' | 'horizontal';\n /** Color variant. */\n variant?: ChoicePickerVariant;\n /** Size (daisyUI). */\n size?: 'xs' | 'sm' | 'md' | 'lg';\n /** Disable the whole group. */\n disabled?: boolean;\n /** Optional group label rendered above the options. */\n label?: React.ReactNode;\n /** Native name attribute for the radios. */\n name?: string;\n /** Extra class names on the wrapper. */\n className?: string;\n /** Inline styles on the wrapper. */\n style?: React.CSSProperties;\n};\n\nconst VARIANT_CHIP_ACTIVE: Record<Exclude<ChoicePickerVariant, 'default'>, string> = {\n primary: 'btn-primary',\n secondary: 'btn-secondary',\n accent: 'btn-accent',\n info: 'btn-info',\n success: 'btn-success',\n warning: 'btn-warning',\n error: 'btn-error',\n};\n\nconst VARIANT_RADIO: Record<Exclude<ChoicePickerVariant, 'default'>, string> = {\n primary: 'radio-primary',\n secondary: 'radio-secondary',\n accent: 'radio-accent',\n info: 'radio-info',\n success: 'radio-success',\n warning: 'radio-warning',\n error: 'radio-error',\n};\n\nconst SIZE_CHIP: Record<ChoicePickerProps['size'] & string, string> = {\n xs: 'btn-xs',\n sm: 'btn-sm',\n md: 'btn-md',\n lg: 'btn-lg',\n};\n\nconst SIZE_RADIO: Record<ChoicePickerProps['size'] & string, string> = {\n xs: 'radio-xs',\n sm: 'radio-sm',\n md: 'radio-md',\n lg: 'radio-lg',\n};\n\n/**\n * ChoicePicker —— A2UI 交互组件(v0.9):单选项组。\n * `list` 外观为 radio 行(daisyUI radio),`chip` 外观为按钮组(daisyUI btn)。\n */\nconst ChoicePicker: React.FC<ChoicePickerProps> = ({\n options,\n value,\n defaultValue,\n onChange,\n appearance = 'list',\n orientation = 'vertical',\n variant = 'primary',\n size = 'sm',\n disabled = false,\n label,\n name,\n className = '',\n style,\n}) => {\n const [internal, setInternal] = useState<string | number | undefined>(defaultValue);\n const isControlled = value !== undefined;\n const selected = isControlled ? value : internal;\n\n const select = useCallback(\n (optionValue: string | number) => {\n if (!isControlled) setInternal(optionValue);\n onChange?.(optionValue);\n },\n [isControlled, onChange],\n );\n\n const generatedName = useId();\n const groupName = name ?? `choicepicker-${generatedName}`;\n const labelId = `${generatedName}-label`;\n\n const layoutClass =\n orientation === 'horizontal' ? 'flex flex-wrap items-center' : 'flex flex-col';\n\n return (\n <div className={cn('w-full', className)} style={style} role={appearance === 'chip' ? 'group' : 'radiogroup'} aria-labelledby={label !== undefined && label !== null ? labelId : undefined}>\n {label !== undefined && (\n <div id={labelId} className=\"mb-1.5 text-sm font-medium text-base-content/80\">{label}</div>\n )}\n\n {appearance === 'chip' ? (\n <div className={cn(layoutClass, 'gap-2')}>\n {options.map((option) => {\n const active = selected === option.value;\n return (\n <button\n key={String(option.value)}\n type=\"button\"\n disabled={disabled || option.disabled}\n aria-pressed={active}\n onClick={() => select(option.value)}\n className={cn(\n 'btn',\n SIZE_CHIP[size],\n active\n ? variant === 'default'\n ? 'btn-neutral'\n : VARIANT_CHIP_ACTIVE[variant]\n : 'btn-outline',\n )}\n >\n {option.label}\n </button>\n );\n })}\n </div>\n ) : (\n <div className={cn(layoutClass, 'gap-2.5')}>\n {options.map((option) => {\n const active = selected === option.value;\n return (\n <label\n key={String(option.value)}\n className={cn(\n 'flex w-full cursor-pointer items-center gap-2.5',\n (disabled || option.disabled) && 'cursor-not-allowed opacity-50',\n )}\n >\n <input\n type=\"radio\"\n className={cn(\n 'radio',\n variant !== 'default' && VARIANT_RADIO[variant],\n SIZE_RADIO[size],\n )}\n name={groupName}\n value={String(option.value)}\n checked={active}\n disabled={disabled || option.disabled}\n onChange={() => select(option.value)}\n />\n <span className=\"text-sm text-base-content\">{option.label}</span>\n </label>\n );\n })}\n </div>\n )}\n </div>\n );\n};\n\nexport default ChoicePicker;\n"],"mappings":";;;;AAmDA,IAAM,IAA+E;CACnF,SAAS;CACT,WAAW;CACX,QAAQ;CACR,MAAM;CACN,SAAS;CACT,SAAS;CACT,OAAO;AACT,GAEM,IAAyE;CAC7E,SAAS;CACT,WAAW;CACX,QAAQ;CACR,MAAM;CACN,SAAS;CACT,SAAS;CACT,OAAO;AACT,GAEM,IAAgE;CACpE,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN,GAEM,IAAiE;CACrE,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN,GAMM,KAA6C,EACjD,YACA,UACA,iBACA,aACA,gBAAa,QACb,iBAAc,YACd,aAAU,WACV,UAAO,MACP,cAAW,IACX,UACA,SACA,eAAY,IACZ,eACI;CACJ,IAAM,CAAC,GAAU,KAAe,EAAsC,CAAY,GAC5E,IAAe,MAAU,KAAA,GACzB,IAAW,IAAe,IAAQ,GAElC,IAAS,GACZ,MAAiC;EAEhC,AADK,KAAc,EAAY,CAAW,GAC1C,IAAW,CAAW;CACxB,GACA,CAAC,GAAc,CAAQ,CACzB,GAEM,IAAgB,EAAM,GACtB,IAAY,KAAQ,gBAAgB,KACpC,IAAU,GAAG,EAAc,SAE3B,IACJ,MAAgB,eAAe,gCAAgC;CAEjE,OACE,kBAAC,OAAD;EAAK,WAAW,EAAG,UAAU,CAAS;EAAU;EAAO,MAAM,MAAe,SAAS,UAAU;EAAc,mBAAiB,KAAiC,OAAiB,KAAA,IAAV;EAAtK,UAAA,CACG,MAAU,KAAA,KACT,kBAAC,OAAD;GAAK,IAAI;GAAS,WAAU;GAAmD,UAAA;EAAW,CAAA,GAG3F,MAAe,SACd,kBAAC,OAAD;GAAK,WAAW,EAAG,GAAa,OAAO;GACpC,UAAA,EAAQ,KAAK,MAAW;IACvB,IAAM,IAAS,MAAa,EAAO;IACnC,OACE,kBAAC,UAAD;KAEE,MAAK;KACL,UAAU,KAAY,EAAO;KAC7B,gBAAc;KACd,eAAe,EAAO,EAAO,KAAK;KAClC,WAAW,EACT,OACA,EAAU,IACV,IACI,MAAY,YACV,gBACA,EAAoB,KACtB,aACN;KAEC,UAAA,EAAO;IACF,GAhBD,OAAO,EAAO,KAAK,CAgBlB;GAEZ,CAAC;EACE,CAAA,IAEL,kBAAC,OAAD;GAAK,WAAW,EAAG,GAAa,SAAS;GACtC,UAAA,EAAQ,KAAK,MAAW;IACvB,IAAM,IAAS,MAAa,EAAO;IACnC,OACE,kBAAC,SAAD;KAEE,WAAW,EACT,oDACC,KAAY,EAAO,aAAa,+BACnC;KALF,UAAA,CAOE,kBAAC,SAAD;MACE,MAAK;MACL,WAAW,EACT,SACA,MAAY,aAAa,EAAc,IACvC,EAAW,EACb;MACA,MAAM;MACN,OAAO,OAAO,EAAO,KAAK;MAC1B,SAAS;MACT,UAAU,KAAY,EAAO;MAC7B,gBAAgB,EAAO,EAAO,KAAK;KACpC,CAAA,GACD,kBAAC,QAAD;MAAM,WAAU;MAA6B,UAAA,EAAO;KAAY,CAAA,CAC3D;IApBA,GAAA,OAAO,EAAO,KAAK,CAoBnB;GAEX,CAAC;EACE,CAAA,CAEJ;;AAET"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
|
|
3
|
-
export type
|
|
2
|
+
import { type FlexAlign, type FlexJustify } from '../../utils/flexLayout';
|
|
3
|
+
export type ColumnAlign = FlexAlign;
|
|
4
|
+
export type ColumnJustify = FlexJustify;
|
|
4
5
|
export type ColumnProps = {
|
|
5
6
|
children?: React.ReactNode;
|
|
6
7
|
/** Gap between children; number is treated as px. */
|
|
@@ -1,24 +1,12 @@
|
|
|
1
1
|
import { cn as e } from "../../utils/cn.js";
|
|
2
|
+
import { FLEX_ALIGN_CLASS as t, FLEX_JUSTIFY_CLASS as n, flexGap as r } from "../../utils/flexLayout.js";
|
|
2
3
|
import "react";
|
|
3
|
-
import { jsx as
|
|
4
|
+
import { jsx as i } from "react/jsx-runtime";
|
|
4
5
|
//#region src/components/Column/index.tsx
|
|
5
|
-
var
|
|
6
|
-
|
|
7
|
-
center: "items-center",
|
|
8
|
-
end: "items-end",
|
|
9
|
-
baseline: "items-baseline",
|
|
10
|
-
stretch: "items-stretch"
|
|
11
|
-
}, r = {
|
|
12
|
-
start: "justify-start",
|
|
13
|
-
center: "justify-center",
|
|
14
|
-
end: "justify-end",
|
|
15
|
-
between: "justify-between",
|
|
16
|
-
around: "justify-around",
|
|
17
|
-
evenly: "justify-evenly"
|
|
18
|
-
}, i = (e) => typeof e == "number" ? `${e}px` : e, a = ({ children: a, gap: o, align: s = "stretch", justify: c = "start", wrap: l = !1, reverse: u = !1, className: d = "", style: f, onClick: p }) => /* @__PURE__ */ t("div", {
|
|
19
|
-
className: e("flex", u ? "flex-col-reverse" : "flex-col", n[s], r[c], l && "flex-wrap", d),
|
|
6
|
+
var a = ({ children: a, gap: o, align: s = "stretch", justify: c = "start", wrap: l = !1, reverse: u = !1, className: d = "", style: f, onClick: p }) => /* @__PURE__ */ i("div", {
|
|
7
|
+
className: e("flex", u ? "flex-col-reverse" : "flex-col", t[s], n[c], l && "flex-wrap", d),
|
|
20
8
|
style: {
|
|
21
|
-
gap:
|
|
9
|
+
gap: r(o),
|
|
22
10
|
...f
|
|
23
11
|
},
|
|
24
12
|
onClick: p,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/Column/index.tsx"],"sourcesContent":["import React from 'react';\nimport { cn } from '../../utils/cn';\
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/Column/index.tsx"],"sourcesContent":["import React from 'react';\nimport { cn } from '../../utils/cn';\nimport { FLEX_ALIGN_CLASS, FLEX_JUSTIFY_CLASS, flexGap, type FlexAlign, type FlexJustify } from '../../utils/flexLayout';\n\nexport type ColumnAlign = FlexAlign;\nexport type ColumnJustify = FlexJustify;\n\nexport type ColumnProps = {\n children?: React.ReactNode;\n /** Gap between children; number is treated as px. */\n gap?: number | string;\n /** Cross-axis alignment. */\n align?: ColumnAlign;\n /** Main-axis (vertical) distribution. */\n justify?: ColumnJustify;\n /** Allow children to wrap (rarely used in a column). */\n wrap?: boolean;\n /** Reverse the visual order of children. */\n reverse?: boolean;\n /** Extra class names on the container. */\n className?: string;\n /** Inline styles on the container. */\n style?: React.CSSProperties;\n /** Click handler on the container. */\n onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;\n};\n\n\n/**\n * Column —— A2UI 布局组件:垂直排列子元素。\n * 等价于 `display: flex; flex-direction: column` 的语义化封装。\n */\nconst Column: React.FC<ColumnProps> = ({\n children,\n gap,\n align = 'stretch',\n justify = 'start',\n wrap = false,\n reverse = false,\n className = '',\n style,\n onClick,\n}) => (\n <div\n className={cn(\n 'flex',\n reverse ? 'flex-col-reverse' : 'flex-col',\n FLEX_ALIGN_CLASS[align],\n FLEX_JUSTIFY_CLASS[justify],\n wrap && 'flex-wrap',\n className,\n )}\n style={{ gap: flexGap(gap), ...style }}\n onClick={onClick}\n >\n {children}\n </div>\n);\n\nexport default Column;\n"],"mappings":";;;;;AAgCA,IAAM,KAAiC,EACrC,aACA,QACA,WAAQ,WACR,aAAU,SACV,UAAO,IACP,aAAU,IACV,eAAY,IACZ,UACA,iBAEA,kBAAC,OAAD;CACE,WAAW,EACT,QACA,IAAU,qBAAqB,YAC/B,EAAiB,IACjB,EAAmB,IACnB,KAAQ,aACR,CACF;CACA,OAAO;EAAE,KAAK,EAAQ,CAAG;EAAG,GAAG;CAAM;CAC5B;CAER;AACE,CAAA"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export type ConfirmDialogProps = {
|
|
3
|
+
open: boolean;
|
|
4
|
+
onClose: () => void;
|
|
5
|
+
/** Called once per confirmation. Reject to keep the dialog open and show an error. */
|
|
6
|
+
onConfirm: () => void | Promise<void>;
|
|
7
|
+
title: string;
|
|
8
|
+
/** Name of the object affected by the action. */
|
|
9
|
+
objectName: string;
|
|
10
|
+
/** Explain the effect of confirming (including irreversible consequences). */
|
|
11
|
+
consequence: string;
|
|
12
|
+
/** Require the exact object name before enabling confirmation for highest-risk actions. */
|
|
13
|
+
requireName?: boolean;
|
|
14
|
+
confirmLabel?: string;
|
|
15
|
+
cancelLabel?: string;
|
|
16
|
+
pendingLabel?: string;
|
|
17
|
+
failureMessage?: string;
|
|
18
|
+
namePrompt?: string;
|
|
19
|
+
};
|
|
20
|
+
export default function ConfirmDialog(props: ConfirmDialogProps): React.JSX.Element | null;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import e from "../Button/index.js";
|
|
2
|
+
import t from "../Modal/index.js";
|
|
3
|
+
import { useEffect as n, useId as r, useRef as i, useState as a } from "react";
|
|
4
|
+
import { Fragment as o, jsx as s, jsxs as c } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/ConfirmDialog/index.tsx
|
|
6
|
+
function l(e) {
|
|
7
|
+
return e.open ? /* @__PURE__ */ s(u, { ...e }) : null;
|
|
8
|
+
}
|
|
9
|
+
function u({ onClose: l, onConfirm: u, title: d, objectName: f, consequence: p, requireName: m = !1, confirmLabel: h = "Confirm", cancelLabel: g = "Cancel", pendingLabel: _ = "Working…", failureMessage: v = "The action failed. Try again or cancel.", namePrompt: y = "Type the object name to confirm" }) {
|
|
10
|
+
let [b, x] = a(!1), [S, C] = a(!1), [w, T] = a(""), E = i(!1), D = i(!0), O = r();
|
|
11
|
+
n(() => (D.current = !0, () => {
|
|
12
|
+
D.current = !1;
|
|
13
|
+
}), []);
|
|
14
|
+
let k = () => {
|
|
15
|
+
E.current || l();
|
|
16
|
+
};
|
|
17
|
+
return /* @__PURE__ */ c(t, {
|
|
18
|
+
open: !0,
|
|
19
|
+
onClose: k,
|
|
20
|
+
title: d,
|
|
21
|
+
description: /* @__PURE__ */ c(o, { children: [
|
|
22
|
+
"Object: ",
|
|
23
|
+
/* @__PURE__ */ s("strong", { children: f }),
|
|
24
|
+
". ",
|
|
25
|
+
p
|
|
26
|
+
] }),
|
|
27
|
+
size: "sm",
|
|
28
|
+
showClose: !1,
|
|
29
|
+
closeOnOverlay: !1,
|
|
30
|
+
footer: /* @__PURE__ */ c(o, { children: [/* @__PURE__ */ s(e, {
|
|
31
|
+
type: "button",
|
|
32
|
+
variant: "neutral",
|
|
33
|
+
autoFocus: !0,
|
|
34
|
+
disabled: b,
|
|
35
|
+
onClick: k,
|
|
36
|
+
children: g
|
|
37
|
+
}), /* @__PURE__ */ s(e, {
|
|
38
|
+
type: "button",
|
|
39
|
+
variant: "destructive",
|
|
40
|
+
disabled: b || m && w !== f,
|
|
41
|
+
onClick: async () => {
|
|
42
|
+
if (!(E.current || m && w !== f)) {
|
|
43
|
+
E.current = !0, C(!1), x(!0);
|
|
44
|
+
try {
|
|
45
|
+
await u(), D.current && l();
|
|
46
|
+
} catch {
|
|
47
|
+
D.current && C(!0);
|
|
48
|
+
} finally {
|
|
49
|
+
D.current && (E.current = !1, x(!1));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
children: b ? _ : h
|
|
54
|
+
})] }),
|
|
55
|
+
children: [m && /* @__PURE__ */ c("div", {
|
|
56
|
+
className: "flex flex-col gap-2",
|
|
57
|
+
children: [/* @__PURE__ */ c("label", {
|
|
58
|
+
htmlFor: O,
|
|
59
|
+
className: "text-body-sm font-medium",
|
|
60
|
+
children: [
|
|
61
|
+
y,
|
|
62
|
+
": ",
|
|
63
|
+
f
|
|
64
|
+
]
|
|
65
|
+
}), /* @__PURE__ */ s("input", {
|
|
66
|
+
id: O,
|
|
67
|
+
type: "text",
|
|
68
|
+
value: w,
|
|
69
|
+
disabled: b,
|
|
70
|
+
onChange: (e) => T(e.target.value),
|
|
71
|
+
className: "input input-bordered w-full",
|
|
72
|
+
autoComplete: "off"
|
|
73
|
+
})]
|
|
74
|
+
}), S && /* @__PURE__ */ s("p", {
|
|
75
|
+
role: "alert",
|
|
76
|
+
className: "mt-3 text-body-sm text-error",
|
|
77
|
+
children: v
|
|
78
|
+
})]
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
//#endregion
|
|
82
|
+
export { l as default };
|
|
83
|
+
|
|
84
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/ConfirmDialog/index.tsx"],"sourcesContent":["import React, { useEffect, useId, useRef, useState } from 'react';\nimport Button from '../Button';\nimport Modal from '../Modal';\n\nexport type ConfirmDialogProps = {\n open: boolean;\n onClose: () => void;\n /** Called once per confirmation. Reject to keep the dialog open and show an error. */\n onConfirm: () => void | Promise<void>;\n title: string;\n /** Name of the object affected by the action. */\n objectName: string;\n /** Explain the effect of confirming (including irreversible consequences). */\n consequence: string;\n /** Require the exact object name before enabling confirmation for highest-risk actions. */\n requireName?: boolean;\n confirmLabel?: string;\n cancelLabel?: string;\n pendingLabel?: string;\n failureMessage?: string;\n namePrompt?: string;\n};\n\nexport default function ConfirmDialog(props: ConfirmDialogProps) {\n return props.open ? <ConfirmDialogContent {...props} /> : null;\n}\n\nfunction ConfirmDialogContent({\n onClose,\n onConfirm,\n title,\n objectName,\n consequence,\n requireName = false,\n confirmLabel = 'Confirm',\n cancelLabel = 'Cancel',\n pendingLabel = 'Working…',\n failureMessage = 'The action failed. Try again or cancel.',\n namePrompt = 'Type the object name to confirm',\n}: ConfirmDialogProps) {\n const [pending, setPending] = useState(false);\n const [error, setError] = useState(false);\n const [enteredName, setEnteredName] = useState('');\n const submitting = useRef(false);\n const mounted = useRef(true);\n const nameId = useId();\n\n useEffect(() => {\n mounted.current = true;\n return () => { mounted.current = false; };\n }, []);\n\n const dismiss = () => {\n if (!submitting.current) onClose();\n };\n const confirm = async () => {\n if (submitting.current || (requireName && enteredName !== objectName)) return;\n submitting.current = true;\n\n setError(false);\n setPending(true);\n try {\n await onConfirm();\n if (mounted.current) onClose();\n } catch {\n if (mounted.current) setError(true);\n } finally {\n if (mounted.current) {\n submitting.current = false;\n setPending(false);\n }\n }\n };\n\n return (\n <Modal\n open\n onClose={dismiss}\n title={title}\n description={<>Object: <strong>{objectName}</strong>. {consequence}</>}\n size=\"sm\"\n showClose={false}\n closeOnOverlay={false}\n footer={<>\n <Button type=\"button\" variant=\"neutral\" autoFocus disabled={pending} onClick={dismiss}>{cancelLabel}</Button>\n <Button type=\"button\" variant=\"destructive\" disabled={pending || (requireName && enteredName !== objectName)} onClick={confirm}>\n {pending ? pendingLabel : confirmLabel}\n </Button>\n </>}\n >\n {requireName && (\n <div className=\"flex flex-col gap-2\">\n <label htmlFor={nameId} className=\"text-body-sm font-medium\">{namePrompt}: {objectName}</label>\n <input id={nameId} type=\"text\" value={enteredName} disabled={pending} onChange={event => setEnteredName(event.target.value)}\n className=\"input input-bordered w-full\" autoComplete=\"off\" />\n </div>\n )}\n {error && <p role=\"alert\" className=\"mt-3 text-body-sm text-error\">{failureMessage}</p>}\n </Modal>\n );\n}\n"],"mappings":";;;;;AAuBA,SAAwB,EAAc,GAA2B;CAC7D,OAAO,EAAM,OAAO,kBAAC,GAAD,EAAsB,GAAI,EAAQ,CAAA,IAAI;AAC9D;AAEA,SAAS,EAAqB,EAC1B,YACA,cACA,UACA,eACA,gBACA,iBAAc,IACd,kBAAe,WACf,iBAAc,UACd,kBAAe,YACf,oBAAiB,2CACjB,gBAAa,qCACM;CACnB,IAAM,CAAC,GAAS,KAAc,EAAS,EAAK,GACtC,CAAC,GAAO,KAAY,EAAS,EAAK,GAClC,CAAC,GAAa,KAAkB,EAAS,EAAE,GAC3C,IAAa,EAAO,EAAK,GACzB,IAAU,EAAO,EAAI,GACrB,IAAS,EAAM;CAErB,SACI,EAAQ,UAAU,UACL;EAAE,EAAQ,UAAU;CAAO,IACzC,CAAC,CAAC;CAEL,IAAM,UAAgB;EAClB,AAAK,EAAW,WAAS,EAAQ;CACrC;CAoBA,OACI,kBAAC,GAAD;EACI,MAAA;EACA,SAAS;EACF;EACP,aAAa,kBAAA,GAAA,EAAA,UAAA;GAAE;GAAQ,kBAAC,UAAD,EAAA,UAAS,EAAmB,CAAA;GAAC;GAAG;EAAc,EAAA,CAAA;EACrE,MAAK;EACL,WAAW;EACX,gBAAgB;EAChB,QAAQ,kBAAA,GAAA,EAAA,UAAA,CACJ,kBAAC,GAAD;GAAQ,MAAK;GAAS,SAAQ;GAAU,WAAA;GAAU,UAAU;GAAS,SAAS;GAAU,UAAA;EAAoB,CAAA,GAC5G,kBAAC,GAAD;GAAQ,MAAK;GAAS,SAAQ;GAAc,UAAU,KAAY,KAAe,MAAgB;GAAa,SAAS,YA9BvG;IACpB,QAAW,WAAY,KAAe,MAAgB,IAI1D;KAHA,EAAW,UAAU,IAErB,EAAS,EAAK,GACd,EAAW,EAAI;KACf,IAAI;MAEA,AADA,MAAM,EAAU,GACZ,EAAQ,WAAS,EAAQ;KACjC,QAAQ;MACJ,AAAI,EAAQ,WAAS,EAAS,EAAI;KACtC,UAAU;MACN,AAAI,EAAQ,YACR,EAAW,UAAU,IACrB,EAAW,EAAK;KAExB;IAXe;GAYnB;GAciB,UAAA,IAAU,IAAe;EACtB,CAAA,CACV,EAAA,CAAA;EAbN,UAAA,CAeK,KACG,kBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACI,kBAAC,SAAD;IAAO,SAAS;IAAQ,WAAU;IAAlC,UAAA;KAA8D;KAAW;KAAG;IAAkB;GAC9F,CAAA,GAAA,kBAAC,SAAD;IAAO,IAAI;IAAQ,MAAK;IAAO,OAAO;IAAa,UAAU;IAAS,WAAU,MAAS,EAAe,EAAM,OAAO,KAAK;IACtH,WAAU;IAA8B,cAAa;GAAO,CAAA,CAC/D;EAER,CAAA,GAAA,KAAS,kBAAC,KAAD;GAAG,MAAK;GAAQ,WAAU;GAAgC,UAAA;EAAkB,CAAA,CACnF;;AAEf"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { cn as e } from "../../utils/cn.js";
|
|
2
2
|
/* empty css */
|
|
3
|
-
import { useCallback as t } from "react";
|
|
4
|
-
import { jsx as
|
|
3
|
+
import { useCallback as t, useId as n } from "react";
|
|
4
|
+
import { jsx as r, jsxs as i } from "react/jsx-runtime";
|
|
5
5
|
//#region src/components/DateTimeInput/index.tsx
|
|
6
|
-
var
|
|
6
|
+
var a = {
|
|
7
7
|
default: "",
|
|
8
8
|
primary: "input-primary",
|
|
9
9
|
secondary: "input-secondary",
|
|
@@ -12,38 +12,40 @@ var i = {
|
|
|
12
12
|
success: "input-success",
|
|
13
13
|
warning: "input-warning",
|
|
14
14
|
error: "input-error"
|
|
15
|
-
},
|
|
15
|
+
}, o = {
|
|
16
16
|
xs: "input-xs",
|
|
17
17
|
sm: "input-sm",
|
|
18
18
|
md: "input-md",
|
|
19
19
|
lg: "input-lg"
|
|
20
|
-
},
|
|
21
|
-
let
|
|
22
|
-
|
|
23
|
-
}, [
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
20
|
+
}, s = ({ type: s = "date", value: c, defaultValue: l, onChange: u, min: d, max: f, step: p, label: m, placeholder: h, variant: g = "default", size: _ = "sm", disabled: v = !1, readOnly: y = !1, name: b, className: x = "", style: S }) => {
|
|
21
|
+
let C = n(), w = t((e) => {
|
|
22
|
+
u?.(e.target.value, e);
|
|
23
|
+
}, [u]), T = /* @__PURE__ */ r("input", {
|
|
24
|
+
id: C,
|
|
25
|
+
type: s,
|
|
26
|
+
className: e("input dishui-datetime-input w-full", a[g], o[_], x),
|
|
27
|
+
value: c,
|
|
28
|
+
defaultValue: c === void 0 ? l : void 0,
|
|
29
|
+
min: d,
|
|
30
|
+
max: f,
|
|
31
|
+
step: p,
|
|
32
|
+
placeholder: h,
|
|
33
|
+
disabled: v,
|
|
34
|
+
readOnly: y,
|
|
35
|
+
name: b,
|
|
36
|
+
onChange: w
|
|
36
37
|
});
|
|
37
|
-
return
|
|
38
|
+
return m == null || m === "" ? T : /* @__PURE__ */ i("div", {
|
|
38
39
|
className: "dishui-datetime-field w-full",
|
|
39
|
-
style:
|
|
40
|
-
children: [/* @__PURE__ */
|
|
40
|
+
style: S,
|
|
41
|
+
children: [/* @__PURE__ */ r("label", {
|
|
42
|
+
htmlFor: C,
|
|
41
43
|
className: "label-text mb-1 block text-sm text-base-content/80",
|
|
42
|
-
children:
|
|
43
|
-
}),
|
|
44
|
+
children: m
|
|
45
|
+
}), T]
|
|
44
46
|
});
|
|
45
47
|
};
|
|
46
48
|
//#endregion
|
|
47
|
-
export {
|
|
49
|
+
export { s as default };
|
|
48
50
|
|
|
49
51
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/DateTimeInput/index.tsx"],"sourcesContent":["import React, { useCallback } from 'react';\nimport { cn } from '../../utils/cn';\nimport './DateTimeInput.css';\n\nexport type DateTimeType = 'date' | 'time' | 'datetime-local' | 'month' | 'week';\n\nexport type DateTimeVariant =\n | 'default'\n | 'primary'\n | 'secondary'\n | 'accent'\n | 'info'\n | 'success'\n | 'warning'\n | 'error';\n\nexport type DateTimeSize = 'xs' | 'sm' | 'md' | 'lg';\n\nexport type DateTimeInputProps = {\n /** Which native picker to render. */\n type?: DateTimeType;\n /** Controlled value (`YYYY-MM-DD`, `HH:mm`, … per `type`). */\n value?: string;\n /** Uncontrolled initial value. */\n defaultValue?: string;\n /** Change handler with the raw input value. */\n onChange?: (value: string, e: React.ChangeEvent<HTMLInputElement>) => void;\n /** Minimum allowed value. */\n min?: string;\n /** Maximum allowed value. */\n max?: string;\n /** Step (seconds for time / datetime-local, days for date, …). */\n step?: number | string;\n /** Optional label rendered above the field. */\n label?: React.ReactNode;\n /** Placeholder (only shown by browsers that support it). */\n placeholder?: string;\n /** Color variant (daisyUI). */\n variant?: DateTimeVariant;\n /** Size (daisyUI). */\n size?: DateTimeSize;\n /** Disabled state. */\n disabled?: boolean;\n /** Read-only state. */\n readOnly?: boolean;\n /** Native name attribute. */\n name?: string;\n /** Extra class names on the input. */\n className?: string;\n /** Inline styles on the wrapper. */\n style?: React.CSSProperties;\n};\n\nconst VARIANT_CLASS: Record<DateTimeVariant, string> = {\n default: '',\n primary: 'input-primary',\n secondary: 'input-secondary',\n accent: 'input-accent',\n info: 'input-info',\n success: 'input-success',\n warning: 'input-warning',\n error: 'input-error',\n};\n\nconst SIZE_CLASS: Record<DateTimeSize, string> = {\n xs: 'input-xs',\n sm: 'input-sm',\n md: 'input-md',\n lg: 'input-lg',\n};\n\n/**\n * DateTimeInput —— A2UI 交互组件:日期 / 时间选择输入。\n * 基于原生 `date / time / datetime-local / month / week` 输入,套用 daisyUI input 样式。\n */\nconst DateTimeInput: React.FC<DateTimeInputProps> = ({\n type = 'date',\n value,\n defaultValue,\n onChange,\n min,\n max,\n step,\n label,\n placeholder,\n variant = 'default',\n size = 'sm',\n disabled = false,\n readOnly = false,\n name,\n className = '',\n style,\n}) => {\n const handleChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n onChange?.(e.target.value, e);\n },\n [onChange],\n );\n\n const input = (\n <input\n type={type}\n className={cn('input dishui-datetime-input w-full', VARIANT_CLASS[variant], SIZE_CLASS[size], className)}\n value={value}\n defaultValue={value !== undefined ? undefined : defaultValue}\n min={min}\n max={max}\n step={step}\n placeholder={placeholder}\n disabled={disabled}\n readOnly={readOnly}\n name={name}\n onChange={handleChange}\n />\n );\n\n if (label === undefined || label === null || label === '') {\n return input;\n }\n\n return (\n <div className=\"dishui-datetime-field w-full\" style={style}>\n <label className=\"label-text mb-1 block text-sm text-base-content/80\">\n {label}\n </label>\n {input}\n </div>\n );\n};\n\nexport default DateTimeInput;\n"],"mappings":";;;;;AAqDA,IAAM,IAAiD;CACrD,SAAS;CACT,SAAS;CACT,WAAW;CACX,QAAQ;CACR,MAAM;CACN,SAAS;CACT,SAAS;CACT,OAAO;AACT,GAEM,IAA2C;CAC/C,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN,GAMM,KAA+C,EACnD,UAAO,QACP,UACA,iBACA,aACA,QACA,QACA,SACA,UACA,gBACA,aAAU,WACV,UAAO,MACP,cAAW,IACX,cAAW,IACX,SACA,eAAY,IACZ,eACI;CACJ,IAAM,IAAe,GAClB,MAA2C;EAC1C,IAAW,EAAE,OAAO,OAAO,CAAC;CAC9B,GACA,CAAC,CAAQ,CACX,GAEM,IACJ,kBAAC,SAAD;
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/DateTimeInput/index.tsx"],"sourcesContent":["import React, { useCallback, useId } from 'react';\nimport { cn } from '../../utils/cn';\nimport './DateTimeInput.css';\n\nexport type DateTimeType = 'date' | 'time' | 'datetime-local' | 'month' | 'week';\n\nexport type DateTimeVariant =\n | 'default'\n | 'primary'\n | 'secondary'\n | 'accent'\n | 'info'\n | 'success'\n | 'warning'\n | 'error';\n\nexport type DateTimeSize = 'xs' | 'sm' | 'md' | 'lg';\n\nexport type DateTimeInputProps = {\n /** Which native picker to render. */\n type?: DateTimeType;\n /** Controlled value (`YYYY-MM-DD`, `HH:mm`, … per `type`). */\n value?: string;\n /** Uncontrolled initial value. */\n defaultValue?: string;\n /** Change handler with the raw input value. */\n onChange?: (value: string, e: React.ChangeEvent<HTMLInputElement>) => void;\n /** Minimum allowed value. */\n min?: string;\n /** Maximum allowed value. */\n max?: string;\n /** Step (seconds for time / datetime-local, days for date, …). */\n step?: number | string;\n /** Optional label rendered above the field. */\n label?: React.ReactNode;\n /** Placeholder (only shown by browsers that support it). */\n placeholder?: string;\n /** Color variant (daisyUI). */\n variant?: DateTimeVariant;\n /** Size (daisyUI). */\n size?: DateTimeSize;\n /** Disabled state. */\n disabled?: boolean;\n /** Read-only state. */\n readOnly?: boolean;\n /** Native name attribute. */\n name?: string;\n /** Extra class names on the input. */\n className?: string;\n /** Inline styles on the wrapper. */\n style?: React.CSSProperties;\n};\n\nconst VARIANT_CLASS: Record<DateTimeVariant, string> = {\n default: '',\n primary: 'input-primary',\n secondary: 'input-secondary',\n accent: 'input-accent',\n info: 'input-info',\n success: 'input-success',\n warning: 'input-warning',\n error: 'input-error',\n};\n\nconst SIZE_CLASS: Record<DateTimeSize, string> = {\n xs: 'input-xs',\n sm: 'input-sm',\n md: 'input-md',\n lg: 'input-lg',\n};\n\n/**\n * DateTimeInput —— A2UI 交互组件:日期 / 时间选择输入。\n * 基于原生 `date / time / datetime-local / month / week` 输入,套用 daisyUI input 样式。\n */\nconst DateTimeInput: React.FC<DateTimeInputProps> = ({\n type = 'date',\n value,\n defaultValue,\n onChange,\n min,\n max,\n step,\n label,\n placeholder,\n variant = 'default',\n size = 'sm',\n disabled = false,\n readOnly = false,\n name,\n className = '',\n style,\n}) => {\n const inputId = useId();\n const handleChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n onChange?.(e.target.value, e);\n },\n [onChange],\n );\n\n const input = (\n <input\n id={inputId}\n type={type}\n className={cn('input dishui-datetime-input w-full', VARIANT_CLASS[variant], SIZE_CLASS[size], className)}\n value={value}\n defaultValue={value !== undefined ? undefined : defaultValue}\n min={min}\n max={max}\n step={step}\n placeholder={placeholder}\n disabled={disabled}\n readOnly={readOnly}\n name={name}\n onChange={handleChange}\n />\n );\n\n if (label === undefined || label === null || label === '') {\n return input;\n }\n\n return (\n <div className=\"dishui-datetime-field w-full\" style={style}>\n <label htmlFor={inputId} className=\"label-text mb-1 block text-sm text-base-content/80\">\n {label}\n </label>\n {input}\n </div>\n );\n};\n\nexport default DateTimeInput;\n"],"mappings":";;;;;AAqDA,IAAM,IAAiD;CACrD,SAAS;CACT,SAAS;CACT,WAAW;CACX,QAAQ;CACR,MAAM;CACN,SAAS;CACT,SAAS;CACT,OAAO;AACT,GAEM,IAA2C;CAC/C,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN,GAMM,KAA+C,EACnD,UAAO,QACP,UACA,iBACA,aACA,QACA,QACA,SACA,UACA,gBACA,aAAU,WACV,UAAO,MACP,cAAW,IACX,cAAW,IACX,SACA,eAAY,IACZ,eACI;CACJ,IAAM,IAAU,EAAM,GAChB,IAAe,GAClB,MAA2C;EAC1C,IAAW,EAAE,OAAO,OAAO,CAAC;CAC9B,GACA,CAAC,CAAQ,CACX,GAEM,IACJ,kBAAC,SAAD;EACE,IAAI;EACE;EACN,WAAW,EAAG,sCAAsC,EAAc,IAAU,EAAW,IAAO,CAAS;EAChG;EACP,cAAc,MAAU,KAAA,IAAwB,IAAZ,KAAA;EAC/B;EACA;EACC;EACO;EACH;EACA;EACJ;EACN,UAAU;CACX,CAAA;CAOH,OAJI,KAAiC,QAAQ,MAAU,KAC9C,IAIP,kBAAC,OAAD;EAAK,WAAU;EAAsC;EAArD,UAAA,CACE,kBAAC,SAAD;GAAO,SAAS;GAAS,WAAU;GAChC,UAAA;EACI,CAAA,GACN,CACE;;AAET"}
|
|
@@ -10,7 +10,7 @@ var a = ({ placeholder: a, value: o, onChange: s, type: c = "text", variant: l,
|
|
|
10
10
|
if (!F.current) return;
|
|
11
11
|
let t = F.current.checkValidity(), n = [];
|
|
12
12
|
x.length > 0 && x.forEach((t) => {
|
|
13
|
-
t.pattern.test(e) || n.push(t.message);
|
|
13
|
+
(t.pattern.global || t.pattern.sticky ? new RegExp(t.pattern.source, t.pattern.flags) : t.pattern).test(e) || n.push(t.message);
|
|
14
14
|
}), P(n), A(t && n.length === 0);
|
|
15
15
|
}, z = () => {
|
|
16
16
|
O(!D);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/Input/index.tsx"],"sourcesContent":["import React, { useState, useRef, useId } from 'react';\nimport './Input.css';\n\nexport type InputProps = Pick<React.InputHTMLAttributes<HTMLInputElement>, 'id' | 'name' | 'readOnly' | 'aria-label' | 'aria-labelledby' | 'aria-describedby' | 'aria-invalid'> & {\n placeholder?: string;\n value?: string;\n onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;\n type?: string;\n variant?: 'neutral' | 'primary' | 'secondary' | 'accent' | 'info' | 'success' | 'warning' | 'error' | 'ghost';\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';\n disabled?: boolean;\n required?: boolean;\n label?: string;\n labelPosition?: 'inline' | 'top';\n leftSection?: React.ReactNode;\n rightSection?: React.ReactNode;\n pattern?: string;\n className?: string;\n validator?: boolean;\n validatorHint?: string;\n validationRules?: {\n pattern: RegExp;\n message: string;\n }[];\n};\n\nconst Input = ({\n placeholder,\n value,\n onChange,\n type = 'text',\n variant,\n size = 'sm',\n disabled = false,\n required = false,\n label,\n labelPosition = 'inline',\n leftSection,\n rightSection,\n pattern,\n className = '',\n validator = false,\n validatorHint,\n validationRules = [],\n id,\n readOnly,\n ...inputAttributes\n}: InputProps) => {\n const generatedId = useId();\n const fieldId = id || generatedId;\n\n // 密码显示/隐藏状态\n const [showPassword, setShowPassword] = useState(false);\n // 表单验证状态\n const [isValid, setIsValid] = useState<boolean | null>(null);\n // 显示验证提示\n const [showValidationHint, setShowValidationHint] = useState(false);\n // 验证失败的规则消息\n const [failedRules, setFailedRules] = useState<string[]>([]);\n \n // 输入框引用,用于焦点和验证\n const inputRef = useRef<HTMLInputElement>(null);\n \n // 确定实际使用的输入类型\n const actualType = type === 'password' && showPassword ? 'text' : type;\n \n // 处理输入变化和验证\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const newValue = e.target.value;\n \n // 验证输入\n if (validator && inputRef.current) {\n validateInput(newValue);\n }\n \n // 调用原始的onChange处理函数\n if (onChange) {\n onChange(e);\n }\n };\n \n // 验证输入值\n const validateInput = (inputValue: string) => {\n if (!inputRef.current) return;\n \n // 使用HTML5自带的验证\n const isNativeValid = inputRef.current.checkValidity();\n \n // 检查自定义验证规则\n const failedMessages: string[] = [];\n \n if (validationRules.length > 0) {\n validationRules.forEach(rule => {\n if (!rule.pattern.test(inputValue)) {\n failedMessages.push(rule.message);\n }\n });\n }\n \n setFailedRules(failedMessages);\n setIsValid(isNativeValid && failedMessages.length === 0);\n };\n \n // 切换密码显示/隐藏\n const togglePasswordVisibility = () => {\n setShowPassword(!showPassword);\n };\n \n // 鼠标悬停事件处理\n const handleMouseEnter = () => {\n if (validator && !isValid) {\n setShowValidationHint(true);\n }\n };\n \n const handleMouseLeave = () => {\n setShowValidationHint(false);\n };\n \n // 验证状态相关的类名\n let validationStateClass = '';\n if (validator && isValid !== null) {\n validationStateClass = isValid ? 'input-success' : 'input-error';\n }\n \n const fieldAttributes = {\n ...inputAttributes,\n id: fieldId,\n readOnly,\n 'aria-invalid': inputAttributes['aria-invalid'] ?? (validator && isValid === false ? true : undefined),\n };\n const baseClass = 'input dishui-input';\n const variantClass = variant && !validationStateClass ? `input-${variant}` : '';\n const sizeClass = size ? `input-${size}` : '';\n const validatorClass = validator ? 'validator' : '';\n \n\n // 密码可见性切换按钮\n const passwordToggle = type === 'password' && (\n <button\n type=\"button\"\n disabled={disabled}\n aria-label={showPassword ? 'Hide password' : 'Show password'}\n aria-pressed={showPassword}\n className=\"cursor-pointer opacity-70 hover:opacity-100\"\n onClick={togglePasswordVisibility}\n >\n {showPassword ? (\n // 眼睛关闭图标\n <svg xmlns=\"http://www.w3.org/2000/svg\" className=\"h-5 w-5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94\" />\n <path d=\"M14.12 14.12a3 3 0 1 1-4.24-4.24\" />\n <path d=\"M1 1l22 22\" />\n <path d=\"M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19\" />\n </svg>\n ) : (\n // 眼睛打开图标\n <svg xmlns=\"http://www.w3.org/2000/svg\" className=\"h-5 w-5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\" />\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n </svg>\n )}\n </button>\n );\n \n // 构建验证提示悬浮框\n const validationTooltip = (\n <div className=\"validation-tooltip\">\n {failedRules.length > 0 ? (\n <ul className=\"text-xs list-disc pl-4 py-1\">\n {failedRules.map((rule, index) => (\n <li key={index}>{rule}</li>\n ))}\n </ul>\n ) : validatorHint ? (\n <p className=\"text-xs py-1\">{validatorHint}</p>\n ) : null}\n </div>\n );\n\n // 如果有label,则使用label作为容器\n if (label || leftSection || rightSection || type === 'password') {\n // labelPosition === 'top': label above the input field\n if (label && labelPosition === 'top') {\n return (\n <div className={`relative w-full ${className}`} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>\n <label htmlFor={fieldId} className=\"text-sm font-medium mb-1 block\">{label}</label>\n <label className={`${baseClass} ${variantClass} ${sizeClass} ${validatorClass} ${validationStateClass} w-full flex items-center gap-2`}>\n {leftSection && <span>{leftSection}</span>}\n <input\n {...fieldAttributes}\n ref={inputRef}\n type={actualType}\n placeholder={placeholder}\n value={value}\n onChange={handleInputChange}\n disabled={disabled}\n required={required}\n className=\"grow\"\n pattern={pattern}\n />\n {type === 'password' ? (\n <button type=\"button\" disabled={disabled} aria-label={showPassword ? 'Hide password' : 'Show password'} aria-pressed={showPassword} className=\"cursor-pointer opacity-70 hover:opacity-100\" onClick={togglePasswordVisibility}>\n {showPassword ? (\n <svg xmlns=\"http://www.w3.org/2000/svg\" className=\"h-5 w-5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94\" />\n <path d=\"M14.12 14.12a3 3 0 1 1-4.24-4.24\" />\n <path d=\"M1 1l22 22\" />\n <path d=\"M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19\" />\n </svg>\n ) : (\n <svg xmlns=\"http://www.w3.org/2000/svg\" className=\"h-5 w-5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\" />\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n </svg>\n )}\n </button>\n ) : rightSection ? (\n <span>{rightSection}</span>\n ) : null}\n </label>\n {validator && validatorHint && !showValidationHint && (\n <p className=\"validator-hint text-xs mt-1 opacity-70\">{validatorHint}</p>\n )}\n {showValidationHint && validator && !isValid && (\n <div className=\"absolute z-10 mt-1 bg-base-200 border border-error rounded shadow-lg text-error px-3 py-1\">\n {validationTooltip}\n </div>\n )}\n </div>\n );\n }\n\n // Default: inline label\n return (\n <div className={`relative w-full ${className}`} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>\n <label className={`${baseClass} ${variantClass} ${sizeClass} ${validatorClass} ${validationStateClass} w-full flex items-center gap-2`}>\n {leftSection && <span>{leftSection}</span>}\n {label && <span>{label}</span>}\n <input\n {...fieldAttributes}\n ref={inputRef}\n type={actualType}\n placeholder={placeholder}\n value={value}\n onChange={handleInputChange}\n disabled={disabled}\n required={required}\n className=\"grow\"\n pattern={pattern}\n />\n {/* 加入密码切换按钮或其他右侧元素 */}\n {type === 'password' ? (\n <span>{passwordToggle}</span>\n ) : rightSection ? (\n <span>{rightSection}</span>\n ) : null}\n </label>\n {validator && validatorHint && !showValidationHint && (\n <p className=\"validator-hint text-xs mt-1 opacity-70\">{validatorHint}</p>\n )}\n {showValidationHint && validator && !isValid && (\n <div className=\"absolute z-10 mt-1 bg-base-200 border border-error rounded shadow-lg text-error px-3 py-1\">\n {validationTooltip}\n </div>\n )}\n </div>\n );\n }\n\n // 如果没有label,则直接返回input\n return (\n <div className={`relative w-full ${className}`} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>\n <input\n {...fieldAttributes}\n ref={inputRef}\n type={actualType}\n placeholder={placeholder}\n value={value}\n onChange={handleInputChange}\n disabled={disabled}\n required={required}\n className={`${baseClass} ${variantClass} ${sizeClass} ${validatorClass} ${validationStateClass} w-full`}\n pattern={pattern}\n />\n {validator && validatorHint && !showValidationHint && (\n <p className=\"validator-hint text-xs mt-1 opacity-70\">{validatorHint}</p>\n )}\n {showValidationHint && validator && !isValid && (\n <div className=\"absolute z-10 mt-1 bg-base-200 border border-error rounded shadow-lg text-error px-3 py-1\">\n {validationTooltip}\n </div>\n )}\n </div>\n );\n};\n\nexport default Input; "],"mappings":";;;;AA0BA,IAAM,KAAS,EACb,gBACA,UACA,aACA,UAAO,QACP,YACA,UAAO,MACP,cAAW,IACX,cAAW,IACX,UACA,mBAAgB,UAChB,gBACA,iBACA,YACA,eAAY,IACZ,eAAY,IACZ,kBACA,qBAAkB,CAAC,GACnB,OACA,aACA,GAAG,QACa;CAChB,IAAM,IAAc,EAAM,GACpB,IAAU,KAAM,GAGhB,CAAC,GAAc,KAAmB,EAAS,EAAK,GAEhD,CAAC,GAAS,KAAc,EAAyB,IAAI,GAErD,CAAC,GAAoB,KAAyB,EAAS,EAAK,GAE5D,CAAC,GAAa,KAAkB,EAAmB,CAAC,CAAC,GAGrD,IAAW,EAAyB,IAAI,GAGxC,IAAa,MAAS,cAAc,IAAe,SAAS,GAG5D,KAAqB,MAA2C;EACpE,IAAM,IAAW,EAAE,OAAO;EAQ1B,AALI,KAAa,EAAS,WACxB,EAAc,CAAQ,GAIpB,KACF,EAAS,CAAC;CAEd,GAGM,KAAiB,MAAuB;EAC5C,IAAI,CAAC,EAAS,SAAS;EAGvB,IAAM,IAAgB,EAAS,QAAQ,cAAc,GAG/C,IAA2B,CAAC;EAWlC,AATI,EAAgB,SAAS,KAC3B,EAAgB,SAAQ,MAAQ;GAC9B,AAAK,EAAK,QAAQ,KAAK,CAAU,KAC/B,EAAe,KAAK,EAAK,OAAO;EAEpC,CAAC,GAGH,EAAe,CAAc,GAC7B,EAAW,KAAiB,EAAe,WAAW,CAAC;CACzD,GAGM,UAAiC;EACrC,EAAgB,CAAC,CAAY;CAC/B,GAGM,UAAyB;EAC7B,AAAI,KAAa,CAAC,KAChB,EAAsB,EAAI;CAE9B,GAEM,UAAyB;EAC7B,EAAsB,EAAK;CAC7B,GAGI,IAAuB;CAC3B,AAAI,KAAa,MAAY,SAC3B,IAAuB,IAAU,kBAAkB;CAGrD,IAAM,IAAkB;EACtB,GAAG;EACH,IAAI;EACJ;EACA,gBAAgB,EAAgB,oBAAoB,KAAa,MAAY,KAAQ,KAAO,KAAA;CAC9F,GACM,IAAY,sBACZ,IAAe,KAAW,CAAC,IAAuB,SAAS,MAAY,IACvE,IAAY,IAAO,SAAS,MAAS,IACrC,IAAiB,IAAY,cAAc,IAI3C,IAAiB,MAAS,cAC9B,kBAAC,UAAD;EACE,MAAK;EACK;EACV,cAAY,IAAe,kBAAkB;EAC7C,gBAAc;EACd,WAAU;EACV,SAAS;EAER,UAAA,IAEC,kBAAC,OAAD;GAAK,OAAM;GAA6B,WAAU;GAAU,SAAQ;GAAY,MAAK;GAAO,QAAO;GAAe,aAAY;GAA9H,UAAA;IACE,kBAAC,QAAD,EAAM,GAAE,kFAAmF,CAAA;IAC3F,kBAAC,QAAD,EAAM,GAAE,mCAAoC,CAAA;IAC5C,kBAAC,QAAD,EAAM,GAAE,aAAc,CAAA;IACtB,kBAAC,QAAD,EAAM,GAAE,yEAA0E,CAAA;GAC/E;EAGL,CAAA,IAAA,kBAAC,OAAD;GAAK,OAAM;GAA6B,WAAU;GAAU,SAAQ;GAAY,MAAK;GAAO,QAAO;GAAe,aAAY;GAA9H,UAAA,CACE,kBAAC,QAAD,EAAM,GAAE,+CAAgD,CAAA,GACxD,kBAAC,UAAD;IAAQ,IAAG;IAAK,IAAG;IAAK,GAAE;GAAK,CAAA,CAC5B;;CAED,CAAA,GAIJ,IACJ,kBAAC,OAAD;EAAK,WAAU;EACZ,UAAA,EAAY,SAAS,IACpB,kBAAC,MAAD;GAAI,WAAU;GACX,UAAA,EAAY,KAAK,GAAM,MACtB,kBAAC,MAAD,EAAA,UAAiB,EAAS,GAAjB,CAAiB,CAC3B;EACC,CAAA,IACF,IACF,kBAAC,KAAD;GAAG,WAAU;GAAgB,UAAA;EAAiB,CAAA,IAC5C;CACD,CAAA;CA8FP,OA1FI,KAAS,KAAe,KAAgB,MAAS,aAE/C,KAAS,MAAkB,QAE3B,kBAAC,OAAD;EAAK,WAAW,mBAAmB;EAAa,cAAc;EAAkB,cAAc;EAA9F,UAAA;GACE,kBAAC,SAAD;IAAO,SAAS;IAAS,WAAU;IAAkC,UAAA;GAAa,CAAA;GAClF,kBAAC,SAAD;IAAO,WAAW,GAAG,EAAU,GAAG,EAAa,GAAG,EAAU,GAAG,EAAe,GAAG,EAAqB;IAAtG,UAAA;KACG,KAAe,kBAAC,QAAD,EAAA,UAAO,EAAkB,CAAA;KACzC,kBAAC,SAAD;MACE,GAAI;MACJ,KAAK;MACL,MAAM;MACO;MACN;MACP,UAAU;MACA;MACA;MACV,WAAU;MACD;KACV,CAAA;KACA,MAAS,aACR,kBAAC,UAAD;MAAQ,MAAK;MAAmB;MAAU,cAAY,IAAe,kBAAkB;MAAiB,gBAAc;MAAc,WAAU;MAA8C,SAAS;MAClM,UAAA,IACC,kBAAC,OAAD;OAAK,OAAM;OAA6B,WAAU;OAAU,SAAQ;OAAY,MAAK;OAAO,QAAO;OAAe,aAAY;OAA9H,UAAA;QACE,kBAAC,QAAD,EAAM,GAAE,kFAAmF,CAAA;QAC3F,kBAAC,QAAD,EAAM,GAAE,mCAAoC,CAAA;QAC5C,kBAAC,QAAD,EAAM,GAAE,aAAc,CAAA;QACtB,kBAAC,QAAD,EAAM,GAAE,yEAA0E,CAAA;OAC/E;MAEL,CAAA,IAAA,kBAAC,OAAD;OAAK,OAAM;OAA6B,WAAU;OAAU,SAAQ;OAAY,MAAK;OAAO,QAAO;OAAe,aAAY;OAA9H,UAAA,CACE,kBAAC,QAAD,EAAM,GAAE,+CAAgD,CAAA,GACxD,kBAAC,UAAD;QAAQ,IAAG;QAAK,IAAG;QAAK,GAAE;OAAK,CAAA,CAC5B;;KAED,CAAA,IACN,IACF,kBAAC,QAAD,EAAA,UAAO,EAAmB,CAAA,IACxB;IACC;;GACN,KAAa,KAAiB,CAAC,KAC9B,kBAAC,KAAD;IAAG,WAAU;IAA0C,UAAA;GAAiB,CAAA;GAEzE,KAAsB,KAAa,CAAC,KACnC,kBAAC,OAAD;IAAK,WAAU;IACZ,UAAA;GACE,CAAA;EAEJ;MAMP,kBAAC,OAAD;EAAK,WAAW,mBAAmB;EAAa,cAAc;EAAkB,cAAc;EAA9F,UAAA;GACE,kBAAC,SAAD;IAAO,WAAW,GAAG,EAAU,GAAG,EAAa,GAAG,EAAU,GAAG,EAAe,GAAG,EAAqB;IAAtG,UAAA;KACG,KAAe,kBAAC,QAAD,EAAA,UAAO,EAAkB,CAAA;KACxC,KAAS,kBAAC,QAAD,EAAA,UAAO,EAAY,CAAA;KAC7B,kBAAC,SAAD;MACE,GAAI;MACJ,KAAK;MACL,MAAM;MACO;MACN;MACP,UAAU;MACA;MACA;MACV,WAAU;MACD;KACV,CAAA;KAEA,MAAS,aACR,kBAAC,QAAD,EAAA,UAAO,EAAqB,CAAA,IAC1B,IACF,kBAAC,QAAD,EAAA,UAAO,EAAmB,CAAA,IACxB;IACC;;GACN,KAAa,KAAiB,CAAC,KAC9B,kBAAC,KAAD;IAAG,WAAU;IAA0C,UAAA;GAAiB,CAAA;GAEzE,KAAsB,KAAa,CAAC,KACnC,kBAAC,OAAD;IAAK,WAAU;IACZ,UAAA;GACE,CAAA;EAEJ;MAMP,kBAAC,OAAD;EAAK,WAAW,mBAAmB;EAAa,cAAc;EAAkB,cAAc;EAA9F,UAAA;GACE,kBAAC,SAAD;IACE,GAAI;IACJ,KAAK;IACL,MAAM;IACO;IACN;IACP,UAAU;IACA;IACA;IACV,WAAW,GAAG,EAAU,GAAG,EAAa,GAAG,EAAU,GAAG,EAAe,GAAG,EAAqB;IACtF;GACV,CAAA;GACA,KAAa,KAAiB,CAAC,KAC9B,kBAAC,KAAD;IAAG,WAAU;IAA0C,UAAA;GAAiB,CAAA;GAEzE,KAAsB,KAAa,CAAC,KACnC,kBAAC,OAAD;IAAK,WAAU;IACZ,UAAA;GACE,CAAA;EAEJ;;AAET"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/Input/index.tsx"],"sourcesContent":["import React, { useState, useRef, useId } from 'react';\nimport './Input.css';\n\nexport type InputProps = Pick<React.InputHTMLAttributes<HTMLInputElement>, 'id' | 'name' | 'readOnly' | 'aria-label' | 'aria-labelledby' | 'aria-describedby' | 'aria-invalid'> & {\n placeholder?: string;\n value?: string;\n onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;\n type?: string;\n variant?: 'neutral' | 'primary' | 'secondary' | 'accent' | 'info' | 'success' | 'warning' | 'error' | 'ghost';\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';\n disabled?: boolean;\n required?: boolean;\n label?: string;\n labelPosition?: 'inline' | 'top';\n leftSection?: React.ReactNode;\n rightSection?: React.ReactNode;\n pattern?: string;\n className?: string;\n validator?: boolean;\n validatorHint?: string;\n validationRules?: {\n pattern: RegExp;\n message: string;\n }[];\n};\n\nconst Input = ({\n placeholder,\n value,\n onChange,\n type = 'text',\n variant,\n size = 'sm',\n disabled = false,\n required = false,\n label,\n labelPosition = 'inline',\n leftSection,\n rightSection,\n pattern,\n className = '',\n validator = false,\n validatorHint,\n validationRules = [],\n id,\n readOnly,\n ...inputAttributes\n}: InputProps) => {\n const generatedId = useId();\n const fieldId = id || generatedId;\n\n // 密码显示/隐藏状态\n const [showPassword, setShowPassword] = useState(false);\n // 表单验证状态\n const [isValid, setIsValid] = useState<boolean | null>(null);\n // 显示验证提示\n const [showValidationHint, setShowValidationHint] = useState(false);\n // 验证失败的规则消息\n const [failedRules, setFailedRules] = useState<string[]>([]);\n \n // 输入框引用,用于焦点和验证\n const inputRef = useRef<HTMLInputElement>(null);\n \n // 确定实际使用的输入类型\n const actualType = type === 'password' && showPassword ? 'text' : type;\n \n // 处理输入变化和验证\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const newValue = e.target.value;\n \n // 验证输入\n if (validator && inputRef.current) {\n validateInput(newValue);\n }\n \n // 调用原始的onChange处理函数\n if (onChange) {\n onChange(e);\n }\n };\n \n // 验证输入值\n const validateInput = (inputValue: string) => {\n if (!inputRef.current) return;\n \n // 使用HTML5自带的验证\n const isNativeValid = inputRef.current.checkValidity();\n \n // 检查自定义验证规则\n const failedMessages: string[] = [];\n \n if (validationRules.length > 0) {\n validationRules.forEach(rule => {\n // Global/sticky RegExp.test mutates lastIndex; validation must not depend on prior keystrokes.\n const rulePattern = rule.pattern.global || rule.pattern.sticky\n ? new RegExp(rule.pattern.source, rule.pattern.flags)\n : rule.pattern;\n if (!rulePattern.test(inputValue)) {\n failedMessages.push(rule.message);\n }\n });\n }\n \n setFailedRules(failedMessages);\n setIsValid(isNativeValid && failedMessages.length === 0);\n };\n \n // 切换密码显示/隐藏\n const togglePasswordVisibility = () => {\n setShowPassword(!showPassword);\n };\n \n // 鼠标悬停事件处理\n const handleMouseEnter = () => {\n if (validator && !isValid) {\n setShowValidationHint(true);\n }\n };\n \n const handleMouseLeave = () => {\n setShowValidationHint(false);\n };\n \n // 验证状态相关的类名\n let validationStateClass = '';\n if (validator && isValid !== null) {\n validationStateClass = isValid ? 'input-success' : 'input-error';\n }\n \n const fieldAttributes = {\n ...inputAttributes,\n id: fieldId,\n readOnly,\n 'aria-invalid': inputAttributes['aria-invalid'] ?? (validator && isValid === false ? true : undefined),\n };\n const baseClass = 'input dishui-input';\n const variantClass = variant && !validationStateClass ? `input-${variant}` : '';\n const sizeClass = size ? `input-${size}` : '';\n const validatorClass = validator ? 'validator' : '';\n \n\n // 密码可见性切换按钮\n const passwordToggle = type === 'password' && (\n <button\n type=\"button\"\n disabled={disabled}\n aria-label={showPassword ? 'Hide password' : 'Show password'}\n aria-pressed={showPassword}\n className=\"cursor-pointer opacity-70 hover:opacity-100\"\n onClick={togglePasswordVisibility}\n >\n {showPassword ? (\n // 眼睛关闭图标\n <svg xmlns=\"http://www.w3.org/2000/svg\" className=\"h-5 w-5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94\" />\n <path d=\"M14.12 14.12a3 3 0 1 1-4.24-4.24\" />\n <path d=\"M1 1l22 22\" />\n <path d=\"M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19\" />\n </svg>\n ) : (\n // 眼睛打开图标\n <svg xmlns=\"http://www.w3.org/2000/svg\" className=\"h-5 w-5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\" />\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n </svg>\n )}\n </button>\n );\n \n // 构建验证提示悬浮框\n const validationTooltip = (\n <div className=\"validation-tooltip\">\n {failedRules.length > 0 ? (\n <ul className=\"text-xs list-disc pl-4 py-1\">\n {failedRules.map((rule, index) => (\n <li key={index}>{rule}</li>\n ))}\n </ul>\n ) : validatorHint ? (\n <p className=\"text-xs py-1\">{validatorHint}</p>\n ) : null}\n </div>\n );\n\n // 如果有label,则使用label作为容器\n if (label || leftSection || rightSection || type === 'password') {\n // labelPosition === 'top': label above the input field\n if (label && labelPosition === 'top') {\n return (\n <div className={`relative w-full ${className}`} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>\n <label htmlFor={fieldId} className=\"text-sm font-medium mb-1 block\">{label}</label>\n <label className={`${baseClass} ${variantClass} ${sizeClass} ${validatorClass} ${validationStateClass} w-full flex items-center gap-2`}>\n {leftSection && <span>{leftSection}</span>}\n <input\n {...fieldAttributes}\n ref={inputRef}\n type={actualType}\n placeholder={placeholder}\n value={value}\n onChange={handleInputChange}\n disabled={disabled}\n required={required}\n className=\"grow\"\n pattern={pattern}\n />\n {type === 'password' ? (\n <button type=\"button\" disabled={disabled} aria-label={showPassword ? 'Hide password' : 'Show password'} aria-pressed={showPassword} className=\"cursor-pointer opacity-70 hover:opacity-100\" onClick={togglePasswordVisibility}>\n {showPassword ? (\n <svg xmlns=\"http://www.w3.org/2000/svg\" className=\"h-5 w-5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94\" />\n <path d=\"M14.12 14.12a3 3 0 1 1-4.24-4.24\" />\n <path d=\"M1 1l22 22\" />\n <path d=\"M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19\" />\n </svg>\n ) : (\n <svg xmlns=\"http://www.w3.org/2000/svg\" className=\"h-5 w-5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\" />\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n </svg>\n )}\n </button>\n ) : rightSection ? (\n <span>{rightSection}</span>\n ) : null}\n </label>\n {validator && validatorHint && !showValidationHint && (\n <p className=\"validator-hint text-xs mt-1 opacity-70\">{validatorHint}</p>\n )}\n {showValidationHint && validator && !isValid && (\n <div className=\"absolute z-10 mt-1 bg-base-200 border border-error rounded shadow-lg text-error px-3 py-1\">\n {validationTooltip}\n </div>\n )}\n </div>\n );\n }\n\n // Default: inline label\n return (\n <div className={`relative w-full ${className}`} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>\n <label className={`${baseClass} ${variantClass} ${sizeClass} ${validatorClass} ${validationStateClass} w-full flex items-center gap-2`}>\n {leftSection && <span>{leftSection}</span>}\n {label && <span>{label}</span>}\n <input\n {...fieldAttributes}\n ref={inputRef}\n type={actualType}\n placeholder={placeholder}\n value={value}\n onChange={handleInputChange}\n disabled={disabled}\n required={required}\n className=\"grow\"\n pattern={pattern}\n />\n {/* 加入密码切换按钮或其他右侧元素 */}\n {type === 'password' ? (\n <span>{passwordToggle}</span>\n ) : rightSection ? (\n <span>{rightSection}</span>\n ) : null}\n </label>\n {validator && validatorHint && !showValidationHint && (\n <p className=\"validator-hint text-xs mt-1 opacity-70\">{validatorHint}</p>\n )}\n {showValidationHint && validator && !isValid && (\n <div className=\"absolute z-10 mt-1 bg-base-200 border border-error rounded shadow-lg text-error px-3 py-1\">\n {validationTooltip}\n </div>\n )}\n </div>\n );\n }\n\n // 如果没有label,则直接返回input\n return (\n <div className={`relative w-full ${className}`} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>\n <input\n {...fieldAttributes}\n ref={inputRef}\n type={actualType}\n placeholder={placeholder}\n value={value}\n onChange={handleInputChange}\n disabled={disabled}\n required={required}\n className={`${baseClass} ${variantClass} ${sizeClass} ${validatorClass} ${validationStateClass} w-full`}\n pattern={pattern}\n />\n {validator && validatorHint && !showValidationHint && (\n <p className=\"validator-hint text-xs mt-1 opacity-70\">{validatorHint}</p>\n )}\n {showValidationHint && validator && !isValid && (\n <div className=\"absolute z-10 mt-1 bg-base-200 border border-error rounded shadow-lg text-error px-3 py-1\">\n {validationTooltip}\n </div>\n )}\n </div>\n );\n};\n\nexport default Input; "],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/MultipleChoice/index.tsx"],"sourcesContent":["import React, { useCallback, useMemo, useState } from 'react';\nimport { cn } from '../../utils/cn';\nimport Checkbox from '../Checkbox';\n\nexport type MultipleChoiceOption = {\n /** Value submitted for this option. */\n value: string | number;\n /** Display label. */\n label: React.ReactNode;\n /** Disable this option only. */\n disabled?: boolean;\n};\n\nexport type MultipleChoiceVariant =\n | 'default'\n | 'primary'\n | 'secondary'\n | 'accent'\n | 'info'\n | 'success'\n | 'warning'\n | 'error';\n\nexport type MultipleChoiceProps = {\n /** Available options. */\n options: MultipleChoiceOption[];\n /** Controlled selected values. */\n value?: Array<string | number>;\n /** Uncontrolled initial selection. */\n defaultValue?: Array<string | number>;\n /** Change handler with the full selected-value array. */\n onChange?: (values: Array<string | number>) => void;\n /** Layout of the options. */\n orientation?: 'vertical' | 'horizontal';\n /** Color variant forwarded to each checkbox. */\n variant?: MultipleChoiceVariant;\n /** Size forwarded to each checkbox. */\n size?: 'xs' | 'sm' | 'md' | 'lg';\n /** Disable the whole group. */\n disabled?: boolean;\n /** Limit the number of selectable options. */\n maxSelected?: number;\n /** Optional group label rendered above the options. */\n label?: React.ReactNode;\n /** Native name attribute for each checkbox. */\n name?: string;\n /** Extra class names on the wrapper. */\n className?: string;\n /** Inline styles on the wrapper. */\n style?: React.CSSProperties;\n};\n\n/**\n * MultipleChoice —— A2UI 交互组件(v0.8):多选项组。\n * 基于 dishui Checkbox 的受控 / 非受控多选组,支持横竖排与最大选择数限制。\n */\nconst MultipleChoice: React.FC<MultipleChoiceProps> = ({\n options,\n value,\n defaultValue = [],\n onChange,\n orientation = 'vertical',\n variant = 'default',\n size = 'sm',\n disabled = false,\n maxSelected,\n label,\n name,\n className = '',\n style,\n}) => {\n const [internal, setInternal] = useState<Array<string | number>>(defaultValue);\n const isControlled = value !== undefined;\n const selected = useMemo(\n () => new Set<string | number>(isControlled ? (value as Array<string | number>) : internal),\n [isControlled, value, internal],\n );\n\n const toggle = useCallback(\n (optionValue: string | number, next: boolean) => {\n const base = Array.from(selected);\n const result = next ? [...base, optionValue] : base.filter((v) => v !== optionValue);\n if (!isControlled) setInternal(result);\n onChange?.(result);\n },\n [selected, isControlled, onChange],\n );\n\n const handleToggle = useCallback(\n (optionValue: string | number, next: boolean) => {\n // Enforce maxSelected only when turning a new option ON.\n if (next && maxSelected !== undefined && selected.size >= maxSelected) return;\n toggle(optionValue, next);\n },\n [toggle, maxSelected, selected],\n );\n\n return (\n <div className={cn('w-full', className)} style={style}>\n {label !== undefined && (\n <div className=\"mb-1.5 text-sm font-medium text-base-content/80\">{label}</div>\n )}\n <div\n className={cn(\n 'gap-2',\n orientation === 'horizontal' ? 'flex flex-wrap items-center' : 'flex flex-col',\n )}\n role=\"group\"\n >\n {options.map((option) => {\n const checked = selected.has(option.value);\n const optionDisabled = disabled || option.disabled;\n return (\n <Checkbox\n key={String(option.value)}\n label={option.label}\n checked={checked}\n onChange={(next) => handleToggle(option.value, next)}\n variant={variant === 'default' ? 'default' : variant}\n size={size}\n disabled={optionDisabled}\n name={name}\n />\n );\n })}\n </div>\n </div>\n );\n};\n\nexport default MultipleChoice;\n"],"mappings":";;;;;AAwDA,IAAM,KAAiD,EACrD,YACA,UACA,kBAAe,CAAC,GAChB,aACA,iBAAc,YACd,aAAU,WACV,UAAO,MACP,cAAW,IACX,gBACA,UACA,SACA,eAAY,IACZ,eACI;CACJ,IAAM,CAAC,GAAU,KAAe,EAAiC,CAAY,GACvE,IAAe,MAAU,KAAA,GACzB,IAAW,QACT,IAAI,IAAqB,IAAgB,IAAmC,CAAQ,GAC1F;EAAC;EAAc;EAAO;CAAQ,CAChC,GAEM,IAAS,GACZ,GAA8B,MAAkB;EAC/C,IAAM,IAAO,MAAM,KAAK,CAAQ,GAC1B,IAAS,IAAO,CAAC,GAAG,GAAM,CAAW,IAAI,EAAK,QAAQ,MAAM,MAAM,CAAW;EAEnF,AADK,KAAc,EAAY,CAAM,GACrC,IAAW,CAAM;CACnB,GACA;EAAC;EAAU;EAAc;CAAQ,CACnC,GAEM,IAAe,GAClB,GAA8B,MAAkB;EAE3C,KAAQ,MAAgB,KAAA,KAAa,EAAS,QAAQ,KAC1D,EAAO,GAAa,CAAI;CAC1B,GACA;EAAC;EAAQ;EAAa;CAAQ,CAChC;CAEA,OACE,kBAAC,OAAD;EAAK,WAAW,EAAG,UAAU,CAAS;EAAU;EAAhD,UAAA,CACG,MAAU,KAAA,KACT,kBAAC,OAAD;GAAK,WAAU;GAAmD,UAAA;EAAW,CAAA,GAE/E,kBAAC,OAAD;GACE,WAAW,EACT,SACA,MAAgB,eAAe,gCAAgC,eACjE;GACA,MAAK;GAEJ,UAAA,EAAQ,KAAK,MAAW;IACvB,IAAM,IAAU,EAAS,IAAI,EAAO,KAAK,GACnC,IAAiB,KAAY,EAAO;IAC1C,OACE,kBAAC,GAAD;KAEE,OAAO,EAAO;KACL;KACT,WAAW,MAAS,EAAa,EAAO,OAAO,CAAI;KACnD,SAAS,MAAY,YAAY,YAAY;KACvC;KACN,UAAU;KACJ;
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/MultipleChoice/index.tsx"],"sourcesContent":["import React, { useCallback, useMemo, useState } from 'react';\nimport { cn } from '../../utils/cn';\nimport Checkbox from '../Checkbox';\n\nexport type MultipleChoiceOption = {\n /** Value submitted for this option. */\n value: string | number;\n /** Display label. */\n label: React.ReactNode;\n /** Disable this option only. */\n disabled?: boolean;\n};\n\nexport type MultipleChoiceVariant =\n | 'default'\n | 'primary'\n | 'secondary'\n | 'accent'\n | 'info'\n | 'success'\n | 'warning'\n | 'error';\n\nexport type MultipleChoiceProps = {\n /** Available options. */\n options: MultipleChoiceOption[];\n /** Controlled selected values. */\n value?: Array<string | number>;\n /** Uncontrolled initial selection. */\n defaultValue?: Array<string | number>;\n /** Change handler with the full selected-value array. */\n onChange?: (values: Array<string | number>) => void;\n /** Layout of the options. */\n orientation?: 'vertical' | 'horizontal';\n /** Color variant forwarded to each checkbox. */\n variant?: MultipleChoiceVariant;\n /** Size forwarded to each checkbox. */\n size?: 'xs' | 'sm' | 'md' | 'lg';\n /** Disable the whole group. */\n disabled?: boolean;\n /** Limit the number of selectable options. */\n maxSelected?: number;\n /** Optional group label rendered above the options. */\n label?: React.ReactNode;\n /** Native name attribute for each checkbox. */\n name?: string;\n /** Extra class names on the wrapper. */\n className?: string;\n /** Inline styles on the wrapper. */\n style?: React.CSSProperties;\n};\n\n/**\n * MultipleChoice —— A2UI 交互组件(v0.8):多选项组。\n * 基于 dishui Checkbox 的受控 / 非受控多选组,支持横竖排与最大选择数限制。\n */\nconst MultipleChoice: React.FC<MultipleChoiceProps> = ({\n options,\n value,\n defaultValue = [],\n onChange,\n orientation = 'vertical',\n variant = 'default',\n size = 'sm',\n disabled = false,\n maxSelected,\n label,\n name,\n className = '',\n style,\n}) => {\n const [internal, setInternal] = useState<Array<string | number>>(defaultValue);\n const isControlled = value !== undefined;\n const selected = useMemo(\n () => new Set<string | number>(isControlled ? (value as Array<string | number>) : internal),\n [isControlled, value, internal],\n );\n\n const toggle = useCallback(\n (optionValue: string | number, next: boolean) => {\n const base = Array.from(selected);\n const result = next ? [...base, optionValue] : base.filter((v) => v !== optionValue);\n if (!isControlled) setInternal(result);\n onChange?.(result);\n },\n [selected, isControlled, onChange],\n );\n\n const handleToggle = useCallback(\n (optionValue: string | number, next: boolean) => {\n // Enforce maxSelected only when turning a new option ON.\n if (next && maxSelected !== undefined && selected.size >= maxSelected) return;\n toggle(optionValue, next);\n },\n [toggle, maxSelected, selected],\n );\n\n return (\n <div className={cn('w-full', className)} style={style}>\n {label !== undefined && (\n <div className=\"mb-1.5 text-sm font-medium text-base-content/80\">{label}</div>\n )}\n <div\n className={cn(\n 'gap-2',\n orientation === 'horizontal' ? 'flex flex-wrap items-center' : 'flex flex-col',\n )}\n role=\"group\"\n >\n {options.map((option) => {\n const checked = selected.has(option.value);\n const optionDisabled = disabled || option.disabled;\n return (\n <Checkbox\n key={String(option.value)}\n label={option.label}\n checked={checked}\n onChange={(next) => handleToggle(option.value, next)}\n variant={variant === 'default' ? 'default' : variant}\n size={size}\n disabled={optionDisabled}\n name={name}\n value={String(option.value)}\n />\n );\n })}\n </div>\n </div>\n );\n};\n\nexport default MultipleChoice;\n"],"mappings":";;;;;AAwDA,IAAM,KAAiD,EACrD,YACA,UACA,kBAAe,CAAC,GAChB,aACA,iBAAc,YACd,aAAU,WACV,UAAO,MACP,cAAW,IACX,gBACA,UACA,SACA,eAAY,IACZ,eACI;CACJ,IAAM,CAAC,GAAU,KAAe,EAAiC,CAAY,GACvE,IAAe,MAAU,KAAA,GACzB,IAAW,QACT,IAAI,IAAqB,IAAgB,IAAmC,CAAQ,GAC1F;EAAC;EAAc;EAAO;CAAQ,CAChC,GAEM,IAAS,GACZ,GAA8B,MAAkB;EAC/C,IAAM,IAAO,MAAM,KAAK,CAAQ,GAC1B,IAAS,IAAO,CAAC,GAAG,GAAM,CAAW,IAAI,EAAK,QAAQ,MAAM,MAAM,CAAW;EAEnF,AADK,KAAc,EAAY,CAAM,GACrC,IAAW,CAAM;CACnB,GACA;EAAC;EAAU;EAAc;CAAQ,CACnC,GAEM,IAAe,GAClB,GAA8B,MAAkB;EAE3C,KAAQ,MAAgB,KAAA,KAAa,EAAS,QAAQ,KAC1D,EAAO,GAAa,CAAI;CAC1B,GACA;EAAC;EAAQ;EAAa;CAAQ,CAChC;CAEA,OACE,kBAAC,OAAD;EAAK,WAAW,EAAG,UAAU,CAAS;EAAU;EAAhD,UAAA,CACG,MAAU,KAAA,KACT,kBAAC,OAAD;GAAK,WAAU;GAAmD,UAAA;EAAW,CAAA,GAE/E,kBAAC,OAAD;GACE,WAAW,EACT,SACA,MAAgB,eAAe,gCAAgC,eACjE;GACA,MAAK;GAEJ,UAAA,EAAQ,KAAK,MAAW;IACvB,IAAM,IAAU,EAAS,IAAI,EAAO,KAAK,GACnC,IAAiB,KAAY,EAAO;IAC1C,OACE,kBAAC,GAAD;KAEE,OAAO,EAAO;KACL;KACT,WAAW,MAAS,EAAa,EAAO,OAAO,CAAI;KACnD,SAAS,MAAY,YAAY,YAAY;KACvC;KACN,UAAU;KACJ;KACN,OAAO,OAAO,EAAO,KAAK;IAC3B,GATM,OAAO,EAAO,KAAK,CASzB;GAEL,CAAC;EACE,CAAA,CACF;;AAET"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
|
|
3
|
-
export type
|
|
2
|
+
import { type FlexAlign, type FlexJustify } from '../../utils/flexLayout';
|
|
3
|
+
export type RowAlign = FlexAlign;
|
|
4
|
+
export type RowJustify = FlexJustify;
|
|
4
5
|
export type RowProps = {
|
|
5
6
|
children?: React.ReactNode;
|
|
6
7
|
/** Gap between children; number is treated as px. */
|
|
@@ -1,24 +1,12 @@
|
|
|
1
1
|
import { cn as e } from "../../utils/cn.js";
|
|
2
|
+
import { FLEX_ALIGN_CLASS as t, FLEX_JUSTIFY_CLASS as n, flexGap as r } from "../../utils/flexLayout.js";
|
|
2
3
|
import "react";
|
|
3
|
-
import { jsx as
|
|
4
|
+
import { jsx as i } from "react/jsx-runtime";
|
|
4
5
|
//#region src/components/Row/index.tsx
|
|
5
|
-
var
|
|
6
|
-
|
|
7
|
-
center: "items-center",
|
|
8
|
-
end: "items-end",
|
|
9
|
-
baseline: "items-baseline",
|
|
10
|
-
stretch: "items-stretch"
|
|
11
|
-
}, r = {
|
|
12
|
-
start: "justify-start",
|
|
13
|
-
center: "justify-center",
|
|
14
|
-
end: "justify-end",
|
|
15
|
-
between: "justify-between",
|
|
16
|
-
around: "justify-around",
|
|
17
|
-
evenly: "justify-evenly"
|
|
18
|
-
}, i = (e) => typeof e == "number" ? `${e}px` : e, a = ({ children: a, gap: o, align: s = "center", justify: c = "start", wrap: l = !1, reverse: u = !1, className: d = "", style: f, onClick: p }) => /* @__PURE__ */ t("div", {
|
|
19
|
-
className: e("flex", u ? "flex-row-reverse" : "flex-row", n[s], r[c], l && "flex-wrap", d),
|
|
6
|
+
var a = ({ children: a, gap: o, align: s = "center", justify: c = "start", wrap: l = !1, reverse: u = !1, className: d = "", style: f, onClick: p }) => /* @__PURE__ */ i("div", {
|
|
7
|
+
className: e("flex", u ? "flex-row-reverse" : "flex-row", t[s], n[c], l && "flex-wrap", d),
|
|
20
8
|
style: {
|
|
21
|
-
gap:
|
|
9
|
+
gap: r(o),
|
|
22
10
|
...f
|
|
23
11
|
},
|
|
24
12
|
onClick: p,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/Row/index.tsx"],"sourcesContent":["import React from 'react';\nimport { cn } from '../../utils/cn';\
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/Row/index.tsx"],"sourcesContent":["import React from 'react';\nimport { cn } from '../../utils/cn';\nimport { FLEX_ALIGN_CLASS, FLEX_JUSTIFY_CLASS, flexGap, type FlexAlign, type FlexJustify } from '../../utils/flexLayout';\n\nexport type RowAlign = FlexAlign;\nexport type RowJustify = FlexJustify;\n\nexport type RowProps = {\n children?: React.ReactNode;\n /** Gap between children; number is treated as px. */\n gap?: number | string;\n /** Cross-axis alignment. */\n align?: RowAlign;\n /** Main-axis distribution. */\n justify?: RowJustify;\n /** Allow children to wrap onto the next line. */\n wrap?: boolean;\n /** Reverse the visual order of children. */\n reverse?: boolean;\n /** Extra class names on the container. */\n className?: string;\n /** Inline styles on the container. */\n style?: React.CSSProperties;\n /** Click handler on the container. */\n onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;\n};\n\n\n/**\n * Row —— A2UI 布局组件:水平排列子元素。\n * 等价于 `display: flex; flex-direction: row` 的语义化封装。\n */\nconst Row: React.FC<RowProps> = ({\n children,\n gap,\n align = 'center',\n justify = 'start',\n wrap = false,\n reverse = false,\n className = '',\n style,\n onClick,\n}) => (\n <div\n className={cn(\n 'flex',\n reverse ? 'flex-row-reverse' : 'flex-row',\n FLEX_ALIGN_CLASS[align],\n FLEX_JUSTIFY_CLASS[justify],\n wrap && 'flex-wrap',\n className,\n )}\n style={{ gap: flexGap(gap), ...style }}\n onClick={onClick}\n >\n {children}\n </div>\n);\n\nexport default Row;\n"],"mappings":";;;;;AAgCA,IAAM,KAA2B,EAC/B,aACA,QACA,WAAQ,UACR,aAAU,SACV,UAAO,IACP,aAAU,IACV,eAAY,IACZ,UACA,iBAEA,kBAAC,OAAD;CACE,WAAW,EACT,QACA,IAAU,qBAAqB,YAC/B,EAAiB,IACjB,EAAmB,IACnB,KAAQ,aACR,CACF;CACA,OAAO;EAAE,KAAK,EAAQ,CAAG;EAAG,GAAG;CAAM;CAC5B;CAER;AACE,CAAA"}
|