@hadoku/task-ui-components 0.0.0-stage → 4.20.4
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/LICENSE +21 -0
- package/README.md +917 -2
- package/dist/app-header.css +288 -0
- package/dist/bento.css +143 -0
- package/dist/button.css +227 -0
- package/dist/components/AppHeader.d.ts +61 -0
- package/dist/components/AppHeader.d.ts.map +1 -0
- package/dist/components/AppHeader.js +23 -0
- package/dist/components/AppHeader.js.map +1 -0
- package/dist/components/BentoCard.d.ts +23 -0
- package/dist/components/BentoCard.d.ts.map +1 -0
- package/dist/components/BentoCard.js +49 -0
- package/dist/components/BentoCard.js.map +1 -0
- package/dist/components/BentoGrid.d.ts +24 -0
- package/dist/components/BentoGrid.d.ts.map +1 -0
- package/dist/components/BentoGrid.js +22 -0
- package/dist/components/BentoGrid.js.map +1 -0
- package/dist/components/Button.d.ts +28 -0
- package/dist/components/Button.d.ts.map +1 -0
- package/dist/components/Button.js +51 -0
- package/dist/components/Button.js.map +1 -0
- package/dist/components/ConnectedSettings.d.ts +53 -0
- package/dist/components/ConnectedSettings.d.ts.map +1 -0
- package/dist/components/ConnectedSettings.js +368 -0
- package/dist/components/ConnectedSettings.js.map +1 -0
- package/dist/components/ConnectedThemePicker.d.ts +52 -0
- package/dist/components/ConnectedThemePicker.d.ts.map +1 -0
- package/dist/components/ConnectedThemePicker.js +32 -0
- package/dist/components/ConnectedThemePicker.js.map +1 -0
- package/dist/components/ContextMenu.d.ts +22 -0
- package/dist/components/ContextMenu.d.ts.map +1 -0
- package/dist/components/ContextMenu.js +23 -0
- package/dist/components/ContextMenu.js.map +1 -0
- package/dist/components/HadokuThemePicker.d.ts +27 -0
- package/dist/components/HadokuThemePicker.d.ts.map +1 -0
- package/dist/components/HadokuThemePicker.js +19 -0
- package/dist/components/HadokuThemePicker.js.map +1 -0
- package/dist/components/LoadingSkeleton.d.ts +14 -0
- package/dist/components/LoadingSkeleton.d.ts.map +1 -0
- package/dist/components/LoadingSkeleton.js +62 -0
- package/dist/components/LoadingSkeleton.js.map +1 -0
- package/dist/components/Modal.d.ts +39 -0
- package/dist/components/Modal.d.ts.map +1 -0
- package/dist/components/Modal.js +106 -0
- package/dist/components/Modal.js.map +1 -0
- package/dist/components/NumberInput.d.ts +26 -0
- package/dist/components/NumberInput.d.ts.map +1 -0
- package/dist/components/NumberInput.js +131 -0
- package/dist/components/NumberInput.js.map +1 -0
- package/dist/components/Select.d.ts +52 -0
- package/dist/components/Select.d.ts.map +1 -0
- package/dist/components/Select.js +281 -0
- package/dist/components/Select.js.map +1 -0
- package/dist/components/SettingsModal.d.ts +69 -0
- package/dist/components/SettingsModal.d.ts.map +1 -0
- package/dist/components/SettingsModal.js +141 -0
- package/dist/components/SettingsModal.js.map +1 -0
- package/dist/components/ThemeIcons.d.ts +26 -0
- package/dist/components/ThemeIcons.d.ts.map +1 -0
- package/dist/components/ThemeIcons.js +74 -0
- package/dist/components/ThemeIcons.js.map +1 -0
- package/dist/components/ThemePicker.d.ts +28 -0
- package/dist/components/ThemePicker.d.ts.map +1 -0
- package/dist/components/ThemePicker.js +101 -0
- package/dist/components/ThemePicker.js.map +1 -0
- package/dist/components/Toast.d.ts +13 -0
- package/dist/components/Toast.d.ts.map +1 -0
- package/dist/components/Toast.js +70 -0
- package/dist/components/Toast.js.map +1 -0
- package/dist/components/Toaster.d.ts +19 -0
- package/dist/components/Toaster.d.ts.map +1 -0
- package/dist/components/Toaster.js +19 -0
- package/dist/components/Toaster.js.map +1 -0
- package/dist/components/Tooltip.d.ts +49 -0
- package/dist/components/Tooltip.d.ts.map +1 -0
- package/dist/components/Tooltip.js +143 -0
- package/dist/components/Tooltip.js.map +1 -0
- package/dist/context-menu.css +11 -0
- package/dist/controls.css +137 -0
- package/dist/hooks/useReducedMotion.d.ts +7 -0
- package/dist/hooks/useReducedMotion.d.ts.map +1 -0
- package/dist/hooks/useReducedMotion.js +25 -0
- package/dist/hooks/useReducedMotion.js.map +1 -0
- package/dist/hooks/useToast.d.ts +18 -0
- package/dist/hooks/useToast.d.ts.map +1 -0
- package/dist/hooks/useToast.js +28 -0
- package/dist/hooks/useToast.js.map +1 -0
- package/dist/icon-btn.css +64 -0
- package/dist/index.d.ts +49 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +75 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/menuPlacement.d.ts +16 -0
- package/dist/lib/menuPlacement.d.ts.map +1 -0
- package/dist/lib/menuPlacement.js +19 -0
- package/dist/lib/menuPlacement.js.map +1 -0
- package/dist/lib/selectMenus.d.ts +30 -0
- package/dist/lib/selectMenus.d.ts.map +1 -0
- package/dist/lib/selectMenus.js +236 -0
- package/dist/lib/selectMenus.js.map +1 -0
- package/dist/lib/settingsClient.d.ts +144 -0
- package/dist/lib/settingsClient.d.ts.map +1 -0
- package/dist/lib/settingsClient.js +157 -0
- package/dist/lib/settingsClient.js.map +1 -0
- package/dist/lib/themeScope.d.ts +19 -0
- package/dist/lib/themeScope.d.ts.map +1 -0
- package/dist/lib/themeScope.js +10 -0
- package/dist/lib/themeScope.js.map +1 -0
- package/dist/lib/titleTooltips.d.ts +38 -0
- package/dist/lib/titleTooltips.d.ts.map +1 -0
- package/dist/lib/titleTooltips.js +166 -0
- package/dist/lib/titleTooltips.js.map +1 -0
- package/dist/lib/tooltipPlacement.d.ts +14 -0
- package/dist/lib/tooltipPlacement.d.ts.map +1 -0
- package/dist/lib/tooltipPlacement.js +34 -0
- package/dist/lib/tooltipPlacement.js.map +1 -0
- package/dist/modal.css +129 -0
- package/dist/number-input.css +131 -0
- package/dist/scrollbar.css +165 -0
- package/dist/select.css +251 -0
- package/dist/styles.css +1866 -0
- package/dist/task-ui-components.css +1866 -0
- package/dist/theme-picker.css +219 -0
- package/dist/toaster.css +159 -0
- package/dist/tooltip.css +103 -0
- package/dist/types/index.d.ts +37 -0
- package/dist/types/index.d.ts.map +1 -0
- package/package.json +60 -4
- package/src/app-header.css +288 -0
- package/src/bento.css +143 -0
- package/src/button.css +227 -0
- package/src/components/AppHeader.tsx +87 -0
- package/src/components/BentoCard.tsx +72 -0
- package/src/components/BentoGrid.tsx +48 -0
- package/src/components/Button.tsx +81 -0
- package/src/components/ConnectedSettings.tsx +545 -0
- package/src/components/ConnectedThemePicker.tsx +79 -0
- package/src/components/ContextMenu.tsx +48 -0
- package/src/components/HadokuThemePicker.tsx +39 -0
- package/src/components/LoadingSkeleton.tsx +84 -0
- package/src/components/Modal.tsx +171 -0
- package/src/components/NumberInput.tsx +161 -0
- package/src/components/Select.tsx +359 -0
- package/src/components/SettingsModal.tsx +235 -0
- package/src/components/ThemeIcons.tsx +127 -0
- package/src/components/ThemePicker.tsx +149 -0
- package/src/components/Toast.tsx +92 -0
- package/src/components/Toaster.tsx +43 -0
- package/src/components/Tooltip.tsx +212 -0
- package/src/context-menu.css +11 -0
- package/src/controls.css +137 -0
- package/src/hooks/useReducedMotion.ts +36 -0
- package/src/hooks/useToast.ts +57 -0
- package/src/icon-btn.css +64 -0
- package/src/index.ts +153 -0
- package/src/lib/menuPlacement.ts +31 -0
- package/src/lib/selectMenus.ts +303 -0
- package/src/lib/settingsClient.ts +425 -0
- package/src/lib/themeScope.ts +23 -0
- package/src/lib/titleTooltips.ts +236 -0
- package/src/lib/tooltipPlacement.ts +59 -0
- package/src/modal.css +129 -0
- package/src/number-input.css +131 -0
- package/src/scrollbar.css +165 -0
- package/src/select.css +251 -0
- package/src/theme-picker.css +219 -0
- package/src/toaster.css +159 -0
- package/src/tooltip.css +103 -0
- package/src/types/index.ts +43 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AppHeader — the ecosystem-standard top bar for every hadoku.me app.
|
|
3
|
+
*
|
|
4
|
+
* Enforces the shared contract so child apps stop drifting:
|
|
5
|
+
* [ title (left) ] ————— <space> ————— [ status? · ThemePicker · Settings gear ] (right)
|
|
6
|
+
*
|
|
7
|
+
* Everything BELOW the header is the app's own concern. The header owns the
|
|
8
|
+
* title, and it OWNS both right-hand controls outright — it renders the theme
|
|
9
|
+
* picker and the settings gear itself. Neither is a prop.
|
|
10
|
+
*
|
|
11
|
+
* WHY THEY ARE NOT PROPS ANY MORE
|
|
12
|
+
* -------------------------------
|
|
13
|
+
* `themePicker?: React.ReactNode` was an open hole: a slot that accepts a node
|
|
14
|
+
* accepts ANY node, so "every app uses the same picker" was a convention, and
|
|
15
|
+
* conventions drift. They did — one app passed a raw `<ThemePicker>` with its
|
|
16
|
+
* own `isOpen` state and a debug logger wired into `getThemeIcon`, and eight
|
|
17
|
+
* apps each kept a hand-copied `useTheme` hook to feed it. Those copies had
|
|
18
|
+
* already diverged on real behaviour (only one wrote `localStorage`, so the
|
|
19
|
+
* rest lost the theme on browser restart and flashed a default on next paint).
|
|
20
|
+
*
|
|
21
|
+
* The control is now rendered here, from `useHadokuTheme()`. An app cannot
|
|
22
|
+
* substitute, restyle or omit it. What an app CAN do is inject its own
|
|
23
|
+
* preferences — see `children`.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```tsx
|
|
27
|
+
* import { AppHeader } from '@wolffm/task-ui-components'
|
|
28
|
+
* import { HadokuThemeRoot } from '@wolffm/themes'
|
|
29
|
+
*
|
|
30
|
+
* // once, at the app root:
|
|
31
|
+
* <HadokuThemeRoot>
|
|
32
|
+
* <AppHeader title="Print Tool" status={<ApiStatus status={apiStatus} />}>
|
|
33
|
+
* <MyAppPreferences /> // optional: appended AFTER the canonical rows
|
|
34
|
+
* </AppHeader>
|
|
35
|
+
* </HadokuThemeRoot>
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
import React from 'react'
|
|
39
|
+
import { ConnectedSettings, type ConnectedSettingsProps } from './ConnectedSettings'
|
|
40
|
+
import { HadokuThemePicker } from './HadokuThemePicker'
|
|
41
|
+
|
|
42
|
+
export interface AppHeaderProps {
|
|
43
|
+
/** Left-aligned header title. A string is wrapped in an <h1>; pass a node for
|
|
44
|
+
* custom markup (e.g. a clickable title button). */
|
|
45
|
+
title: React.ReactNode
|
|
46
|
+
/** Optional status/extra node shown left of the theme + settings controls
|
|
47
|
+
* (e.g. an "API Online" indicator). Acceptable per the header contract. */
|
|
48
|
+
status?: React.ReactNode
|
|
49
|
+
/** Identity hints for the settings popout. Narrow on purpose: `userType` and
|
|
50
|
+
* `name` exist because an app behind a path-prefixed shim may not be able to
|
|
51
|
+
* reach `/session/whoami` (conjure), NOT as a styling or layout hook. */
|
|
52
|
+
settingsProps?: ConnectedSettingsProps
|
|
53
|
+
/** THE app-specific extension point. Rendered inside the settings popout as a
|
|
54
|
+
* final section, under the four canonical rows. Put your app's own
|
|
55
|
+
* preferences here — checkboxes, selects, whatever it needs; this slot is
|
|
56
|
+
* deliberately unconstrained. What it cannot do is alter the rows above it. */
|
|
57
|
+
children?: React.ReactNode
|
|
58
|
+
/** Extra class on the <header> root. Layout only — it cannot reach the
|
|
59
|
+
* controls, which own their own markup. */
|
|
60
|
+
className?: string
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function AppHeader({
|
|
64
|
+
title,
|
|
65
|
+
status,
|
|
66
|
+
settingsProps,
|
|
67
|
+
children,
|
|
68
|
+
className = ''
|
|
69
|
+
}: AppHeaderProps) {
|
|
70
|
+
// No useHadokuTheme() call here: <HadokuThemePicker> reads the context and
|
|
71
|
+
// throws its own actionable error when the app root was never wrapped, so a
|
|
72
|
+
// second read would only duplicate that.
|
|
73
|
+
return (
|
|
74
|
+
<header className={`app-header ${className}`.trim()}>
|
|
75
|
+
{typeof title === 'string' ? (
|
|
76
|
+
<h1 className="app-header__title">{title}</h1>
|
|
77
|
+
) : (
|
|
78
|
+
<div className="app-header__title">{title}</div>
|
|
79
|
+
)}
|
|
80
|
+
<div className="app-header__actions">
|
|
81
|
+
{status}
|
|
82
|
+
<HadokuThemePicker />
|
|
83
|
+
<ConnectedSettings {...settingsProps}>{children}</ConnectedSettings>
|
|
84
|
+
</div>
|
|
85
|
+
</header>
|
|
86
|
+
)
|
|
87
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BentoCard Component
|
|
3
|
+
* Base card component for bento grid layouts
|
|
4
|
+
* Supports various sizes and disabled state for preloading
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React from 'react'
|
|
8
|
+
|
|
9
|
+
export type CardSize = 'default' | 'wide' | 'tall' | 'large' | 'hero'
|
|
10
|
+
|
|
11
|
+
export interface BentoCardProps {
|
|
12
|
+
children: React.ReactNode
|
|
13
|
+
className?: string
|
|
14
|
+
/** Card size affects grid span */
|
|
15
|
+
size?: CardSize
|
|
16
|
+
/** Disable interactions (for preload state) */
|
|
17
|
+
disabled?: boolean
|
|
18
|
+
/** Click handler */
|
|
19
|
+
onClick?: () => void
|
|
20
|
+
/** Custom aria-label for accessibility */
|
|
21
|
+
ariaLabel?: string
|
|
22
|
+
/** Optional data attributes for animation libraries */
|
|
23
|
+
'data-layout-id'?: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const sizeClasses: Record<CardSize, string> = {
|
|
27
|
+
default: 'bento-card--default',
|
|
28
|
+
wide: 'bento-card--wide',
|
|
29
|
+
tall: 'bento-card--tall',
|
|
30
|
+
large: 'bento-card--large',
|
|
31
|
+
hero: 'bento-card--hero'
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function BentoCard({
|
|
35
|
+
children,
|
|
36
|
+
className = '',
|
|
37
|
+
size = 'default',
|
|
38
|
+
disabled = false,
|
|
39
|
+
onClick,
|
|
40
|
+
ariaLabel,
|
|
41
|
+
'data-layout-id': layoutId,
|
|
42
|
+
...rest
|
|
43
|
+
}: BentoCardProps) {
|
|
44
|
+
const handleClick = () => {
|
|
45
|
+
if (!disabled && onClick) {
|
|
46
|
+
onClick()
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
51
|
+
if ((e.key === 'Enter' || e.key === ' ') && !disabled && onClick) {
|
|
52
|
+
e.preventDefault()
|
|
53
|
+
onClick()
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<article
|
|
59
|
+
className={`bento-card ${sizeClasses[size]} ${disabled ? 'bento-card--disabled' : ''} ${className}`}
|
|
60
|
+
onClick={handleClick}
|
|
61
|
+
onKeyDown={handleKeyDown}
|
|
62
|
+
role={onClick ? 'button' : undefined}
|
|
63
|
+
tabIndex={onClick && !disabled ? 0 : undefined}
|
|
64
|
+
aria-label={ariaLabel}
|
|
65
|
+
aria-disabled={disabled}
|
|
66
|
+
data-layout-id={layoutId}
|
|
67
|
+
{...rest}
|
|
68
|
+
>
|
|
69
|
+
{children}
|
|
70
|
+
</article>
|
|
71
|
+
)
|
|
72
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BentoGrid Component
|
|
3
|
+
* Responsive grid container for bento-style card layouts
|
|
4
|
+
* Automatically adjusts columns based on viewport size
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React from 'react'
|
|
8
|
+
|
|
9
|
+
export type BentoColumns = 1 | 2 | 3 | 4 | 6
|
|
10
|
+
|
|
11
|
+
export interface BentoGridProps {
|
|
12
|
+
children: React.ReactNode
|
|
13
|
+
className?: string
|
|
14
|
+
/** Column configuration at each breakpoint */
|
|
15
|
+
columns?: {
|
|
16
|
+
sm?: BentoColumns
|
|
17
|
+
md?: BentoColumns
|
|
18
|
+
lg?: BentoColumns
|
|
19
|
+
xl?: BentoColumns
|
|
20
|
+
}
|
|
21
|
+
/** Gap between cards in rem */
|
|
22
|
+
gap?: number
|
|
23
|
+
/** Maximum width of the grid */
|
|
24
|
+
maxWidth?: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function BentoGrid({
|
|
28
|
+
children,
|
|
29
|
+
className = '',
|
|
30
|
+
columns = { sm: 2, md: 3, lg: 4, xl: 6 },
|
|
31
|
+
gap = 1,
|
|
32
|
+
maxWidth = '1400px'
|
|
33
|
+
}: BentoGridProps) {
|
|
34
|
+
const gridStyle: React.CSSProperties = {
|
|
35
|
+
'--bento-gap': `${gap}rem`,
|
|
36
|
+
'--bento-max-width': maxWidth,
|
|
37
|
+
'--bento-cols-sm': columns.sm ?? 2,
|
|
38
|
+
'--bento-cols-md': columns.md ?? 3,
|
|
39
|
+
'--bento-cols-lg': columns.lg ?? 4,
|
|
40
|
+
'--bento-cols-xl': columns.xl ?? 6
|
|
41
|
+
} as React.CSSProperties
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<div className={`bento-grid ${className}`} style={gridStyle}>
|
|
45
|
+
{children}
|
|
46
|
+
</div>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Button Component
|
|
3
|
+
* Themed button in five treatments x five colour families, so an app never
|
|
4
|
+
* falls back to the browser's default button chrome.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React from 'react'
|
|
8
|
+
import { Icon, type IconName } from '@wolffm/themes'
|
|
9
|
+
import '../button.css'
|
|
10
|
+
|
|
11
|
+
export type ButtonVariant = 'solid' | 'gradient' | 'soft' | 'outline' | 'ghost'
|
|
12
|
+
export type ButtonFamily = 'primary' | 'success' | 'warning' | 'danger' | 'neutral'
|
|
13
|
+
export type ButtonSize = 'sm' | 'md' | 'lg'
|
|
14
|
+
export type ButtonShape = 'rounded' | 'pill' | 'square'
|
|
15
|
+
|
|
16
|
+
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
17
|
+
variant?: ButtonVariant
|
|
18
|
+
family?: ButtonFamily
|
|
19
|
+
size?: ButtonSize
|
|
20
|
+
shape?: ButtonShape
|
|
21
|
+
/** Registry icon before the label. */
|
|
22
|
+
icon?: IconName
|
|
23
|
+
/** Registry icon after the label (e.g. `chevron-down` on a menu button). */
|
|
24
|
+
iconEnd?: IconName
|
|
25
|
+
/** Shows a spinner, sets aria-busy and blocks clicks. */
|
|
26
|
+
loading?: boolean
|
|
27
|
+
/** Stretch to the container width. */
|
|
28
|
+
block?: boolean
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const ICON_SIZE: Record<ButtonSize, number> = { sm: 14, md: 16, lg: 18 }
|
|
32
|
+
|
|
33
|
+
export function Button({
|
|
34
|
+
variant = 'soft',
|
|
35
|
+
family = 'primary',
|
|
36
|
+
size = 'md',
|
|
37
|
+
shape = 'rounded',
|
|
38
|
+
icon,
|
|
39
|
+
iconEnd,
|
|
40
|
+
loading = false,
|
|
41
|
+
block = false,
|
|
42
|
+
disabled,
|
|
43
|
+
type = 'button',
|
|
44
|
+
className,
|
|
45
|
+
children,
|
|
46
|
+
...rest
|
|
47
|
+
}: ButtonProps) {
|
|
48
|
+
// An icon with no label is a square button; it still needs an aria-label.
|
|
49
|
+
const iconOnly = !children && (icon || iconEnd)
|
|
50
|
+
const classes = [
|
|
51
|
+
'hdk-btn',
|
|
52
|
+
`hdk-btn--${variant}`,
|
|
53
|
+
`hdk-btn--${family}`,
|
|
54
|
+
`hdk-btn--${size}`,
|
|
55
|
+
`hdk-btn--${shape}`,
|
|
56
|
+
iconOnly && 'hdk-btn--icon-only',
|
|
57
|
+
block && 'hdk-btn--block',
|
|
58
|
+
loading && 'hdk-btn--loading',
|
|
59
|
+
className
|
|
60
|
+
]
|
|
61
|
+
.filter(Boolean)
|
|
62
|
+
.join(' ')
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<button
|
|
66
|
+
type={type}
|
|
67
|
+
className={classes}
|
|
68
|
+
disabled={disabled || loading}
|
|
69
|
+
aria-busy={loading || undefined}
|
|
70
|
+
{...rest}
|
|
71
|
+
>
|
|
72
|
+
{loading ? (
|
|
73
|
+
<span className="hdk-btn__spinner" aria-hidden="true" />
|
|
74
|
+
) : (
|
|
75
|
+
icon && <Icon name={icon} size={ICON_SIZE[size]} />
|
|
76
|
+
)}
|
|
77
|
+
{children}
|
|
78
|
+
{iconEnd && <Icon name={iconEnd} size={ICON_SIZE[size]} />}
|
|
79
|
+
</button>
|
|
80
|
+
)
|
|
81
|
+
}
|