dishui 0.0.82 → 0.0.84

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/dist/components/Button/index.d.ts +1 -0
  2. package/dist/components/Button/index.js.map +1 -1
  3. package/dist/components/ButtonMenu/index.d.ts +28 -0
  4. package/dist/components/ButtonMenu/index.js +197 -0
  5. package/dist/components/ButtonMenu/index.js.map +1 -0
  6. package/dist/components/Demo/index.d.ts +5 -0
  7. package/dist/components/Demo/index.js +131 -56
  8. package/dist/components/Demo/index.js.map +1 -1
  9. package/dist/components/Demo/source.d.ts +9 -0
  10. package/dist/components/Demo/source.js +32 -0
  11. package/dist/components/Demo/source.js.map +1 -0
  12. package/dist/components/Field/index.d.ts +8 -0
  13. package/dist/components/Field/index.js +43 -0
  14. package/dist/components/Field/index.js.map +1 -0
  15. package/dist/components/LayoutSuite/DashboardSuite.d.ts +35 -0
  16. package/dist/components/LayoutSuite/DashboardSuite.js +61 -0
  17. package/dist/components/LayoutSuite/DashboardSuite.js.map +1 -0
  18. package/dist/components/LayoutSuite/index.d.ts +56 -0
  19. package/dist/components/LayoutSuite/index.js +101 -0
  20. package/dist/components/LayoutSuite/index.js.map +1 -0
  21. package/dist/components/Modal/focusScope.d.ts +1 -0
  22. package/dist/components/Modal/focusScope.js +81 -0
  23. package/dist/components/Modal/focusScope.js.map +1 -0
  24. package/dist/components/Modal/index.d.ts +4 -1
  25. package/dist/components/Modal/index.js +48 -43
  26. package/dist/components/Modal/index.js.map +1 -1
  27. package/dist/components/Page/Float.d.ts +14 -0
  28. package/dist/components/Page/Float2.js +154 -0
  29. package/dist/components/Page/Float2.js.map +1 -0
  30. package/dist/components/Page/index.d.ts +34 -7
  31. package/dist/components/Page/index.js +117 -36
  32. package/dist/components/Page/index.js.map +1 -1
  33. package/dist/components/Page/resize.d.ts +28 -0
  34. package/dist/components/Page/resize2.js +95 -0
  35. package/dist/components/Page/resize2.js.map +1 -0
  36. package/dist/components/PageHeader/index.d.ts +10 -3
  37. package/dist/components/PageHeader/index.js +46 -25
  38. package/dist/components/PageHeader/index.js.map +1 -1
  39. package/dist/components/PagePanel/index.d.ts +29 -0
  40. package/dist/components/PagePanel/index.js +99 -0
  41. package/dist/components/PagePanel/index.js.map +1 -0
  42. package/dist/components/Resizable/ResizeGrip.d.ts +5 -0
  43. package/dist/components/Resizable/ResizeGrip.js +14 -0
  44. package/dist/components/Resizable/ResizeGrip.js.map +1 -0
  45. package/dist/components/Resizable/index.js +57 -59
  46. package/dist/components/Resizable/index.js.map +1 -1
  47. package/dist/components/Resizable/useResize.d.ts +6 -0
  48. package/dist/components/Resizable/useResize.js +29 -28
  49. package/dist/components/Resizable/useResize.js.map +1 -1
  50. package/dist/components/SidebarToggleButton/index.d.ts +5 -3
  51. package/dist/components/SidebarToggleButton/index.js +24 -21
  52. package/dist/components/SidebarToggleButton/index.js.map +1 -1
  53. package/dist/components/Table/index.d.ts +10 -12
  54. package/dist/components/Table/index.js +25 -20
  55. package/dist/components/Table/index.js.map +1 -1
  56. package/dist/components/Text/index.d.ts +2 -2
  57. package/dist/components/Text/index.js +13 -12
  58. package/dist/components/Text/index.js.map +1 -1
  59. package/dist/components/Textarea/index.d.ts +14 -0
  60. package/dist/components/Textarea/index.js +44 -0
  61. package/dist/components/Textarea/index.js.map +1 -0
  62. package/dist/components/Tree/Tree.d.ts +2 -0
  63. package/dist/components/Tree/Tree.js +49 -40
  64. package/dist/components/Tree/Tree.js.map +1 -1
  65. package/dist/dishui.css +1 -1
  66. package/dist/index-nostyled.d.ts +9 -0
  67. package/dist/index.d.ts +11 -2
  68. package/dist/index.js +119 -113
  69. package/dist/layouts/Sidebar.d.ts +14 -1
  70. package/dist/layouts/Sidebar.js +170 -137
  71. package/dist/layouts/Sidebar.js.map +1 -1
  72. package/dist/lite.d.ts +10 -2
  73. package/dist/lite.js +17 -12
  74. package/dist/utils/i18n.js +1 -1
  75. package/dist/utils/i18n.js.map +1 -1
  76. package/dist/utils/menu.js +14 -16
  77. package/dist/utils/menu.js.map +1 -1
  78. package/package.json +3 -1
@@ -6,6 +6,7 @@ export type ButtonState = {
6
6
  icon?: React.ReactNode;
7
7
  };
8
8
  export type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
9
+ ref?: React.Ref<HTMLButtonElement>;
9
10
  children?: React.ReactNode;
10
11
  variant?: 'neutral' | 'primary' | 'secondary' | 'accent' | 'info' | 'success' | 'warning' | 'error' | 'destructive' | 'ghost' | 'link';
11
12
  size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/Button/index.tsx"],"sourcesContent":["import React, { ReactElement, useState, useCallback } from 'react';\nimport { cn } from '../../utils/cn';\n\n// 状态项定义\nexport type ButtonState = {\n label: string; // 显示的字符/文本\n key: string; // 状态的唯一标识\n variant?: 'neutral' | 'primary' | 'secondary' | 'accent' | 'info' | 'success' | 'warning' | 'error' | 'destructive' | 'ghost' | 'link';\n icon?: React.ReactNode; // 可选的图标\n};\n\nexport type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {\n children?: React.ReactNode;\n variant?: 'neutral' | 'primary' | 'secondary' | 'accent' | 'info' | 'success' | 'warning' | 'error' | 'destructive' | 'ghost' | 'link';\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';\n outline?: boolean;\n dash?: boolean;\n soft?: boolean;\n active?: boolean;\n disabled?: boolean;\n wide?: boolean;\n block?: boolean;\n square?: boolean;\n circle?: boolean;\n leftSection?: React.ReactNode;\n rightSection?: React.ReactNode;\n onClick?: React.MouseEventHandler<HTMLButtonElement>;\n /** 原生 button type;不传时保持浏览器默认行为(表单内默认 submit) */\n type?: 'button' | 'submit' | 'reset';\n className?: string;\n\n // 状态切换相关\n states?: ButtonState[]; // 状态列表\n initialStateIndex?: number; // 初始状态索引\n onStateChange?: (state: ButtonState, clickCount: number, stateIndex: number) => void; // 状态改变回调\n};\n\nconst Button = ({\n children,\n variant = 'primary',\n size = 'sm',\n outline = false,\n dash = false,\n soft = false,\n active = false,\n disabled = false,\n wide = false,\n block = false,\n square = false,\n circle = false,\n leftSection,\n rightSection,\n onClick,\n type,\n className = '',\n states,\n initialStateIndex = 0,\n onStateChange,\n title,\n ...rest\n}: ButtonProps) => {\n // 状态管理\n const [currentStateIndex, setCurrentStateIndex] = useState(initialStateIndex);\n const [clickCount, setClickCount] = useState(0);\n\n // 是否是状态按钮\n const isStateful = states && states.length > 0;\n\n // 获取当前状态\n const currentState = isStateful ? states[currentStateIndex] : null;\n\n // 处理点击\n const handleClick = useCallback((event: React.MouseEvent<HTMLButtonElement>) => {\n if (isStateful) {\n const newClickCount = clickCount + 1;\n const newStateIndex = (currentStateIndex + 1) % states.length;\n const newState = states[newStateIndex];\n\n setClickCount(newClickCount);\n setCurrentStateIndex(newStateIndex);\n\n // 调用状态改变回调\n if (onStateChange) {\n onStateChange(newState, newClickCount, newStateIndex);\n }\n }\n\n // 调用原始 onClick\n if (onClick) {\n onClick(event);\n }\n }, [isStateful, states, currentStateIndex, clickCount, onStateChange, onClick]);\n\n // 确定使用的 variant\n const effectiveVariant = currentState?.variant || variant;\n\n // 确定显示的内容\n const displayContent = isStateful ? (\n <>\n {currentState?.icon && <span className=\"mr-1\">{currentState.icon}</span>}\n {currentState?.label}\n </>\n ) : children;\n\n const baseClass = 'btn';\n const variantClass = effectiveVariant === 'destructive' ? 'btn-error btn-destructive' : effectiveVariant ? `btn-${effectiveVariant}` : '';\n const sizeClass = size ? `btn-${size}` : '';\n const outlineClass = outline ? 'btn-outline' : '';\n const dashClass = dash ? 'btn-dash' : '';\n const softClass = soft ? 'btn-soft' : '';\n const activeClass = active || isStateful ? 'btn-active' : '';\n const wideClass = wide ? 'btn-wide' : '';\n const blockClass = block ? 'btn-block' : '';\n const squareClass = square ? 'btn-square' : '';\n const circleClass = circle ? 'btn-circle' : '';\n\n const buttonClasses = cn(\n baseClass,\n variantClass,\n sizeClass,\n outlineClass,\n dashClass,\n softClass,\n activeClass,\n wideClass,\n blockClass,\n squareClass,\n circleClass,\n className\n );\n\n return (\n <button\n {...rest}\n type={type}\n className={buttonClasses}\n disabled={disabled}\n onClick={handleClick}\n title={title ?? (isStateful ? `当前状态: ${currentState?.key} (点击次数: ${clickCount})` : undefined)}\n >\n {!isStateful && leftSection && <span className=\"mr-2\">{leftSection}</span>}\n {displayContent}\n {!isStateful && rightSection && <span className=\"ml-2\">{rightSection}</span>}\n </button>\n );\n};\n\n// Button.Group 组件\nexport type ButtonGroupProps = {\n children: React.ReactNode;\n vertical?: boolean;\n className?: string;\n rounded?: 'left' | 'right' | 'both' | 'none' | 'top' | 'bottom';\n radius?: 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full';\n roundedStyle?: {\n left?: string;\n right?: string;\n top?: string;\n bottom?: string;\n };\n};\n\nconst ButtonGroup = ({\n children,\n vertical = false,\n className = '',\n rounded = 'none',\n radius = 'full',\n roundedStyle,\n}: ButtonGroupProps) => {\n const joinClasses = cn(\n 'join',\n vertical ? 'join-vertical' : 'join-horizontal',\n className\n );\n\n // 当rounded不为'none'时,强制使用'full'作为radius值\n const effectiveRadius = rounded !== 'none' ? 'full' : radius;\n\n return (\n <div className={joinClasses}>\n {React.Children.map(children, (child, index) => {\n // 只处理有效的React元素\n if (!React.isValidElement(child)) return child;\n\n const childElement = child as ReactElement<{ className?: string }>;\n const childClassName = childElement.props.className || '';\n const isFirstChild = index === 0;\n const isLastChild = index === React.Children.count(children) - 1;\n\n // 根据rounded属性添加额外的圆角类\n let roundedClasses = '';\n\n if (roundedStyle) {\n // 使用自定义圆角样式\n if (vertical) {\n if ((rounded === 'left' || rounded === 'top') && isFirstChild && roundedStyle.top) {\n roundedClasses = roundedStyle.top;\n } else if ((rounded === 'right' || rounded === 'bottom') && isLastChild && roundedStyle.bottom) {\n roundedClasses = roundedStyle.bottom;\n } else if (rounded === 'both') {\n if (isFirstChild && roundedStyle.top) roundedClasses = roundedStyle.top;\n if (isLastChild && roundedStyle.bottom) roundedClasses = roundedStyle.bottom;\n }\n } else {\n if ((rounded === 'left' || rounded === 'top') && isFirstChild && roundedStyle.left) {\n roundedClasses = roundedStyle.left;\n } else if ((rounded === 'right' || rounded === 'bottom') && isLastChild && roundedStyle.right) {\n roundedClasses = roundedStyle.right;\n } else if (rounded === 'both') {\n if (isFirstChild && roundedStyle.left) roundedClasses = roundedStyle.left;\n if (isLastChild && roundedStyle.right) roundedClasses = roundedStyle.right;\n }\n }\n } else {\n // 使用默认的圆角大小\n if (vertical) {\n // 垂直布局时,圆角应用在上下方\n if (rounded === 'left' || rounded === 'top') {\n if (isFirstChild) roundedClasses = `rounded-t-${effectiveRadius}`;\n } else if (rounded === 'right' || rounded === 'bottom') {\n if (isLastChild) roundedClasses = `rounded-b-${effectiveRadius}`;\n } else if (rounded === 'both') {\n if (isFirstChild) roundedClasses = `rounded-t-${effectiveRadius}`;\n if (isLastChild) roundedClasses = `rounded-b-${effectiveRadius}`;\n }\n } else {\n // 水平布局时,圆角应用在左右方\n if (rounded === 'left' || rounded === 'top') {\n if (isFirstChild) roundedClasses = `rounded-l-${effectiveRadius}`;\n } else if (rounded === 'right' || rounded === 'bottom') {\n if (isLastChild) roundedClasses = `rounded-r-${effectiveRadius}`;\n } else if (rounded === 'both') {\n if (isFirstChild) roundedClasses = `rounded-l-${effectiveRadius}`;\n if (isLastChild) roundedClasses = `rounded-r-${effectiveRadius}`;\n }\n }\n }\n\n const newClassName = `${childClassName} join-item ${roundedClasses}`.trim();\n\n return React.cloneElement(childElement, {\n className: newClassName,\n });\n })}\n </div>\n );\n};\n\n// Button.GroupSection 组件\nexport type ButtonGroupSectionProps = {\n children: React.ReactNode;\n className?: string;\n};\n\nconst ButtonGroupSection = ({\n children,\n className = '',\n}: ButtonGroupSectionProps) => {\n const sectionClasses = cn(\n 'join-item',\n 'flex',\n 'items-center',\n 'px-4',\n 'bg-base-200',\n 'border',\n 'border-base-300',\n className\n );\n\n return (\n <div className={sectionClasses}>\n {children}\n </div>\n );\n};\n\n// 扩展Button组件,添加Group和GroupSection子组件\ninterface ButtonCompound extends React.FC<ButtonProps> {\n Group: React.FC<ButtonGroupProps>;\n GroupSection: React.FC<ButtonGroupSectionProps>;\n}\n\nconst ButtonWithCompounds = Button as ButtonCompound;\nButtonWithCompounds.Group = ButtonGroup;\nButtonWithCompounds.GroupSection = ButtonGroupSection;\n\nexport default ButtonWithCompounds; "],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/Button/index.tsx"],"sourcesContent":["import React, { ReactElement, useState, useCallback } from 'react';\nimport { cn } from '../../utils/cn';\n\n// 状态项定义\nexport type ButtonState = {\n label: string; // 显示的字符/文本\n key: string; // 状态的唯一标识\n variant?: 'neutral' | 'primary' | 'secondary' | 'accent' | 'info' | 'success' | 'warning' | 'error' | 'destructive' | 'ghost' | 'link';\n icon?: React.ReactNode; // 可选的图标\n};\n\nexport type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {\n ref?: React.Ref<HTMLButtonElement>;\n children?: React.ReactNode;\n variant?: 'neutral' | 'primary' | 'secondary' | 'accent' | 'info' | 'success' | 'warning' | 'error' | 'destructive' | 'ghost' | 'link';\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';\n outline?: boolean;\n dash?: boolean;\n soft?: boolean;\n active?: boolean;\n disabled?: boolean;\n wide?: boolean;\n block?: boolean;\n square?: boolean;\n circle?: boolean;\n leftSection?: React.ReactNode;\n rightSection?: React.ReactNode;\n onClick?: React.MouseEventHandler<HTMLButtonElement>;\n /** 原生 button type;不传时保持浏览器默认行为(表单内默认 submit) */\n type?: 'button' | 'submit' | 'reset';\n className?: string;\n\n // 状态切换相关\n states?: ButtonState[]; // 状态列表\n initialStateIndex?: number; // 初始状态索引\n onStateChange?: (state: ButtonState, clickCount: number, stateIndex: number) => void; // 状态改变回调\n};\n\nconst Button = ({\n children,\n variant = 'primary',\n size = 'sm',\n outline = false,\n dash = false,\n soft = false,\n active = false,\n disabled = false,\n wide = false,\n block = false,\n square = false,\n circle = false,\n leftSection,\n rightSection,\n onClick,\n type,\n className = '',\n states,\n initialStateIndex = 0,\n onStateChange,\n title,\n ...rest\n}: ButtonProps) => {\n // 状态管理\n const [currentStateIndex, setCurrentStateIndex] = useState(initialStateIndex);\n const [clickCount, setClickCount] = useState(0);\n\n // 是否是状态按钮\n const isStateful = states && states.length > 0;\n\n // 获取当前状态\n const currentState = isStateful ? states[currentStateIndex] : null;\n\n // 处理点击\n const handleClick = useCallback((event: React.MouseEvent<HTMLButtonElement>) => {\n if (isStateful) {\n const newClickCount = clickCount + 1;\n const newStateIndex = (currentStateIndex + 1) % states.length;\n const newState = states[newStateIndex];\n\n setClickCount(newClickCount);\n setCurrentStateIndex(newStateIndex);\n\n // 调用状态改变回调\n if (onStateChange) {\n onStateChange(newState, newClickCount, newStateIndex);\n }\n }\n\n // 调用原始 onClick\n if (onClick) {\n onClick(event);\n }\n }, [isStateful, states, currentStateIndex, clickCount, onStateChange, onClick]);\n\n // 确定使用的 variant\n const effectiveVariant = currentState?.variant || variant;\n\n // 确定显示的内容\n const displayContent = isStateful ? (\n <>\n {currentState?.icon && <span className=\"mr-1\">{currentState.icon}</span>}\n {currentState?.label}\n </>\n ) : children;\n\n const baseClass = 'btn';\n const variantClass = effectiveVariant === 'destructive' ? 'btn-error btn-destructive' : effectiveVariant ? `btn-${effectiveVariant}` : '';\n const sizeClass = size ? `btn-${size}` : '';\n const outlineClass = outline ? 'btn-outline' : '';\n const dashClass = dash ? 'btn-dash' : '';\n const softClass = soft ? 'btn-soft' : '';\n const activeClass = active || isStateful ? 'btn-active' : '';\n const wideClass = wide ? 'btn-wide' : '';\n const blockClass = block ? 'btn-block' : '';\n const squareClass = square ? 'btn-square' : '';\n const circleClass = circle ? 'btn-circle' : '';\n\n const buttonClasses = cn(\n baseClass,\n variantClass,\n sizeClass,\n outlineClass,\n dashClass,\n softClass,\n activeClass,\n wideClass,\n blockClass,\n squareClass,\n circleClass,\n className\n );\n\n return (\n <button\n {...rest}\n type={type}\n className={buttonClasses}\n disabled={disabled}\n onClick={handleClick}\n title={title ?? (isStateful ? `当前状态: ${currentState?.key} (点击次数: ${clickCount})` : undefined)}\n >\n {!isStateful && leftSection && <span className=\"mr-2\">{leftSection}</span>}\n {displayContent}\n {!isStateful && rightSection && <span className=\"ml-2\">{rightSection}</span>}\n </button>\n );\n};\n\n// Button.Group 组件\nexport type ButtonGroupProps = {\n children: React.ReactNode;\n vertical?: boolean;\n className?: string;\n rounded?: 'left' | 'right' | 'both' | 'none' | 'top' | 'bottom';\n radius?: 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full';\n roundedStyle?: {\n left?: string;\n right?: string;\n top?: string;\n bottom?: string;\n };\n};\n\nconst ButtonGroup = ({\n children,\n vertical = false,\n className = '',\n rounded = 'none',\n radius = 'full',\n roundedStyle,\n}: ButtonGroupProps) => {\n const joinClasses = cn(\n 'join',\n vertical ? 'join-vertical' : 'join-horizontal',\n className\n );\n\n // 当rounded不为'none'时,强制使用'full'作为radius值\n const effectiveRadius = rounded !== 'none' ? 'full' : radius;\n\n return (\n <div className={joinClasses}>\n {React.Children.map(children, (child, index) => {\n // 只处理有效的React元素\n if (!React.isValidElement(child)) return child;\n\n const childElement = child as ReactElement<{ className?: string }>;\n const childClassName = childElement.props.className || '';\n const isFirstChild = index === 0;\n const isLastChild = index === React.Children.count(children) - 1;\n\n // 根据rounded属性添加额外的圆角类\n let roundedClasses = '';\n\n if (roundedStyle) {\n // 使用自定义圆角样式\n if (vertical) {\n if ((rounded === 'left' || rounded === 'top') && isFirstChild && roundedStyle.top) {\n roundedClasses = roundedStyle.top;\n } else if ((rounded === 'right' || rounded === 'bottom') && isLastChild && roundedStyle.bottom) {\n roundedClasses = roundedStyle.bottom;\n } else if (rounded === 'both') {\n if (isFirstChild && roundedStyle.top) roundedClasses = roundedStyle.top;\n if (isLastChild && roundedStyle.bottom) roundedClasses = roundedStyle.bottom;\n }\n } else {\n if ((rounded === 'left' || rounded === 'top') && isFirstChild && roundedStyle.left) {\n roundedClasses = roundedStyle.left;\n } else if ((rounded === 'right' || rounded === 'bottom') && isLastChild && roundedStyle.right) {\n roundedClasses = roundedStyle.right;\n } else if (rounded === 'both') {\n if (isFirstChild && roundedStyle.left) roundedClasses = roundedStyle.left;\n if (isLastChild && roundedStyle.right) roundedClasses = roundedStyle.right;\n }\n }\n } else {\n // 使用默认的圆角大小\n if (vertical) {\n // 垂直布局时,圆角应用在上下方\n if (rounded === 'left' || rounded === 'top') {\n if (isFirstChild) roundedClasses = `rounded-t-${effectiveRadius}`;\n } else if (rounded === 'right' || rounded === 'bottom') {\n if (isLastChild) roundedClasses = `rounded-b-${effectiveRadius}`;\n } else if (rounded === 'both') {\n if (isFirstChild) roundedClasses = `rounded-t-${effectiveRadius}`;\n if (isLastChild) roundedClasses = `rounded-b-${effectiveRadius}`;\n }\n } else {\n // 水平布局时,圆角应用在左右方\n if (rounded === 'left' || rounded === 'top') {\n if (isFirstChild) roundedClasses = `rounded-l-${effectiveRadius}`;\n } else if (rounded === 'right' || rounded === 'bottom') {\n if (isLastChild) roundedClasses = `rounded-r-${effectiveRadius}`;\n } else if (rounded === 'both') {\n if (isFirstChild) roundedClasses = `rounded-l-${effectiveRadius}`;\n if (isLastChild) roundedClasses = `rounded-r-${effectiveRadius}`;\n }\n }\n }\n\n const newClassName = `${childClassName} join-item ${roundedClasses}`.trim();\n\n return React.cloneElement(childElement, {\n className: newClassName,\n });\n })}\n </div>\n );\n};\n\n// Button.GroupSection 组件\nexport type ButtonGroupSectionProps = {\n children: React.ReactNode;\n className?: string;\n};\n\nconst ButtonGroupSection = ({\n children,\n className = '',\n}: ButtonGroupSectionProps) => {\n const sectionClasses = cn(\n 'join-item',\n 'flex',\n 'items-center',\n 'px-4',\n 'bg-base-200',\n 'border',\n 'border-base-300',\n className\n );\n\n return (\n <div className={sectionClasses}>\n {children}\n </div>\n );\n};\n\n// 扩展Button组件,添加Group和GroupSection子组件\ninterface ButtonCompound extends React.FC<ButtonProps> {\n Group: React.FC<ButtonGroupProps>;\n GroupSection: React.FC<ButtonGroupSectionProps>;\n}\n\nconst ButtonWithCompounds = Button as ButtonCompound;\nButtonWithCompounds.Group = ButtonGroup;\nButtonWithCompounds.GroupSection = ButtonGroupSection;\n\nexport default ButtonWithCompounds; "],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import React from 'react';
2
+ import { type ButtonProps } from '../Button';
3
+ export type ButtonMenuItem = {
4
+ id: string;
5
+ label: string;
6
+ icon?: React.ReactNode;
7
+ disabled?: boolean;
8
+ onSelect?: () => void;
9
+ };
10
+ export type ButtonMenuProps = {
11
+ label: string;
12
+ icon?: React.ReactNode;
13
+ iconOnly?: boolean;
14
+ items: ButtonMenuItem[];
15
+ selectedId?: string;
16
+ onSelect?: (id: string) => void;
17
+ align?: 'start' | 'end';
18
+ disabled?: boolean;
19
+ variant?: ButtonProps['variant'];
20
+ size?: ButtonProps['size'];
21
+ className?: string;
22
+ 'aria-label'?: string;
23
+ open?: boolean;
24
+ defaultOpen?: boolean;
25
+ onOpenChange?: (open: boolean) => void;
26
+ };
27
+ /** A button-triggered action menu, or a single-choice menu when selectedId is supplied. */
28
+ export default function ButtonMenu({ label, icon, iconOnly, items, selectedId, onSelect, align, disabled, variant, size, className, 'aria-label': ariaLabel, open, defaultOpen, onOpenChange, }: ButtonMenuProps): React.JSX.Element;
@@ -0,0 +1,197 @@
1
+ import e from "../Button/index.js";
2
+ import t from "../Text/index.js";
3
+ /* empty css */
4
+ import { useCallback as n, useId as r, useLayoutEffect as i, useRef as a, useState as o } from "react";
5
+ import { Fragment as s, jsx as c, jsxs as l } from "react/jsx-runtime";
6
+ import { createPortal as u } from "react-dom";
7
+ //#region src/components/ButtonMenu/index.tsx
8
+ var d = () => /* @__PURE__ */ c("svg", {
9
+ viewBox: "0 0 16 16",
10
+ fill: "none",
11
+ "aria-hidden": "true",
12
+ children: /* @__PURE__ */ c("path", {
13
+ d: "m4 6 4 4 4-4",
14
+ stroke: "currentColor",
15
+ strokeWidth: "1.5",
16
+ strokeLinecap: "round",
17
+ strokeLinejoin: "round"
18
+ })
19
+ }), f = () => /* @__PURE__ */ c("svg", {
20
+ viewBox: "0 0 16 16",
21
+ fill: "none",
22
+ "aria-hidden": "true",
23
+ children: /* @__PURE__ */ c("path", {
24
+ d: "m3 8 3 3 7-7",
25
+ stroke: "currentColor",
26
+ strokeWidth: "1.5",
27
+ strokeLinecap: "round",
28
+ strokeLinejoin: "round"
29
+ })
30
+ });
31
+ function p({ label: p, icon: m, iconOnly: h = !1, items: g, selectedId: _, onSelect: v, align: y = "start", disabled: b = !1, variant: x = "ghost", size: S = "sm", className: C = "", "aria-label": w, open: T, defaultOpen: E = !1, onOpenChange: D }) {
32
+ let O = r(), k = a(null), A = a(null), j = a("selected"), [M, N] = o(E), P = b || g.length === 0, F = (T ?? M) && !P, [I, L] = o(), R = a(!1), z = n((e) => {
33
+ T === void 0 && N(e), e !== (T ?? M) && D?.(e);
34
+ }, [
35
+ T,
36
+ M,
37
+ D
38
+ ]), B = () => k.current?.focus({ preventScroll: !0 }), V = () => {
39
+ z(!1), B();
40
+ }, H = () => Array.from(A.current?.querySelectorAll("button:not(:disabled)") ?? []), U = (e) => {
41
+ if (!e) {
42
+ A.current?.focus({ preventScroll: !0 });
43
+ return;
44
+ }
45
+ e.focus({ preventScroll: !0 }), e.scrollIntoView({
46
+ block: "nearest",
47
+ inline: "nearest"
48
+ });
49
+ };
50
+ i(() => {
51
+ if (!F) return;
52
+ let e = k.current, t = A.current, n = () => {
53
+ let n = e.closest("[data-theme]")?.getAttribute("data-theme");
54
+ n == null ? t.removeAttribute("data-theme") : t.setAttribute("data-theme", n);
55
+ let r = getComputedStyle(e).direction === "rtl";
56
+ t.dir = r ? "rtl" : "ltr";
57
+ let i = window.visualViewport, a = (i?.offsetLeft ?? 0) + 8, o = (i?.offsetTop ?? 0) + 8, s = a + (i?.width ?? document.documentElement.clientWidth) - 16, c = o + (i?.height ?? window.innerHeight) - 16;
58
+ t.style.maxWidth = `${Math.max(0, Math.min(320, s - a))}px`;
59
+ let l = e.getBoundingClientRect(), u = Math.max(0, c - l.bottom - 4), d = Math.max(0, l.top - o - 4), f = u < Math.min(t.scrollHeight + 2, 352) && d > u;
60
+ t.style.maxHeight = `${Math.min(352, f ? d : u)}px`;
61
+ let p = t.getBoundingClientRect(), m = y === "end" === r ? l.left : l.right - p.width, h = f ? l.top - p.height - 4 : l.bottom + 4;
62
+ t.style.left = `${Math.max(a, Math.min(m, s - p.width))}px`, t.style.top = `${Math.max(o, Math.min(h, c - p.height))}px`;
63
+ };
64
+ n();
65
+ let r = new ResizeObserver(n);
66
+ r.observe(e), r.observe(t);
67
+ let i = new MutationObserver(n);
68
+ for (let t = e; t; t = t.parentElement) i.observe(t, {
69
+ attributes: !0,
70
+ attributeFilter: ["data-theme", "dir"]
71
+ });
72
+ return window.addEventListener("resize", n), window.addEventListener("scroll", n, !0), window.visualViewport?.addEventListener("resize", n), window.visualViewport?.addEventListener("scroll", n), () => {
73
+ r.disconnect(), i.disconnect(), window.removeEventListener("resize", n), window.removeEventListener("scroll", n, !0), window.visualViewport?.removeEventListener("resize", n), window.visualViewport?.removeEventListener("scroll", n);
74
+ };
75
+ }, [
76
+ F,
77
+ y,
78
+ g
79
+ ]), i(() => {
80
+ if (F && !R.current) {
81
+ let e = H(), t = j.current === "last" ? e.at(-1) : j.current === "first" ? e[0] : e.find((e) => e.dataset.itemId === _) ?? e[0];
82
+ U(t), j.current = "selected";
83
+ } else if (!F && R.current && document.activeElement === document.body) B();
84
+ else if (F && (document.activeElement === document.body || A.current?.contains(document.activeElement))) {
85
+ let e = document.activeElement;
86
+ (e === A.current || e?.matches(":disabled") || !A.current?.contains(e)) && U(H()[0]);
87
+ }
88
+ R.current = F;
89
+ }), i(() => {
90
+ if (!F) return;
91
+ let e = (e) => {
92
+ let t = e.target;
93
+ t instanceof Node && !k.current?.contains(t) && !A.current?.contains(t) && z(!1);
94
+ };
95
+ return document.addEventListener("pointerdown", e, !0), document.addEventListener("focusin", e), () => {
96
+ document.removeEventListener("pointerdown", e, !0), document.removeEventListener("focusin", e);
97
+ };
98
+ }, [F, z]);
99
+ let W = (e) => {
100
+ if (e.key === "Escape") e.preventDefault(), e.stopPropagation(), V();
101
+ else if (e.key === "Tab") B(), z(!1);
102
+ else if ([
103
+ "ArrowDown",
104
+ "ArrowUp",
105
+ "Home",
106
+ "End"
107
+ ].includes(e.key)) {
108
+ e.preventDefault();
109
+ let t = H(), n = t.indexOf(document.activeElement), r = e.key === "Home" ? 0 : e.key === "End" ? t.length - 1 : (n + (e.key === "ArrowDown" ? 1 : -1) + t.length) % t.length;
110
+ U(t[r]);
111
+ }
112
+ };
113
+ return /* @__PURE__ */ l(s, { children: [/* @__PURE__ */ l(e, {
114
+ ref: k,
115
+ id: `${O}-trigger`,
116
+ type: "button",
117
+ variant: x,
118
+ size: S,
119
+ square: h,
120
+ disabled: P,
121
+ className: `dishui-button-menu-trigger ${C}`,
122
+ "aria-label": w ?? p,
123
+ title: h ? p : void 0,
124
+ "aria-haspopup": "menu",
125
+ "aria-expanded": F,
126
+ "aria-controls": F ? `${O}-menu` : void 0,
127
+ onClick: () => {
128
+ j.current = "selected", z(!F);
129
+ },
130
+ onKeyDown: (e) => {
131
+ P || (e.key === "ArrowDown" || e.key === "ArrowUp" ? (e.preventDefault(), j.current = e.key === "ArrowUp" ? "last" : "first", F ? U(e.key === "ArrowUp" ? H().at(-1) : H()[0]) : z(!0)) : F && e.key === "Escape" && W(e));
132
+ },
133
+ children: [
134
+ m != null && /* @__PURE__ */ c("span", {
135
+ className: "dishui-button-menu-icon",
136
+ "aria-hidden": "true",
137
+ children: m
138
+ }),
139
+ !h && /* @__PURE__ */ c(t, {
140
+ size: "sm",
141
+ weight: "medium",
142
+ color: "inherit",
143
+ className: "dishui-button-menu-label",
144
+ children: p
145
+ }),
146
+ (!h || m == null) && /* @__PURE__ */ c("span", {
147
+ className: "dishui-button-menu-icon",
148
+ "aria-hidden": "true",
149
+ children: /* @__PURE__ */ c(d, {})
150
+ })
151
+ ]
152
+ }), F && typeof document < "u" && u(/* @__PURE__ */ c("div", {
153
+ ref: A,
154
+ id: `${O}-menu`,
155
+ role: "menu",
156
+ "aria-labelledby": `${O}-trigger`,
157
+ tabIndex: -1,
158
+ className: "dishui-button-menu-surface",
159
+ onKeyDown: W,
160
+ children: g.map((e) => /* @__PURE__ */ l("button", {
161
+ type: "button",
162
+ "data-item-id": e.id,
163
+ role: _ === void 0 ? "menuitem" : "menuitemradio",
164
+ "aria-checked": _ === void 0 ? void 0 : _ === e.id,
165
+ disabled: e.disabled,
166
+ "aria-disabled": e.disabled || void 0,
167
+ tabIndex: !e.disabled && I === e.id ? 0 : -1,
168
+ className: "dishui-button-menu-item",
169
+ onFocus: () => L(e.id),
170
+ onClick: () => {
171
+ e.disabled || (V(), e.onSelect?.(), v?.(e.id));
172
+ },
173
+ children: [
174
+ e.icon != null && /* @__PURE__ */ c("span", {
175
+ className: "dishui-button-menu-icon",
176
+ "aria-hidden": "true",
177
+ children: e.icon
178
+ }),
179
+ /* @__PURE__ */ c(t, {
180
+ size: "sm",
181
+ color: "inherit",
182
+ className: "dishui-button-menu-label",
183
+ children: e.label
184
+ }),
185
+ _ !== void 0 && /* @__PURE__ */ c("span", {
186
+ className: "dishui-button-menu-check",
187
+ "aria-hidden": "true",
188
+ children: _ === e.id && /* @__PURE__ */ c(f, {})
189
+ })
190
+ ]
191
+ }, e.id))
192
+ }), document.body)] });
193
+ }
194
+ //#endregion
195
+ export { p as default };
196
+
197
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/ButtonMenu/index.tsx"],"sourcesContent":["import React, { useCallback, useId, useLayoutEffect, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport Button, { type ButtonProps } from '../Button';\nimport Text from '../Text';\nimport './style.css';\n\nexport type ButtonMenuItem = {\n id: string;\n label: string;\n icon?: React.ReactNode;\n disabled?: boolean;\n onSelect?: () => void;\n};\n\nexport type ButtonMenuProps = {\n label: string;\n icon?: React.ReactNode;\n iconOnly?: boolean;\n items: ButtonMenuItem[];\n selectedId?: string;\n onSelect?: (id: string) => void;\n align?: 'start' | 'end';\n disabled?: boolean;\n variant?: ButtonProps['variant'];\n size?: ButtonProps['size'];\n className?: string;\n 'aria-label'?: string;\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n};\n\nconst Chevron = () => <svg viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\"><path d=\"m4 6 4 4 4-4\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" /></svg>;\nconst Check = () => <svg viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\"><path d=\"m3 8 3 3 7-7\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" /></svg>;\n\n/** A button-triggered action menu, or a single-choice menu when selectedId is supplied. */\nexport default function ButtonMenu({\n label, icon, iconOnly = false, items, selectedId, onSelect, align = 'start',\n disabled = false, variant = 'ghost', size = 'sm', className = '',\n 'aria-label': ariaLabel, open, defaultOpen = false, onOpenChange,\n}: ButtonMenuProps) {\n const id = useId();\n const triggerRef = useRef<HTMLButtonElement>(null);\n const menuRef = useRef<HTMLDivElement>(null);\n const initialFocus = useRef<'first' | 'last' | 'selected'>('selected');\n const [internalOpen, setInternalOpen] = useState(defaultOpen);\n const unavailable = disabled || items.length === 0;\n const isOpen = (open ?? internalOpen) && !unavailable;\n const [activeId, setActiveId] = useState<string>();\n const wasOpen = useRef(false);\n const requestOpen = useCallback((next: boolean) => {\n if (open === undefined) setInternalOpen(next);\n if (next !== (open ?? internalOpen)) onOpenChange?.(next);\n }, [open, internalOpen, onOpenChange]);\n const focusTrigger = () => triggerRef.current?.focus({ preventScroll: true });\n const close = () => { requestOpen(false); focusTrigger(); };\n const enabledElements = () => Array.from(menuRef.current?.querySelectorAll<HTMLButtonElement>('button:not(:disabled)') ?? []);\n const focusItem = (element?: HTMLButtonElement) => {\n if (!element) { menuRef.current?.focus({ preventScroll: true }); return; }\n element.focus({ preventScroll: true });\n element.scrollIntoView({ block: 'nearest', inline: 'nearest' });\n };\n\n useLayoutEffect(() => {\n if (!isOpen) return;\n const trigger = triggerRef.current!;\n const menu = menuRef.current!;\n const position = () => {\n const theme = trigger.closest('[data-theme]')?.getAttribute('data-theme');\n if (theme !== null && theme !== undefined) menu.setAttribute('data-theme', theme);\n else menu.removeAttribute('data-theme');\n const rtl = getComputedStyle(trigger).direction === 'rtl';\n menu.dir = rtl ? 'rtl' : 'ltr';\n const viewport = window.visualViewport;\n const leftEdge = (viewport?.offsetLeft ?? 0) + 8;\n const topEdge = (viewport?.offsetTop ?? 0) + 8;\n const rightEdge = leftEdge + (viewport?.width ?? document.documentElement.clientWidth) - 16;\n const bottomEdge = topEdge + (viewport?.height ?? window.innerHeight) - 16;\n menu.style.maxWidth = `${Math.max(0, Math.min(320, rightEdge - leftEdge))}px`;\n const rect = trigger.getBoundingClientRect();\n const below = Math.max(0, bottomEdge - rect.bottom - 4);\n const above = Math.max(0, rect.top - topEdge - 4);\n const desiredHeight = Math.min(menu.scrollHeight + 2, 352);\n const upwards = below < desiredHeight && above > below;\n menu.style.maxHeight = `${Math.min(352, upwards ? above : below)}px`;\n const box = menu.getBoundingClientRect();\n const end = (align === 'end') !== rtl;\n const left = end ? rect.right - box.width : rect.left;\n const top = upwards ? rect.top - box.height - 4 : rect.bottom + 4;\n menu.style.left = `${Math.max(leftEdge, Math.min(left, rightEdge - box.width))}px`;\n menu.style.top = `${Math.max(topEdge, Math.min(top, bottomEdge - box.height))}px`;\n };\n position();\n const resize = new ResizeObserver(position);\n resize.observe(trigger);\n resize.observe(menu);\n const themeObserver = new MutationObserver(position);\n for (let ancestor: HTMLElement | null = trigger; ancestor; ancestor = ancestor.parentElement) {\n themeObserver.observe(ancestor, { attributes: true, attributeFilter: ['data-theme', 'dir'] });\n }\n window.addEventListener('resize', position);\n window.addEventListener('scroll', position, true);\n window.visualViewport?.addEventListener('resize', position);\n window.visualViewport?.addEventListener('scroll', position);\n return () => {\n resize.disconnect();\n themeObserver.disconnect();\n window.removeEventListener('resize', position);\n window.removeEventListener('scroll', position, true);\n window.visualViewport?.removeEventListener('resize', position);\n window.visualViewport?.removeEventListener('scroll', position);\n };\n }, [isOpen, align, items]);\n\n useLayoutEffect(() => {\n if (isOpen && !wasOpen.current) {\n const enabled = enabledElements();\n const target = initialFocus.current === 'last' ? enabled.at(-1)\n : initialFocus.current === 'first' ? enabled[0]\n : enabled.find(element => element.dataset.itemId === selectedId) ?? enabled[0];\n focusItem(target);\n initialFocus.current = 'selected';\n } else if (!isOpen && wasOpen.current && document.activeElement === document.body) {\n // External controlled closes should not strand focus on the removed portal.\n focusTrigger();\n } else if (isOpen && (document.activeElement === document.body || menuRef.current?.contains(document.activeElement))) {\n const focused = document.activeElement;\n if (focused === menuRef.current || focused?.matches(':disabled') || !menuRef.current?.contains(focused)) focusItem(enabledElements()[0]);\n }\n wasOpen.current = isOpen;\n });\n\n useLayoutEffect(() => {\n if (!isOpen) return;\n const dismiss = (event: Event) => {\n const target = event.target;\n if (target instanceof Node && !triggerRef.current?.contains(target) && !menuRef.current?.contains(target)) requestOpen(false);\n };\n document.addEventListener('pointerdown', dismiss, true);\n document.addEventListener('focusin', dismiss);\n return () => {\n document.removeEventListener('pointerdown', dismiss, true);\n document.removeEventListener('focusin', dismiss);\n };\n }, [isOpen, requestOpen]);\n\n const handleKeyDown = (event: React.KeyboardEvent) => {\n if (event.key === 'Escape') {\n event.preventDefault();\n event.stopPropagation();\n close();\n } else if (event.key === 'Tab') {\n // The portal is last in DOM order. Resume native traversal from its trigger,\n // without preventing Tab, so both directions reach the adjacent page control.\n focusTrigger();\n requestOpen(false);\n } else if (['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(event.key)) {\n event.preventDefault();\n const enabled = enabledElements();\n const index = enabled.indexOf(document.activeElement as HTMLButtonElement);\n const next = event.key === 'Home' ? 0 : event.key === 'End' ? enabled.length - 1\n : (index + (event.key === 'ArrowDown' ? 1 : -1) + enabled.length) % enabled.length;\n focusItem(enabled[next]);\n }\n };\n\n return <>\n <Button ref={triggerRef} id={`${id}-trigger`} type=\"button\" variant={variant} size={size}\n square={iconOnly} disabled={unavailable}\n className={`dishui-button-menu-trigger ${className}`}\n aria-label={ariaLabel ?? label} title={iconOnly ? label : undefined}\n aria-haspopup=\"menu\" aria-expanded={isOpen} aria-controls={isOpen ? `${id}-menu` : undefined}\n onClick={() => { initialFocus.current = 'selected'; requestOpen(!isOpen); }}\n onKeyDown={event => {\n if (unavailable) return;\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault();\n initialFocus.current = event.key === 'ArrowUp' ? 'last' : 'first';\n if (isOpen) focusItem(event.key === 'ArrowUp' ? enabledElements().at(-1) : enabledElements()[0]);\n else requestOpen(true);\n } else if (isOpen && event.key === 'Escape') handleKeyDown(event);\n }}>\n {icon != null && <span className=\"dishui-button-menu-icon\" aria-hidden=\"true\">{icon}</span>}\n {!iconOnly && <Text size=\"sm\" weight=\"medium\" color=\"inherit\" className=\"dishui-button-menu-label\">{label}</Text>}\n {(!iconOnly || icon == null) && <span className=\"dishui-button-menu-icon\" aria-hidden=\"true\"><Chevron /></span>}\n </Button>\n {isOpen && typeof document !== 'undefined' && createPortal(\n <div ref={menuRef} id={`${id}-menu`} role=\"menu\" aria-labelledby={`${id}-trigger`}\n tabIndex={-1} className=\"dishui-button-menu-surface\" onKeyDown={handleKeyDown}>\n {items.map(item => <button key={item.id} type=\"button\" data-item-id={item.id}\n role={selectedId === undefined ? 'menuitem' : 'menuitemradio'}\n aria-checked={selectedId === undefined ? undefined : selectedId === item.id}\n disabled={item.disabled} aria-disabled={item.disabled || undefined}\n tabIndex={!item.disabled && activeId === item.id ? 0 : -1}\n className=\"dishui-button-menu-item\" onFocus={() => setActiveId(item.id)}\n onClick={() => {\n if (item.disabled) return;\n close();\n item.onSelect?.();\n onSelect?.(item.id);\n }}>\n {item.icon != null && <span className=\"dishui-button-menu-icon\" aria-hidden=\"true\">{item.icon}</span>}\n <Text size=\"sm\" color=\"inherit\" className=\"dishui-button-menu-label\">{item.label}</Text>\n {selectedId !== undefined && <span className=\"dishui-button-menu-check\" aria-hidden=\"true\">{selectedId === item.id && <Check />}</span>}\n </button>)}\n </div>, document.body)}\n </>;\n}\n"],"mappings":";;;;;;;AAgCA,IAAM,UAAgB,kBAAC,OAAD;CAAK,SAAQ;CAAY,MAAK;CAAO,eAAY;CAAO,UAAA,kBAAC,QAAD;EAAM,GAAE;EAAe,QAAO;EAAe,aAAY;EAAM,eAAc;EAAQ,gBAAe;CAAS,CAAA;AAAM,CAAA,GAC3L,UAAc,kBAAC,OAAD;CAAK,SAAQ;CAAY,MAAK;CAAO,eAAY;CAAO,UAAA,kBAAC,QAAD;EAAM,GAAE;EAAe,QAAO;EAAe,aAAY;EAAM,eAAc;EAAQ,gBAAe;CAAS,CAAA;AAAM,CAAA;AAG/L,SAAwB,EAAW,EACjC,UAAO,SAAM,cAAW,IAAO,UAAO,eAAY,aAAU,WAAQ,SACpE,cAAW,IAAO,aAAU,SAAS,UAAO,MAAM,eAAY,IAC9D,cAAc,GAAW,SAAM,iBAAc,IAAO,mBAClC;CAClB,IAAM,IAAK,EAAM,GACX,IAAa,EAA0B,IAAI,GAC3C,IAAU,EAAuB,IAAI,GACrC,IAAe,EAAsC,UAAU,GAC/D,CAAC,GAAc,KAAmB,EAAS,CAAW,GACtD,IAAc,KAAY,EAAM,WAAW,GAC3C,KAAU,KAAQ,MAAiB,CAAC,GACpC,CAAC,GAAU,KAAe,EAAiB,GAC3C,IAAU,EAAO,EAAK,GACtB,IAAc,GAAa,MAAkB;EAEjD,AADI,MAAS,KAAA,KAAW,EAAgB,CAAI,GACxC,OAAU,KAAQ,MAAe,IAAe,CAAI;CAC1D,GAAG;EAAC;EAAM;EAAc;CAAY,CAAC,GAC/B,UAAqB,EAAW,SAAS,MAAM,EAAE,eAAe,GAAK,CAAC,GACtE,UAAc;EAAsB,AAApB,EAAY,EAAK,GAAG,EAAa;CAAG,GACpD,UAAwB,MAAM,KAAK,EAAQ,SAAS,iBAAoC,uBAAuB,KAAK,CAAC,CAAC,GACtH,KAAa,MAAgC;EACjD,IAAI,CAAC,GAAS;GAAE,EAAQ,SAAS,MAAM,EAAE,eAAe,GAAK,CAAC;GAAG;EAAQ;EAEzE,AADA,EAAQ,MAAM,EAAE,eAAe,GAAK,CAAC,GACrC,EAAQ,eAAe;GAAE,OAAO;GAAW,QAAQ;EAAU,CAAC;CAChE;CAuEA,AArEA,QAAsB;EACpB,IAAI,CAAC,GAAQ;EACb,IAAM,IAAU,EAAW,SACrB,IAAO,EAAQ,SACf,UAAiB;GACrB,IAAM,IAAQ,EAAQ,QAAQ,cAAc,CAAC,EAAE,aAAa,YAAY;GACxE,AAAI,KAAU,OACT,EAAK,gBAAgB,YAAY,IADK,EAAK,aAAa,cAAc,CAAK;GAEhF,IAAM,IAAM,iBAAiB,CAAO,CAAC,CAAC,cAAc;GACpD,EAAK,MAAM,IAAM,QAAQ;GACzB,IAAM,IAAW,OAAO,gBAClB,KAAY,GAAU,cAAc,KAAK,GACzC,KAAW,GAAU,aAAa,KAAK,GACvC,IAAY,KAAY,GAAU,SAAS,SAAS,gBAAgB,eAAe,IACnF,IAAa,KAAW,GAAU,UAAU,OAAO,eAAe;GACxE,EAAK,MAAM,WAAW,GAAG,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,IAAY,CAAQ,CAAC,EAAE;GAC1E,IAAM,IAAO,EAAQ,sBAAsB,GACrC,IAAQ,KAAK,IAAI,GAAG,IAAa,EAAK,SAAS,CAAC,GAChD,IAAQ,KAAK,IAAI,GAAG,EAAK,MAAM,IAAU,CAAC,GAE1C,IAAU,IADM,KAAK,IAAI,EAAK,eAAe,GAAG,GAC9B,KAAiB,IAAQ;GACjD,EAAK,MAAM,YAAY,GAAG,KAAK,IAAI,KAAK,IAAU,IAAQ,CAAK,EAAE;GACjE,IAAM,IAAM,EAAK,sBAAsB,GAEjC,IADO,MAAU,UAAW,IACU,EAAK,OAA9B,EAAK,QAAQ,EAAI,OAC9B,IAAM,IAAU,EAAK,MAAM,EAAI,SAAS,IAAI,EAAK,SAAS;GAEhE,AADA,EAAK,MAAM,OAAO,GAAG,KAAK,IAAI,GAAU,KAAK,IAAI,GAAM,IAAY,EAAI,KAAK,CAAC,EAAE,KAC/E,EAAK,MAAM,MAAM,GAAG,KAAK,IAAI,GAAS,KAAK,IAAI,GAAK,IAAa,EAAI,MAAM,CAAC,EAAE;EAChF;EACA,EAAS;EACT,IAAM,IAAS,IAAI,eAAe,CAAQ;EAE1C,AADA,EAAO,QAAQ,CAAO,GACtB,EAAO,QAAQ,CAAI;EACnB,IAAM,IAAgB,IAAI,iBAAiB,CAAQ;EACnD,KAAK,IAAI,IAA+B,GAAS,GAAU,IAAW,EAAS,eAC7E,EAAc,QAAQ,GAAU;GAAE,YAAY;GAAM,iBAAiB,CAAC,cAAc,KAAK;EAAE,CAAC;EAM9F,OAJA,OAAO,iBAAiB,UAAU,CAAQ,GAC1C,OAAO,iBAAiB,UAAU,GAAU,EAAI,GAChD,OAAO,gBAAgB,iBAAiB,UAAU,CAAQ,GAC1D,OAAO,gBAAgB,iBAAiB,UAAU,CAAQ,SAC7C;GAMX,AALA,EAAO,WAAW,GAClB,EAAc,WAAW,GACzB,OAAO,oBAAoB,UAAU,CAAQ,GAC7C,OAAO,oBAAoB,UAAU,GAAU,EAAI,GACnD,OAAO,gBAAgB,oBAAoB,UAAU,CAAQ,GAC7D,OAAO,gBAAgB,oBAAoB,UAAU,CAAQ;EAC/D;CACF,GAAG;EAAC;EAAQ;EAAO;CAAK,CAAC,GAEzB,QAAsB;EACpB,IAAI,KAAU,CAAC,EAAQ,SAAS;GAC9B,IAAM,IAAU,EAAgB,GAC1B,IAAS,EAAa,YAAY,SAAS,EAAQ,GAAG,EAAE,IAC1D,EAAa,YAAY,UAAU,EAAQ,KAC3C,EAAQ,MAAK,MAAW,EAAQ,QAAQ,WAAW,CAAU,KAAK,EAAQ;GAE9E,AADA,EAAU,CAAM,GAChB,EAAa,UAAU;EACzB,OAAO,IAAI,CAAC,KAAU,EAAQ,WAAW,SAAS,kBAAkB,SAAS,MAE3E,EAAa;OACR,IAAI,MAAW,SAAS,kBAAkB,SAAS,QAAQ,EAAQ,SAAS,SAAS,SAAS,aAAa,IAAI;GACpH,IAAM,IAAU,SAAS;GACzB,CAAI,MAAY,EAAQ,WAAW,GAAS,QAAQ,WAAW,KAAK,CAAC,EAAQ,SAAS,SAAS,CAAO,MAAG,EAAU,EAAgB,CAAC,CAAC,EAAE;EACzI;EACA,EAAQ,UAAU;CACpB,CAAC,GAED,QAAsB;EACpB,IAAI,CAAC,GAAQ;EACb,IAAM,KAAW,MAAiB;GAChC,IAAM,IAAS,EAAM;GACrB,AAAI,aAAkB,QAAQ,CAAC,EAAW,SAAS,SAAS,CAAM,KAAK,CAAC,EAAQ,SAAS,SAAS,CAAM,KAAG,EAAY,EAAK;EAC9H;EAGA,OAFA,SAAS,iBAAiB,eAAe,GAAS,EAAI,GACtD,SAAS,iBAAiB,WAAW,CAAO,SAC/B;GAEX,AADA,SAAS,oBAAoB,eAAe,GAAS,EAAI,GACzD,SAAS,oBAAoB,WAAW,CAAO;EACjD;CACF,GAAG,CAAC,GAAQ,CAAW,CAAC;CAExB,IAAM,KAAiB,MAA+B;EACpD,IAAI,EAAM,QAAQ,UAGhB,AAFA,EAAM,eAAe,GACrB,EAAM,gBAAgB,GACtB,EAAM;OACD,IAAI,EAAM,QAAQ,OAIvB,AADA,EAAa,GACb,EAAY,EAAK;OACZ,IAAI;GAAC;GAAa;GAAW;GAAQ;EAAK,CAAC,CAAC,SAAS,EAAM,GAAG,GAAG;GACtE,EAAM,eAAe;GACrB,IAAM,IAAU,EAAgB,GAC1B,IAAQ,EAAQ,QAAQ,SAAS,aAAkC,GACnE,IAAO,EAAM,QAAQ,SAAS,IAAI,EAAM,QAAQ,QAAQ,EAAQ,SAAS,KAC1E,KAAS,EAAM,QAAQ,cAAc,IAAI,MAAM,EAAQ,UAAU,EAAQ;GAC9E,EAAU,EAAQ,EAAK;EACzB;CACF;CAEA,OAAO,kBAAA,GAAA,EAAA,UAAA,CACL,kBAAC,GAAD;EAAQ,KAAK;EAAY,IAAI,GAAG,EAAG;EAAW,MAAK;EAAkB;EAAe;EAClF,QAAQ;EAAU,UAAU;EAC5B,WAAW,8BAA8B;EACzC,cAAY,KAAa;EAAO,OAAO,IAAW,IAAQ,KAAA;EAC1D,iBAAc;EAAO,iBAAe;EAAQ,iBAAe,IAAS,GAAG,EAAG,SAAS,KAAA;EACnF,eAAe;GAAqC,AAAnC,EAAa,UAAU,YAAY,EAAY,CAAC,CAAM;EAAG;EAC1E,YAAW,MAAS;GACd,MACA,EAAM,QAAQ,eAAe,EAAM,QAAQ,aAC7C,EAAM,eAAe,GACrB,EAAa,UAAU,EAAM,QAAQ,YAAY,SAAS,SACtD,IAAQ,EAAU,EAAM,QAAQ,YAAY,EAAgB,CAAC,CAAC,GAAG,EAAE,IAAI,EAAgB,CAAC,CAAC,EAAE,IAC1F,EAAY,EAAI,KACZ,KAAU,EAAM,QAAQ,YAAU,EAAc,CAAK;EAClE;EAdF,UAAA;GAeG,KAAQ,QAAQ,kBAAC,QAAD;IAAM,WAAU;IAA0B,eAAY;IAAQ,UAAA;GAAW,CAAA;GACzF,CAAC,KAAY,kBAAC,GAAD;IAAM,MAAK;IAAK,QAAO;IAAS,OAAM;IAAU,WAAU;IAA4B,UAAA;GAAY,CAAA;IAC9G,CAAC,KAAY,KAAQ,SAAS,kBAAC,QAAD;IAAM,WAAU;IAA0B,eAAY;IAAO,UAAA,kBAAC,GAAD,CAAU,CAAA;GAAO,CAAA;EACxG;CACP,CAAA,GAAA,KAAU,OAAO,WAAa,OAAe,EAC5C,kBAAC,OAAD;EAAK,KAAK;EAAS,IAAI,GAAG,EAAG;EAAQ,MAAK;EAAO,mBAAiB,GAAG,EAAG;EACtE,UAAU;EAAI,WAAU;EAA6B,WAAW;EAC/D,UAAA,EAAM,KAAI,MAAQ,kBAAC,UAAD;GAAsB,MAAK;GAAS,gBAAc,EAAK;GACxE,MAAM,MAAe,KAAA,IAAY,aAAa;GAC9C,gBAAc,MAAe,KAAA,IAAY,KAAA,IAAY,MAAe,EAAK;GACzE,UAAU,EAAK;GAAU,iBAAe,EAAK,YAAY,KAAA;GACzD,UAAU,CAAC,EAAK,YAAY,MAAa,EAAK,KAAK,IAAI;GACvD,WAAU;GAA0B,eAAe,EAAY,EAAK,EAAE;GACtE,eAAe;IACT,EAAK,aACT,EAAM,GACN,EAAK,WAAW,GAChB,IAAW,EAAK,EAAE;GACpB;GAXiB,UAAA;IAYhB,EAAK,QAAQ,QAAQ,kBAAC,QAAD;KAAM,WAAU;KAA0B,eAAY;KAAQ,UAAA,EAAK;IAAW,CAAA;IACpG,kBAAC,GAAD;KAAM,MAAK;KAAK,OAAM;KAAU,WAAU;KAA4B,UAAA,EAAK;IAAY,CAAA;IACtF,MAAe,KAAA,KAAa,kBAAC,QAAD;KAAM,WAAU;KAA2B,eAAY;KAAQ,UAAA,MAAe,EAAK,MAAM,kBAAC,GAAD,CAAQ,CAAA;IAAQ,CAAA;GAChI;EAfwB,GAAA,EAAK,EAe7B,CAAC;CACN,CAAA,GAAG,SAAS,IAAI,CACvB,EAAA,CAAA;AACJ"}
@@ -1,14 +1,19 @@
1
1
  import React from 'react';
2
+ import { type DemoSource } from './source';
3
+ export type { DemoSource } from './source';
2
4
  export interface CodeBlockProps {
3
5
  children: string;
4
6
  language?: string;
5
7
  }
8
+ type DemoSourceLoader = () => Promise<DemoSource>;
6
9
  export interface DemoProps {
7
10
  title: string;
8
11
  description?: string;
9
12
  children: React.ReactNode;
10
13
  code: string;
11
14
  language?: string;
15
+ source?: DemoSource | DemoSourceLoader;
16
+ sourceContext?: Record<string, unknown>;
12
17
  }
13
18
  declare const Demo: React.FC<DemoProps>;
14
19
  export default Demo;
@@ -1,35 +1,41 @@
1
- import { useCopyToClipboard as e } from "../../hooks/useCopyToClipboard.js";
2
- import { useId as t, useState as n } from "react";
3
- import { jsx as r, jsxs as i } from "react/jsx-runtime";
4
- import { Prism as a } from "react-syntax-highlighter";
5
- import { coldarkDark as o } from "react-syntax-highlighter/dist/esm/styles/prism";
1
+ import e from "../Button/index.js";
2
+ import t from "../Text/index.js";
3
+ import { useCopyToClipboard as n } from "../../hooks/useCopyToClipboard.js";
4
+ import { formatDemoSource as r } from "./source.js";
5
+ import { useEffect as i, useId as a, useRef as o, useState as s } from "react";
6
+ import { jsx as c, jsxs as l } from "react/jsx-runtime";
7
+ import { Prism as u } from "react-syntax-highlighter";
8
+ import { coldarkDark as d } from "react-syntax-highlighter/dist/esm/styles/prism";
6
9
  //#region src/components/Demo/index.tsx
7
- var s = ({ children: t, language: n = "jsx" }) => {
8
- let { copied: s, error: c, copy: l } = e();
9
- return /* @__PURE__ */ i("div", {
10
+ var f = ({ children: r, language: i = "jsx" }) => {
11
+ let { copied: a, error: o, copy: s } = n();
12
+ return /* @__PURE__ */ l("div", {
10
13
  className: "min-w-0 overflow-hidden rounded-lg border border-base-300",
11
- children: [/* @__PURE__ */ i("div", {
14
+ children: [/* @__PURE__ */ l("div", {
12
15
  className: "flex items-center justify-between gap-3 bg-base-200 px-4 py-2",
13
- children: [/* @__PURE__ */ r("span", {
14
- className: "font-mono text-xs opacity-70",
15
- children: n
16
- }), /* @__PURE__ */ i("div", {
16
+ children: [/* @__PURE__ */ c(t, {
17
+ size: "xs",
18
+ mono: !0,
19
+ color: "muted",
20
+ children: i
21
+ }), /* @__PURE__ */ l("div", {
17
22
  className: "flex items-center gap-2",
18
- children: [/* @__PURE__ */ r("span", {
23
+ children: [/* @__PURE__ */ c(t, {
19
24
  role: "status",
20
- className: "text-xs",
21
- children: c ? "Copy failed / 复制失败,请手动复制" : s ? "Copied / 已复制" : ""
22
- }), /* @__PURE__ */ r("button", {
25
+ size: "xs",
26
+ children: o ? "Copy failed / 复制失败,请手动复制" : a ? "Copied / 已复制" : ""
27
+ }), /* @__PURE__ */ c(e, {
23
28
  type: "button",
24
- className: "btn btn-ghost btn-xs",
25
- onClick: () => void l(t),
29
+ variant: "ghost",
30
+ size: "xs",
31
+ onClick: () => void s(r),
26
32
  "aria-label": "Copy source code / 复制源代码",
27
33
  children: "Copy / 复制"
28
34
  })]
29
35
  })]
30
- }), /* @__PURE__ */ r(a, {
31
- language: n,
32
- style: o,
36
+ }), /* @__PURE__ */ c(u, {
37
+ language: i,
38
+ style: d,
33
39
  tabIndex: 0,
34
40
  "aria-label": "Source code / 源代码",
35
41
  customStyle: {
@@ -41,59 +47,128 @@ var s = ({ children: t, language: n = "jsx" }) => {
41
47
  maxHeight: "32rem",
42
48
  overflow: "auto"
43
49
  },
44
- children: t
50
+ children: r
45
51
  })]
46
52
  });
47
- }, c = ({ title: e, description: a, children: o, code: c, language: l = "jsx" }) => {
48
- let [u, d] = n(!1), f = t(), p = `${f}-title`, m = `${f}-code`;
49
- return /* @__PURE__ */ i("section", {
50
- "aria-labelledby": p,
51
- className: "mb-8 min-w-0 rounded-xl border border-base-300 bg-base-100 shadow-sm",
53
+ }, p = ({ title: n, description: u, children: d, code: p, language: m = "jsx", source: h, sourceContext: g }) => {
54
+ let [_, v] = s(!1), [y, b] = s(0), [x, S] = s(null), C = o(null);
55
+ i(() => {
56
+ if (!_ || typeof h != "function") return;
57
+ let e = !0;
58
+ return (C.current?.loader !== h || C.current.attempt !== y) && (C.current = {
59
+ loader: h,
60
+ attempt: y,
61
+ promise: Promise.resolve().then(() => h())
62
+ }), C.current.promise.then((t) => {
63
+ e && S({
64
+ loader: h,
65
+ attempt: y,
66
+ status: "success",
67
+ data: t
68
+ });
69
+ }, () => {
70
+ e && S({
71
+ loader: h,
72
+ attempt: y,
73
+ status: "error"
74
+ });
75
+ }), () => {
76
+ e = !1;
77
+ };
78
+ }, [
79
+ h,
80
+ _,
81
+ y
82
+ ]);
83
+ let w = typeof h == "function" && x?.loader === h && x.attempt === y ? x : null, T = typeof h == "function" ? w?.status === "success" ? w.data : void 0 : h, E = typeof h == "function" && !w, D = a(), O = `${D}-title`, k = `${D}-code`;
84
+ return /* @__PURE__ */ l("section", {
85
+ "aria-labelledby": O,
86
+ className: "dishui-demo mb-4 min-w-0 rounded-lg border border-hairline bg-page",
52
87
  children: [
53
- /* @__PURE__ */ i("div", {
54
- className: "border-b border-base-300 px-5 py-4",
55
- children: [/* @__PURE__ */ r("h3", {
56
- id: p,
57
- className: "text-lg font-semibold",
58
- children: e
59
- }), a && /* @__PURE__ */ r("p", {
60
- className: "mt-1 text-sm leading-relaxed opacity-70",
61
- children: a
88
+ /* @__PURE__ */ l("div", {
89
+ className: "border-b border-hairline bg-subtle px-4 py-3 rounded-t-lg",
90
+ children: [/* @__PURE__ */ c(t, {
91
+ as: "h3",
92
+ id: O,
93
+ size: "sm",
94
+ weight: "semibold",
95
+ children: n
96
+ }), u && /* @__PURE__ */ c(t, {
97
+ as: "p",
98
+ size: "sm",
99
+ color: "muted",
100
+ className: "mt-1 leading-relaxed",
101
+ children: u
62
102
  })]
63
103
  }),
64
- /* @__PURE__ */ r("div", {
65
- className: "flex min-w-0 flex-wrap items-center gap-4 px-5 py-6",
66
- children: o
104
+ /* @__PURE__ */ c("div", {
105
+ className: "flex min-w-0 flex-wrap items-center gap-4 p-4",
106
+ children: d
67
107
  }),
68
- /* @__PURE__ */ i("div", {
108
+ /* @__PURE__ */ l("div", {
69
109
  className: "border-t border-base-300",
70
- children: [/* @__PURE__ */ i("button", {
110
+ children: [/* @__PURE__ */ l(e, {
71
111
  type: "button",
72
- className: "btn btn-ghost flex w-full items-center justify-between rounded-none rounded-b-xl px-5 text-sm font-medium",
73
- "aria-expanded": u,
74
- "aria-controls": m,
75
- onClick: () => d((e) => !e),
76
- children: [/* @__PURE__ */ r("span", { children: "Source Code / 源代码" }), /* @__PURE__ */ r("svg", {
112
+ variant: "ghost",
113
+ className: "flex w-full items-center justify-between rounded-none rounded-b-lg px-4 text-xs font-medium",
114
+ "aria-expanded": _,
115
+ "aria-controls": k,
116
+ onClick: () => v((e) => !e),
117
+ children: [/* @__PURE__ */ c(t, {
118
+ size: "xs",
119
+ weight: "medium",
120
+ color: "inherit",
121
+ children: "Source Code / 源代码"
122
+ }), /* @__PURE__ */ c("svg", {
77
123
  "aria-hidden": "true",
78
124
  xmlns: "http://www.w3.org/2000/svg",
79
- className: `h-4 w-4 transition-transform motion-reduce:transition-none ${u ? "rotate-180" : ""}`,
125
+ className: `h-4 w-4 transition-transform motion-reduce:transition-none ${_ ? "rotate-180" : ""}`,
80
126
  fill: "none",
81
127
  viewBox: "0 0 24 24",
82
128
  stroke: "currentColor",
83
- children: /* @__PURE__ */ r("path", {
129
+ children: /* @__PURE__ */ c("path", {
84
130
  strokeLinecap: "round",
85
131
  strokeLinejoin: "round",
86
132
  strokeWidth: "2",
87
133
  d: "M19 9l-7 7-7-7"
88
134
  })
89
135
  })]
90
- }), /* @__PURE__ */ r("div", {
91
- id: m,
92
- hidden: !u,
136
+ }), /* @__PURE__ */ c("div", {
137
+ id: k,
138
+ hidden: !_,
93
139
  className: "min-w-0 border-t border-base-300 p-4",
94
- children: u && /* @__PURE__ */ r(s, {
95
- language: l,
96
- children: c
140
+ children: _ && /* @__PURE__ */ c("div", {
141
+ className: "min-w-0 space-y-3",
142
+ children: /* @__PURE__ */ c("div", {
143
+ className: "min-w-0",
144
+ "aria-busy": E,
145
+ children: E ? /* @__PURE__ */ c(t, {
146
+ as: "p",
147
+ role: "status",
148
+ size: "sm",
149
+ children: "Loading source code… / 正在加载源码…"
150
+ }) : w?.status === "error" ? /* @__PURE__ */ l("div", {
151
+ className: "space-y-2",
152
+ children: [/* @__PURE__ */ c(t, {
153
+ as: "p",
154
+ role: "alert",
155
+ size: "sm",
156
+ children: "Could not load source code. Please retry. / 源码加载失败,请重试。"
157
+ }), /* @__PURE__ */ c(e, {
158
+ type: "button",
159
+ variant: "ghost",
160
+ size: "sm",
161
+ onClick: () => b((e) => e + 1),
162
+ children: "Retry loading source / 重新加载源码"
163
+ })]
164
+ }) : T ? /* @__PURE__ */ c(f, {
165
+ language: "tsx",
166
+ children: r(T, g)
167
+ }) : h ? null : /* @__PURE__ */ c(f, {
168
+ language: m,
169
+ children: p
170
+ })
171
+ })
97
172
  })
98
173
  })]
99
174
  })
@@ -101,6 +176,6 @@ var s = ({ children: t, language: n = "jsx" }) => {
101
176
  });
102
177
  };
103
178
  //#endregion
104
- export { c as default };
179
+ export { p as default };
105
180
 
106
181
  //# sourceMappingURL=index.js.map