@jbpark/ui-kit 7.1.0 → 8.1.0
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/Button.d.mts +7 -2
- package/dist/Button.d.mts.map +1 -1
- package/dist/Button.mjs.map +1 -1
- package/dist/CodeEditor.d.mts +51 -0
- package/dist/CodeEditor.d.mts.map +1 -0
- package/dist/CodeEditor.mjs +87 -0
- package/dist/CodeEditor.mjs.map +1 -0
- package/dist/Tag.d.mts +8 -3
- package/dist/Tag.d.mts.map +1 -1
- package/dist/Tag.mjs.map +1 -1
- package/dist/components/atoms/float-button/float-button.d.mts.map +1 -1
- package/dist/components/atoms/float-button/float-button.mjs +2 -1
- package/dist/components/atoms/float-button/float-button.mjs.map +1 -1
- package/dist/components/molecules/dropdown.d.mts.map +1 -1
- package/dist/components/molecules/dropdown.mjs +2 -1
- package/dist/components/molecules/dropdown.mjs.map +1 -1
- package/dist/components/molecules/marquees/item.d.mts +2 -1
- package/dist/components/molecules/marquees/item.d.mts.map +1 -1
- package/dist/components/molecules/marquees/item.mjs +20 -5
- package/dist/components/molecules/marquees/item.mjs.map +1 -1
- package/dist/components/molecules/marquees/marquees.d.mts +2 -1
- package/dist/components/molecules/marquees/marquees.d.mts.map +1 -1
- package/dist/components/molecules/marquees/marquees.mjs +3 -2
- package/dist/components/molecules/marquees/marquees.mjs.map +1 -1
- package/dist/components/organisms/drawer.mjs +7 -7
- package/dist/components/organisms/drawer.mjs.map +1 -1
- package/dist/components/templates/layout/header.d.mts.map +1 -1
- package/dist/components/templates/layout/header.mjs +2 -1
- package/dist/components/templates/layout/header.mjs.map +1 -1
- package/dist/components/templates/layout/sider.mjs.map +1 -1
- package/dist/components/templates/page-header.mjs +4 -4
- package/dist/components/templates/page-header.mjs.map +1 -1
- package/dist/core/dialog.mjs +5 -5
- package/dist/core/dialog.mjs.map +1 -1
- package/dist/core/drawer.mjs +3 -2
- package/dist/core/drawer.mjs.map +1 -1
- package/dist/core/popover.mjs +3 -2
- package/dist/core/popover.mjs.map +1 -1
- package/dist/core/select.d.mts.map +1 -1
- package/dist/core/select.mjs +3 -2
- package/dist/core/select.mjs.map +1 -1
- package/dist/lib/colors.d.mts +36 -3
- package/dist/lib/colors.d.mts.map +1 -1
- package/dist/lib/z-layers.mjs +33 -0
- package/dist/lib/z-layers.mjs.map +1 -0
- package/dist/providers/config/config.d.mts.map +1 -1
- package/dist/providers/config/config.mjs +5 -32
- package/dist/providers/config/config.mjs.map +1 -1
- package/dist/providers/config/dark-mode.mjs +39 -0
- package/dist/providers/config/dark-mode.mjs.map +1 -0
- package/dist/providers/config/types.d.mts +25 -18
- package/dist/providers/config/types.d.mts.map +1 -1
- package/dist/style.css +90 -90
- package/dist/utils.d.mts +12 -0
- package/dist/utils.d.mts.map +1 -1
- package/dist/utils.mjs +12 -0
- package/dist/utils.mjs.map +1 -1
- package/package.json +33 -1
package/dist/Button.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PresetColor } from "./lib/colors.mjs";
|
|
1
|
+
import { DeprecatedPresetColor, PresetColor } from "./lib/colors.mjs";
|
|
2
2
|
import * as React$1 from "react";
|
|
3
3
|
//#region src/components/atoms/button.d.ts
|
|
4
4
|
type NativeButtonProps = React$1.ComponentProps<'button'> & {
|
|
@@ -43,8 +43,13 @@ interface Props extends Omit<NativeButtonProps, 'size' | 'variant' | 'type'> {
|
|
|
43
43
|
/**
|
|
44
44
|
* Color, independent of `variant`. Defaults to `'default'`, or to whatever
|
|
45
45
|
* `type` maps to when `type` is set. `danger` overrides this.
|
|
46
|
+
*
|
|
47
|
+
* Preset hues use Tailwind's colour names (#342). The pre-8.0 spellings
|
|
48
|
+
* `magenta`/`geekblue`/`gold`/`volcano` still work and render unchanged, but
|
|
49
|
+
* are deprecated — use `fuchsia`/`indigo`/`amber` instead (`volcano` has no
|
|
50
|
+
* successor; it is `orange` one lightness step darker).
|
|
46
51
|
*/
|
|
47
|
-
color?: PresetColor | 'default' | 'primary' | 'danger';
|
|
52
|
+
color?: PresetColor | DeprecatedPresetColor | 'default' | 'primary' | 'danger';
|
|
48
53
|
loading?: boolean | {
|
|
49
54
|
icon: React$1.ReactNode;
|
|
50
55
|
};
|
package/dist/Button.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.d.mts","names":[],"sources":["../src/components/atoms/button.tsx"],"mappings":";;;KAaK,oBAAoB,QAAM;EAC7B;;UAGe,cAAc,KAC7B;EAGA,OAAO,QAAM;EACb;EACA;EACA;EACA;;;;;;;;;;;;;;;;;;EAkBA;;;;;EAKA;;;;;;EAMA;EACA
|
|
1
|
+
{"version":3,"file":"Button.d.mts","names":[],"sources":["../src/components/atoms/button.tsx"],"mappings":";;;KAaK,oBAAoB,QAAM;EAC7B;;UAGe,cAAc,KAC7B;EAGA,OAAO,QAAM;EACb;EACA;EACA;EACA;;;;;;;;;;;;;;;;;;EAkBA;;;;;EAKA;;;;;;EAMA;EACA;;;;;;;;;;EAUA,QACE,cAAc;EAChB;IAAsB,MAAM,QAAM;;;cAqF9B,WAAU,MAAA,WAAA,MAAA,SAAA,UAAA,MAAA,OAAA,OAAA,OAAA,UAAA,SAAA,QAAA,SAAA,UAAA,gBAAA,SAiBb,UAAK,QAAA,IAAA"}
|
package/dist/Button.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.mjs","names":[],"sources":["../src/components/atoms/button.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\n\nimport { Slot } from '@radix-ui/react-slot';\nimport { LoaderCircle } from 'lucide-react';\n\nimport { useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport { INTERACTIVE_CHASSIS } from '../../lib/chassis';\nimport { type PresetColor } from '../../lib/colors';\n\ntype NativeButtonProps = React.ComponentProps<'button'> & {\n asChild?: boolean;\n};\n\nexport interface Props extends Omit<\n NativeButtonProps,\n 'size' | 'variant' | 'type'\n> {\n icon?: React.ReactNode;\n block?: boolean;\n danger?: boolean;\n disabled?: boolean;\n size?: 'small' | 'middle' | 'large';\n /**\n * Syntactic sugar for a `(color, variant)` pair. **Will follow `variant` and\n * `color` if provided** — `type` only fills in the axes you leave unset.\n *\n * | `type` | equivalent to |\n * | --------- | -------------------------------------- |\n * | `primary` | `color=\"primary\" variant=\"solid\"` |\n * | `default` | `color=\"default\" variant=\"outlined\"` |\n * | `dashed` | `color=\"default\" variant=\"dashed\"` |\n * | `text` | `color=\"default\" variant=\"text\"` |\n * | `link` | `color=\"primary\" variant=\"link\"` |\n *\n * So `<Button type=\"primary\">` is the same as\n * `<Button color=\"primary\" variant=\"solid\">`, and\n * `<Button type=\"primary\" variant=\"filled\">` keeps the primary color but\n * renders filled. `danger` still overrides the resolved color.\n */\n type?: 'primary' | 'default' | 'dashed' | 'text' | 'link';\n /**\n * Visual fill style, independent of `color`. Defaults to `'outlined'`.\n * Takes precedence over whatever `type` would have supplied.\n */\n variant?: 'solid' | 'outlined' | 'dashed' | 'filled' | 'text' | 'link';\n /**\n * Native `<button>` `type` (`button`/`submit`/`reset`), kept separate\n * from the antd-style `type` above. Defaults to `'button'` so a Button\n * placed inside a `<form>` doesn't submit it unless explicitly opted in.\n */\n htmlType?: 'button' | 'submit' | 'reset';\n shape?: 'default' | 'circle' | 'round';\n /**\n * Color, independent of `variant`. Defaults to `'default'`, or to whatever\n * `type` maps to when `type` is set. `danger` overrides this.\n */\n color?: PresetColor | 'default' | 'primary' | 'danger';\n loading?: boolean | { icon: React.ReactNode };\n}\n\n// Chassis absorbed from core/button's cva base (#278 ③ / #294 Phase 4). The\n// shared focus/aria set lives in INTERACTIVE_CHASSIS; a Button additionally\n// disables and sizes its icons. Kept as constants so the absorb relocates this\n// treatment rather than dropping it (the #177 class of silent regression the\n// #300/#301 net guards).\nconst BUTTON_CHASSIS = cn(\n INTERACTIVE_CHASSIS,\n 'outline-none',\n 'disabled:pointer-events-none disabled:opacity-50',\n \"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n);\n\n// Presentation the atom builds on. The size/variant/shape classes below still\n// override the incidental leftovers exactly as they did when this wrapped\n// core/button, so the rendered output is unchanged (verified against a\n// pre-refactor render matrix).\nconst BUTTON_BASE = cn(\n BUTTON_CHASSIS,\n 'gap-2 whitespace-nowrap font-medium transition-all',\n 'has-[>svg]:px-3',\n);\n\nconst variantClasses: Record<string, string> = {\n // solid used to inherit its fill from core/button's default variant; carry it\n // explicitly now that the primitive is absorbed. A colored solid overrides\n // this with the --btn-* custom properties further down.\n solid: cn('bg-primary text-primary-foreground', 'hover:bg-primary/90'),\n outlined: cn(\n 'border border-[color-mix(in_oklch,var(--btn-border),transparent_50%)]',\n 'bg-background text-foreground',\n 'hover:bg-accent',\n ),\n dashed: cn(\n 'border border-dashed border-[color-mix(in_oklch,var(--btn-border),transparent_50%)]',\n 'bg-background text-foreground',\n 'hover:bg-accent',\n ),\n filled: cn('bg-muted text-foreground', 'hover:bg-muted/80'),\n text: cn('bg-transparent text-foreground', 'hover:bg-accent'),\n link: cn(\n 'bg-transparent text-primary',\n 'underline-offset-4 hover:underline',\n 'hover:bg-primary/10',\n ),\n};\n\n/**\n * antd-style syntactic sugar: each `type` expands to a `(color, variant)` pair.\n * Explicit `color`/`variant` props win over whatever the `type` maps to, so\n * `type` only fills in the axes the caller left unset.\n *\n * Mirrors antd: `<Button type=\"primary\">` ≡ `<Button color=\"primary\" variant=\"solid\">`.\n */\nconst typeToColorVariant: Record<\n NonNullable<Props['type']>,\n { color: NonNullable<Props['color']>; variant: NonNullable<Props['variant']> }\n> = {\n primary: { color: 'primary', variant: 'solid' },\n default: { color: 'default', variant: 'outlined' },\n dashed: { color: 'default', variant: 'dashed' },\n text: { color: 'default', variant: 'text' },\n link: { color: 'primary', variant: 'link' },\n};\n\nconst sizesClasses: Record<string, string> = {\n small: 'h-6 px-3 text-sm',\n middle: 'h-8 px-4 text-base',\n large: 'h-10 px-5 text-lg',\n};\n\nconst iconClasses: Record<string, string> = {\n small: `size-6 [&_svg:not([class*='size-'])]:size-3`,\n middle: `size-8 [&_svg:not([class*='size-'])]:size-4`,\n large: `size-10 [&_svg:not([class*='size-'])]:size-5`,\n};\n\nconst shapesClasses = {\n default: 'rounded-sm',\n circle: 'rounded-full',\n round: 'rounded-2xl',\n};\n\nconst Button = ({\n icon,\n className,\n type,\n variant,\n htmlType = 'button',\n size,\n color,\n shape,\n block = false,\n disabled,\n loading,\n danger,\n asChild = false,\n children,\n onMouseDown,\n ...props\n}: Props) => {\n const { componentSize, defaultProps } = useConfig();\n const buttonDefaults = defaultProps?.button as\n Partial<Pick<Props, 'shape' | 'variant' | 'color'>> | undefined;\n const resolvedSize = size ?? componentSize ?? 'middle';\n const resolvedShape = shape ?? buttonDefaults?.shape ?? 'default';\n const iconOnly = icon && !children;\n // `type` is syntactic sugar that expands to a (color, variant) pair; explicit\n // `color`/`variant` win over it, so `type` only fills the axes left unset.\n // A `Config` default sits below the call-site `type` (which is more specific)\n // but above the library built-in. `danger` still trumps everything, matching\n // antd. Defaults live here rather than on the parameters so `type` gets a\n // chance to supply them first.\n const typeDefaults = type ? typeToColorVariant[type] : undefined;\n const resolvedVariant =\n variant ?? typeDefaults?.variant ?? buttonDefaults?.variant ?? 'outlined';\n const computedColor = danger\n ? 'danger'\n : (color ?? typeDefaults?.color ?? buttonDefaults?.color ?? 'default');\n const colored = computedColor !== 'default';\n const isLoading = !!loading;\n\n const Comp = asChild ? Slot : 'button';\n\n const displayIcon = loading ? (\n typeof loading === 'object' ? (\n loading.icon\n ) : (\n <LoaderCircle className=\"animate-spin\" />\n )\n ) : (\n icon\n );\n\n return (\n <Comp\n data-slot=\"button\"\n className={cn(\n BUTTON_BASE,\n 'inline-flex items-center justify-center gap-x-2',\n 'rounded-lg',\n 'cursor-pointer',\n 'h-auto py-0',\n 'transition-all',\n variantClasses[resolvedVariant],\n sizesClasses[resolvedSize],\n iconOnly && ['p-0', iconClasses[resolvedSize]],\n shapesClasses[resolvedShape],\n block && 'w-full',\n colored &&\n (resolvedVariant === 'solid'\n ? [\n 'bg-(--btn-bg)',\n 'hover:bg-(--btn-bg-hover)',\n 'active:bg-(--btn-bg-active)',\n 'text-(--btn-fg)',\n 'border-(--btn-border)',\n ]\n : [\n 'text-(--btn-bg)',\n 'border-[color-mix(in_oklch,var(--btn-border),transparent_50%)]',\n //\n ]),\n className,\n //\n )}\n type={htmlType}\n disabled={disabled || isLoading}\n aria-busy={isLoading}\n data-color={computedColor}\n onMouseDown={e => {\n onMouseDown?.(e);\n }}\n {...props}\n >\n {displayIcon}\n {children}\n </Comp>\n );\n};\n\nexport default Button;\n"],"mappings":";;;;;;;;;AAqEA,MAAM,iBAAiB,GACrB,qBACA,gBACA,oDACA,mFACF;AAMA,MAAM,cAAc,GAClB,gBACA,sDACA,iBACF;AAEA,MAAM,iBAAyC;CAI7C,OAAO,GAAG,sCAAsC,qBAAqB;CACrE,UAAU,GACR,yEACA,iCACA,iBACF;CACA,QAAQ,GACN,uFACA,iCACA,iBACF;CACA,QAAQ,GAAG,4BAA4B,mBAAmB;CAC1D,MAAM,GAAG,kCAAkC,iBAAiB;CAC5D,MAAM,GACJ,+BACA,sCACA,qBACF;AACF;;;;;;;;AASA,MAAM,qBAGF;CACF,SAAS;EAAE,OAAO;EAAW,SAAS;CAAQ;CAC9C,SAAS;EAAE,OAAO;EAAW,SAAS;CAAW;CACjD,QAAQ;EAAE,OAAO;EAAW,SAAS;CAAS;CAC9C,MAAM;EAAE,OAAO;EAAW,SAAS;CAAO;CAC1C,MAAM;EAAE,OAAO;EAAW,SAAS;CAAO;AAC5C;AAEA,MAAM,eAAuC;CAC3C,OAAO;CACP,QAAQ;CACR,OAAO;AACT;AAEA,MAAM,cAAsC;CAC1C,OAAO;CACP,QAAQ;CACR,OAAO;AACT;AAEA,MAAM,gBAAgB;CACpB,SAAS;CACT,QAAQ;CACR,OAAO;AACT;AAEA,MAAM,UAAU,EACd,MACA,WACA,MACA,SACA,WAAW,UACX,MACA,OACA,OACA,QAAQ,OACR,UACA,SACA,QACA,UAAU,OACV,UACA,aACA,GAAG,YACQ;CACX,MAAM,EAAE,eAAe,iBAAiB,UAAU;CAClD,MAAM,iBAAiB,cAAc;CAErC,MAAM,eAAe,QAAQ,iBAAiB;CAC9C,MAAM,gBAAgB,SAAS,gBAAgB,SAAS;CACxD,MAAM,WAAW,QAAQ,CAAC;CAO1B,MAAM,eAAe,OAAO,mBAAmB,QAAQ,KAAA;CACvD,MAAM,kBACJ,WAAW,cAAc,WAAW,gBAAgB,WAAW;CACjE,MAAM,gBAAgB,SAClB,WACC,SAAS,cAAc,SAAS,gBAAgB,SAAS;CAC9D,MAAM,UAAU,kBAAkB;CAClC,MAAM,YAAY,CAAC,CAAC;CAEpB,MAAM,OAAO,UAAU,OAAO;CAE9B,MAAM,cAAc,UAClB,OAAO,YAAY,WACjB,QAAQ,OAER,oBAAC,cAAD,EAAc,WAAU,eAAgB,CAAA,IAG1C;CAGF,OACE,qBAAC,MAAD;EACE,aAAU;EACV,WAAW,GACT,aACA,mDACA,cACA,kBACA,eACA,kBACA,eAAe,kBACf,aAAa,eACb,YAAY,CAAC,OAAO,YAAY,aAAa,GAC7C,cAAc,gBACd,SAAS,UACT,YACG,oBAAoB,UACjB;GACE;GACA;GACA;GACA;GACA;EACF,IACA,CACE,mBACA,gEAEF,IACN,SAEF;EACA,MAAM;EACN,UAAU,YAAY;EACtB,aAAW;EACX,cAAY;EACZ,cAAa,MAAK;GAChB,cAAc,CAAC;EACjB;EACA,GAAI;EAtCN,UAAA,CAwCG,aACA,QACG;;AAEV"}
|
|
1
|
+
{"version":3,"file":"Button.mjs","names":[],"sources":["../src/components/atoms/button.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\n\nimport { Slot } from '@radix-ui/react-slot';\nimport { LoaderCircle } from 'lucide-react';\n\nimport { useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport { INTERACTIVE_CHASSIS } from '../../lib/chassis';\nimport { type DeprecatedPresetColor, type PresetColor } from '../../lib/colors';\n\ntype NativeButtonProps = React.ComponentProps<'button'> & {\n asChild?: boolean;\n};\n\nexport interface Props extends Omit<\n NativeButtonProps,\n 'size' | 'variant' | 'type'\n> {\n icon?: React.ReactNode;\n block?: boolean;\n danger?: boolean;\n disabled?: boolean;\n size?: 'small' | 'middle' | 'large';\n /**\n * Syntactic sugar for a `(color, variant)` pair. **Will follow `variant` and\n * `color` if provided** — `type` only fills in the axes you leave unset.\n *\n * | `type` | equivalent to |\n * | --------- | -------------------------------------- |\n * | `primary` | `color=\"primary\" variant=\"solid\"` |\n * | `default` | `color=\"default\" variant=\"outlined\"` |\n * | `dashed` | `color=\"default\" variant=\"dashed\"` |\n * | `text` | `color=\"default\" variant=\"text\"` |\n * | `link` | `color=\"primary\" variant=\"link\"` |\n *\n * So `<Button type=\"primary\">` is the same as\n * `<Button color=\"primary\" variant=\"solid\">`, and\n * `<Button type=\"primary\" variant=\"filled\">` keeps the primary color but\n * renders filled. `danger` still overrides the resolved color.\n */\n type?: 'primary' | 'default' | 'dashed' | 'text' | 'link';\n /**\n * Visual fill style, independent of `color`. Defaults to `'outlined'`.\n * Takes precedence over whatever `type` would have supplied.\n */\n variant?: 'solid' | 'outlined' | 'dashed' | 'filled' | 'text' | 'link';\n /**\n * Native `<button>` `type` (`button`/`submit`/`reset`), kept separate\n * from the antd-style `type` above. Defaults to `'button'` so a Button\n * placed inside a `<form>` doesn't submit it unless explicitly opted in.\n */\n htmlType?: 'button' | 'submit' | 'reset';\n shape?: 'default' | 'circle' | 'round';\n /**\n * Color, independent of `variant`. Defaults to `'default'`, or to whatever\n * `type` maps to when `type` is set. `danger` overrides this.\n *\n * Preset hues use Tailwind's colour names (#342). The pre-8.0 spellings\n * `magenta`/`geekblue`/`gold`/`volcano` still work and render unchanged, but\n * are deprecated — use `fuchsia`/`indigo`/`amber` instead (`volcano` has no\n * successor; it is `orange` one lightness step darker).\n */\n color?:\n PresetColor | DeprecatedPresetColor | 'default' | 'primary' | 'danger';\n loading?: boolean | { icon: React.ReactNode };\n}\n\n// Chassis absorbed from core/button's cva base (#278 ③ / #294 Phase 4). The\n// shared focus/aria set lives in INTERACTIVE_CHASSIS; a Button additionally\n// disables and sizes its icons. Kept as constants so the absorb relocates this\n// treatment rather than dropping it (the #177 class of silent regression the\n// #300/#301 net guards).\nconst BUTTON_CHASSIS = cn(\n INTERACTIVE_CHASSIS,\n 'outline-none',\n 'disabled:pointer-events-none disabled:opacity-50',\n \"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n);\n\n// Presentation the atom builds on. The size/variant/shape classes below still\n// override the incidental leftovers exactly as they did when this wrapped\n// core/button, so the rendered output is unchanged (verified against a\n// pre-refactor render matrix).\nconst BUTTON_BASE = cn(\n BUTTON_CHASSIS,\n 'gap-2 whitespace-nowrap font-medium transition-all',\n 'has-[>svg]:px-3',\n);\n\nconst variantClasses: Record<string, string> = {\n // solid used to inherit its fill from core/button's default variant; carry it\n // explicitly now that the primitive is absorbed. A colored solid overrides\n // this with the --btn-* custom properties further down.\n solid: cn('bg-primary text-primary-foreground', 'hover:bg-primary/90'),\n outlined: cn(\n 'border border-[color-mix(in_oklch,var(--btn-border),transparent_50%)]',\n 'bg-background text-foreground',\n 'hover:bg-accent',\n ),\n dashed: cn(\n 'border border-dashed border-[color-mix(in_oklch,var(--btn-border),transparent_50%)]',\n 'bg-background text-foreground',\n 'hover:bg-accent',\n ),\n filled: cn('bg-muted text-foreground', 'hover:bg-muted/80'),\n text: cn('bg-transparent text-foreground', 'hover:bg-accent'),\n link: cn(\n 'bg-transparent text-primary',\n 'underline-offset-4 hover:underline',\n 'hover:bg-primary/10',\n ),\n};\n\n/**\n * antd-style syntactic sugar: each `type` expands to a `(color, variant)` pair.\n * Explicit `color`/`variant` props win over whatever the `type` maps to, so\n * `type` only fills in the axes the caller left unset.\n *\n * Mirrors antd: `<Button type=\"primary\">` ≡ `<Button color=\"primary\" variant=\"solid\">`.\n */\nconst typeToColorVariant: Record<\n NonNullable<Props['type']>,\n { color: NonNullable<Props['color']>; variant: NonNullable<Props['variant']> }\n> = {\n primary: { color: 'primary', variant: 'solid' },\n default: { color: 'default', variant: 'outlined' },\n dashed: { color: 'default', variant: 'dashed' },\n text: { color: 'default', variant: 'text' },\n link: { color: 'primary', variant: 'link' },\n};\n\nconst sizesClasses: Record<string, string> = {\n small: 'h-6 px-3 text-sm',\n middle: 'h-8 px-4 text-base',\n large: 'h-10 px-5 text-lg',\n};\n\nconst iconClasses: Record<string, string> = {\n small: `size-6 [&_svg:not([class*='size-'])]:size-3`,\n middle: `size-8 [&_svg:not([class*='size-'])]:size-4`,\n large: `size-10 [&_svg:not([class*='size-'])]:size-5`,\n};\n\nconst shapesClasses = {\n default: 'rounded-sm',\n circle: 'rounded-full',\n round: 'rounded-2xl',\n};\n\nconst Button = ({\n icon,\n className,\n type,\n variant,\n htmlType = 'button',\n size,\n color,\n shape,\n block = false,\n disabled,\n loading,\n danger,\n asChild = false,\n children,\n onMouseDown,\n ...props\n}: Props) => {\n const { componentSize, defaultProps } = useConfig();\n const buttonDefaults = defaultProps?.button as\n Partial<Pick<Props, 'shape' | 'variant' | 'color'>> | undefined;\n const resolvedSize = size ?? componentSize ?? 'middle';\n const resolvedShape = shape ?? buttonDefaults?.shape ?? 'default';\n const iconOnly = icon && !children;\n // `type` is syntactic sugar that expands to a (color, variant) pair; explicit\n // `color`/`variant` win over it, so `type` only fills the axes left unset.\n // A `Config` default sits below the call-site `type` (which is more specific)\n // but above the library built-in. `danger` still trumps everything, matching\n // antd. Defaults live here rather than on the parameters so `type` gets a\n // chance to supply them first.\n const typeDefaults = type ? typeToColorVariant[type] : undefined;\n const resolvedVariant =\n variant ?? typeDefaults?.variant ?? buttonDefaults?.variant ?? 'outlined';\n const computedColor = danger\n ? 'danger'\n : (color ?? typeDefaults?.color ?? buttonDefaults?.color ?? 'default');\n const colored = computedColor !== 'default';\n const isLoading = !!loading;\n\n const Comp = asChild ? Slot : 'button';\n\n const displayIcon = loading ? (\n typeof loading === 'object' ? (\n loading.icon\n ) : (\n <LoaderCircle className=\"animate-spin\" />\n )\n ) : (\n icon\n );\n\n return (\n <Comp\n data-slot=\"button\"\n className={cn(\n BUTTON_BASE,\n 'inline-flex items-center justify-center gap-x-2',\n 'rounded-lg',\n 'cursor-pointer',\n 'h-auto py-0',\n 'transition-all',\n variantClasses[resolvedVariant],\n sizesClasses[resolvedSize],\n iconOnly && ['p-0', iconClasses[resolvedSize]],\n shapesClasses[resolvedShape],\n block && 'w-full',\n colored &&\n (resolvedVariant === 'solid'\n ? [\n 'bg-(--btn-bg)',\n 'hover:bg-(--btn-bg-hover)',\n 'active:bg-(--btn-bg-active)',\n 'text-(--btn-fg)',\n 'border-(--btn-border)',\n ]\n : [\n 'text-(--btn-bg)',\n 'border-[color-mix(in_oklch,var(--btn-border),transparent_50%)]',\n //\n ]),\n className,\n //\n )}\n type={htmlType}\n disabled={disabled || isLoading}\n aria-busy={isLoading}\n data-color={computedColor}\n onMouseDown={e => {\n onMouseDown?.(e);\n }}\n {...props}\n >\n {displayIcon}\n {children}\n </Comp>\n );\n};\n\nexport default Button;\n"],"mappings":";;;;;;;;;AA2EA,MAAM,iBAAiB,GACrB,qBACA,gBACA,oDACA,mFACF;AAMA,MAAM,cAAc,GAClB,gBACA,sDACA,iBACF;AAEA,MAAM,iBAAyC;CAI7C,OAAO,GAAG,sCAAsC,qBAAqB;CACrE,UAAU,GACR,yEACA,iCACA,iBACF;CACA,QAAQ,GACN,uFACA,iCACA,iBACF;CACA,QAAQ,GAAG,4BAA4B,mBAAmB;CAC1D,MAAM,GAAG,kCAAkC,iBAAiB;CAC5D,MAAM,GACJ,+BACA,sCACA,qBACF;AACF;;;;;;;;AASA,MAAM,qBAGF;CACF,SAAS;EAAE,OAAO;EAAW,SAAS;CAAQ;CAC9C,SAAS;EAAE,OAAO;EAAW,SAAS;CAAW;CACjD,QAAQ;EAAE,OAAO;EAAW,SAAS;CAAS;CAC9C,MAAM;EAAE,OAAO;EAAW,SAAS;CAAO;CAC1C,MAAM;EAAE,OAAO;EAAW,SAAS;CAAO;AAC5C;AAEA,MAAM,eAAuC;CAC3C,OAAO;CACP,QAAQ;CACR,OAAO;AACT;AAEA,MAAM,cAAsC;CAC1C,OAAO;CACP,QAAQ;CACR,OAAO;AACT;AAEA,MAAM,gBAAgB;CACpB,SAAS;CACT,QAAQ;CACR,OAAO;AACT;AAEA,MAAM,UAAU,EACd,MACA,WACA,MACA,SACA,WAAW,UACX,MACA,OACA,OACA,QAAQ,OACR,UACA,SACA,QACA,UAAU,OACV,UACA,aACA,GAAG,YACQ;CACX,MAAM,EAAE,eAAe,iBAAiB,UAAU;CAClD,MAAM,iBAAiB,cAAc;CAErC,MAAM,eAAe,QAAQ,iBAAiB;CAC9C,MAAM,gBAAgB,SAAS,gBAAgB,SAAS;CACxD,MAAM,WAAW,QAAQ,CAAC;CAO1B,MAAM,eAAe,OAAO,mBAAmB,QAAQ,KAAA;CACvD,MAAM,kBACJ,WAAW,cAAc,WAAW,gBAAgB,WAAW;CACjE,MAAM,gBAAgB,SAClB,WACC,SAAS,cAAc,SAAS,gBAAgB,SAAS;CAC9D,MAAM,UAAU,kBAAkB;CAClC,MAAM,YAAY,CAAC,CAAC;CAEpB,MAAM,OAAO,UAAU,OAAO;CAE9B,MAAM,cAAc,UAClB,OAAO,YAAY,WACjB,QAAQ,OAER,oBAAC,cAAD,EAAc,WAAU,eAAgB,CAAA,IAG1C;CAGF,OACE,qBAAC,MAAD;EACE,aAAU;EACV,WAAW,GACT,aACA,mDACA,cACA,kBACA,eACA,kBACA,eAAe,kBACf,aAAa,eACb,YAAY,CAAC,OAAO,YAAY,aAAa,GAC7C,cAAc,gBACd,SAAS,UACT,YACG,oBAAoB,UACjB;GACE;GACA;GACA;GACA;GACA;EACF,IACA,CACE,mBACA,gEAEF,IACN,SAEF;EACA,MAAM;EACN,UAAU,YAAY;EACtB,aAAW;EACX,cAAY;EACZ,cAAa,MAAK;GAChB,cAAc,CAAC;EACjB;EACA,GAAI;EAtCN,UAAA,CAwCG,aACA,QACG;;AAEV"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import CodeMirror, { Extension } from "@uiw/react-codemirror";
|
|
2
|
+
//#region src/components/atoms/code-editor.d.ts
|
|
3
|
+
interface Props extends Omit<React.ComponentProps<typeof CodeMirror>, 'value' | 'extensions' | 'onChange' | 'ref' | 'theme'> {
|
|
4
|
+
value: string;
|
|
5
|
+
/** Passed through to CodeMirror; `'100%'` fills the wrapper. */
|
|
6
|
+
height?: string;
|
|
7
|
+
/**
|
|
8
|
+
* `'light'`/`'dark'` pick the VSCode theme pair, `'auto'` (the default)
|
|
9
|
+
* follows the nearest `Config`'s `theme.dark`, and an `Extension` is any
|
|
10
|
+
* CodeMirror theme of your own.
|
|
11
|
+
*
|
|
12
|
+
* This narrows CodeMirror's own meaning for two of the strings — upstream,
|
|
13
|
+
* `'light'`/`'dark'` are *its* built-in themes — so that the two named
|
|
14
|
+
* options match what the component actually renders by default. `'none'`
|
|
15
|
+
* still means what it does upstream: no theme extension, style it yourself.
|
|
16
|
+
*/
|
|
17
|
+
theme?: 'light' | 'dark' | 'auto' | 'none' | Extension;
|
|
18
|
+
/** Appended after the built-in JS/TS + line-wrapping extensions. */
|
|
19
|
+
extensions?: Extension[];
|
|
20
|
+
/**
|
|
21
|
+
* Renders a read-through unified diff against `original` instead of a plain
|
|
22
|
+
* document. `mergeControls` defaults to `false` (display-only review, no
|
|
23
|
+
* accept/reject gutters).
|
|
24
|
+
*/
|
|
25
|
+
diff?: {
|
|
26
|
+
original: string;
|
|
27
|
+
mergeControls?: boolean;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Formatter run on the Cmd/Ctrl+S value before it is written back. Omitted,
|
|
31
|
+
* the shortcut still fires `onSave` — it just doesn't reformat. Kept as an
|
|
32
|
+
* injected function so this package carries no formatter dependency of its
|
|
33
|
+
* own (prettier is ~9.6 MB); pass the one your app already owns.
|
|
34
|
+
*/
|
|
35
|
+
formatCode?: (code: string) => Promise<string>;
|
|
36
|
+
className?: string;
|
|
37
|
+
onChange?: (value: string) => void;
|
|
38
|
+
/** Fires on Cmd/Ctrl+S with the value that was written back. */
|
|
39
|
+
onSave?: (value: string) => void;
|
|
40
|
+
/**
|
|
41
|
+
* `null` on a clean save; a string carries the message a failed `formatCode`
|
|
42
|
+
* threw. Named `onFormatError` rather than `onError` because the base props
|
|
43
|
+
* already carry the DOM `onError` handler, and silently shadowing it would
|
|
44
|
+
* be worse than the extra word.
|
|
45
|
+
*/
|
|
46
|
+
onFormatError?: (error: string | null) => void;
|
|
47
|
+
}
|
|
48
|
+
declare const CodeEditor: ({ value, theme, height, className, extensions: extraExtensions, diff, formatCode, onChange, onSave, onFormatError, ...props }: Props) => import("react").JSX.Element;
|
|
49
|
+
//#endregion
|
|
50
|
+
export { Props, CodeEditor as default };
|
|
51
|
+
//# sourceMappingURL=CodeEditor.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CodeEditor.d.mts","names":[],"sources":["../src/components/atoms/code-editor.tsx"],"mappings":";;UA2BiB,cAAc,KAC7B,MAAM,sBAAsB;EAG5B;;EAEA;;;;;;;;;;;EAWA,6CAA6C;;EAE7C,aAAa;;;;;;EAMb;IACE;IACA;;;;;;;;EAQF,cAAc,iBAAiB;EAC/B;EACA,YAAY;;EAEZ,UAAU;;;;;;;EAOV,iBAAiB;;cAGb,eAAc,OAAA,OAAA,QAAA,WAAA,YAAA,iBAAA,MAAA,YAAA,UAAA,QAAA,kBAAA,SAYjB,0BAAK,IAAA"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "./utils.mjs";
|
|
3
|
+
import { useIsDarkActive } from "./providers/config/dark-mode.mjs";
|
|
4
|
+
import { useCallback, useMemo, useRef } from "react";
|
|
5
|
+
import { jsx } from "react/jsx-runtime";
|
|
6
|
+
import { javascript } from "@codemirror/lang-javascript";
|
|
7
|
+
import { unifiedMergeView } from "@codemirror/merge";
|
|
8
|
+
import { vscodeDark, vscodeLight } from "@uiw/codemirror-theme-vscode";
|
|
9
|
+
import CodeMirror from "@uiw/react-codemirror";
|
|
10
|
+
import { EditorView } from "codemirror";
|
|
11
|
+
//#region src/components/atoms/code-editor.tsx
|
|
12
|
+
const CodeEditor = ({ value, theme = "auto", height, className, extensions: extraExtensions, diff, formatCode, onChange, onSave, onFormatError, ...props }) => {
|
|
13
|
+
const editorRef = useRef(null);
|
|
14
|
+
const isDarkActive = useIsDarkActive();
|
|
15
|
+
const resolvedTheme = useMemo(() => {
|
|
16
|
+
if (theme === "auto") return isDarkActive ? vscodeDark : vscodeLight;
|
|
17
|
+
if (theme === "light") return vscodeLight;
|
|
18
|
+
if (theme === "dark") return vscodeDark;
|
|
19
|
+
return theme;
|
|
20
|
+
}, [theme, isDarkActive]);
|
|
21
|
+
const extensions = useMemo(() => {
|
|
22
|
+
const base = [javascript({
|
|
23
|
+
jsx: true,
|
|
24
|
+
typescript: true
|
|
25
|
+
}), EditorView.lineWrapping];
|
|
26
|
+
if (diff) base.push(unifiedMergeView({
|
|
27
|
+
original: diff.original,
|
|
28
|
+
mergeControls: diff.mergeControls ?? false
|
|
29
|
+
}));
|
|
30
|
+
return [...base, ...extraExtensions ?? []];
|
|
31
|
+
}, [diff, extraExtensions]);
|
|
32
|
+
const save = useCallback(async (val) => {
|
|
33
|
+
const view = editorRef.current?.view;
|
|
34
|
+
if (!view) return;
|
|
35
|
+
try {
|
|
36
|
+
const currentLength = view.state.doc.length;
|
|
37
|
+
const cursorPos = view.state.selection.main.head;
|
|
38
|
+
const next = formatCode ? await formatCode(val) : val;
|
|
39
|
+
if (currentLength !== view.state.doc.length) {
|
|
40
|
+
onFormatError?.(null);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (next !== val) view.dispatch(view.state.update({
|
|
44
|
+
changes: {
|
|
45
|
+
from: 0,
|
|
46
|
+
to: currentLength,
|
|
47
|
+
insert: next
|
|
48
|
+
},
|
|
49
|
+
selection: { anchor: Math.min(cursorPos, next.length) }
|
|
50
|
+
}));
|
|
51
|
+
onChange?.(next);
|
|
52
|
+
onSave?.(next);
|
|
53
|
+
onFormatError?.(null);
|
|
54
|
+
} catch (e) {
|
|
55
|
+
onFormatError?.(e instanceof Error ? e.message : String(e));
|
|
56
|
+
}
|
|
57
|
+
}, [
|
|
58
|
+
formatCode,
|
|
59
|
+
onChange,
|
|
60
|
+
onSave,
|
|
61
|
+
onFormatError
|
|
62
|
+
]);
|
|
63
|
+
const onKeyDown = useCallback((e) => {
|
|
64
|
+
if ((e.metaKey || e.ctrlKey) && e.key === "s") {
|
|
65
|
+
e.preventDefault();
|
|
66
|
+
save(editorRef.current?.view?.state.doc.toString() ?? value);
|
|
67
|
+
}
|
|
68
|
+
}, [save, value]);
|
|
69
|
+
return /* @__PURE__ */ jsx("div", {
|
|
70
|
+
"data-slot": "code-editor",
|
|
71
|
+
className: cn(className),
|
|
72
|
+
onKeyDown,
|
|
73
|
+
children: /* @__PURE__ */ jsx(CodeMirror, {
|
|
74
|
+
ref: editorRef,
|
|
75
|
+
theme: resolvedTheme,
|
|
76
|
+
height: height || "100%",
|
|
77
|
+
value,
|
|
78
|
+
extensions,
|
|
79
|
+
onChange,
|
|
80
|
+
...props
|
|
81
|
+
})
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
//#endregion
|
|
85
|
+
export { CodeEditor as default };
|
|
86
|
+
|
|
87
|
+
//# sourceMappingURL=CodeEditor.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CodeEditor.mjs","names":[],"sources":["../src/components/atoms/code-editor.tsx"],"sourcesContent":["'use client';\n\n// Subpath-only component (`@jbpark/ui-kit/CodeEditor`). Deliberately NOT\n// re-exported from `atoms/index.ts` — CodeMirror is an *optional* peer\n// dependency, and an ESM re-export from the root barrel is eager, so\n// `import { Button } from '@jbpark/ui-kit'` would fail to resolve for every\n// consumer that never asked for an editor. Keeping it behind its own subpath\n// is what makes the peer set genuinely opt-in (#346).\nimport { useCallback, useMemo, useRef } from 'react';\n\nimport { javascript } from '@codemirror/lang-javascript';\nimport { unifiedMergeView } from '@codemirror/merge';\nimport { vscodeDark, vscodeLight } from '@uiw/codemirror-theme-vscode';\nimport CodeMirror, {\n type Extension,\n type ReactCodeMirrorRef,\n} from '@uiw/react-codemirror';\nimport { EditorView } from 'codemirror';\n\nimport { cn } from '@repo/ui/utils';\n\n// Relative rather than `@repo/ui/providers`, because this hook is deliberately\n// absent from that barrel — it is a published subpath, and the hook is internal.\n// Both specifiers resolve to the same files, so there is still one Config\n// context.\nimport { useIsDarkActive } from '../../providers/config';\n\nexport interface Props extends Omit<\n React.ComponentProps<typeof CodeMirror>,\n 'value' | 'extensions' | 'onChange' | 'ref' | 'theme'\n> {\n value: string;\n /** Passed through to CodeMirror; `'100%'` fills the wrapper. */\n height?: string;\n /**\n * `'light'`/`'dark'` pick the VSCode theme pair, `'auto'` (the default)\n * follows the nearest `Config`'s `theme.dark`, and an `Extension` is any\n * CodeMirror theme of your own.\n *\n * This narrows CodeMirror's own meaning for two of the strings — upstream,\n * `'light'`/`'dark'` are *its* built-in themes — so that the two named\n * options match what the component actually renders by default. `'none'`\n * still means what it does upstream: no theme extension, style it yourself.\n */\n theme?: 'light' | 'dark' | 'auto' | 'none' | Extension;\n /** Appended after the built-in JS/TS + line-wrapping extensions. */\n extensions?: Extension[];\n /**\n * Renders a read-through unified diff against `original` instead of a plain\n * document. `mergeControls` defaults to `false` (display-only review, no\n * accept/reject gutters).\n */\n diff?: {\n original: string;\n mergeControls?: boolean;\n };\n /**\n * Formatter run on the Cmd/Ctrl+S value before it is written back. Omitted,\n * the shortcut still fires `onSave` — it just doesn't reformat. Kept as an\n * injected function so this package carries no formatter dependency of its\n * own (prettier is ~9.6 MB); pass the one your app already owns.\n */\n formatCode?: (code: string) => Promise<string>;\n className?: string;\n onChange?: (value: string) => void;\n /** Fires on Cmd/Ctrl+S with the value that was written back. */\n onSave?: (value: string) => void;\n /**\n * `null` on a clean save; a string carries the message a failed `formatCode`\n * threw. Named `onFormatError` rather than `onError` because the base props\n * already carry the DOM `onError` handler, and silently shadowing it would\n * be worse than the extra word.\n */\n onFormatError?: (error: string | null) => void;\n}\n\nconst CodeEditor = ({\n value,\n theme = 'auto',\n height,\n className,\n extensions: extraExtensions,\n diff,\n formatCode,\n onChange,\n onSave,\n onFormatError,\n ...props\n}: Props) => {\n const editorRef = useRef<ReactCodeMirrorRef>(null);\n const isDarkActive = useIsDarkActive();\n\n const resolvedTheme = useMemo(() => {\n if (theme === 'auto') {\n return isDarkActive ? vscodeDark : vscodeLight;\n }\n if (theme === 'light') {\n return vscodeLight;\n }\n if (theme === 'dark') {\n return vscodeDark;\n }\n\n // `'none'` and a caller-supplied Extension both mean what they mean\n // upstream, so hand them straight to CodeMirror.\n return theme;\n }, [theme, isDarkActive]);\n\n const extensions = useMemo(() => {\n const base: Extension[] = [\n javascript({ jsx: true, typescript: true }),\n EditorView.lineWrapping,\n ];\n\n if (diff) {\n base.push(\n unifiedMergeView({\n original: diff.original,\n mergeControls: diff.mergeControls ?? false,\n }),\n );\n }\n\n return [...base, ...(extraExtensions ?? [])];\n }, [diff, extraExtensions]);\n\n const save = useCallback(\n async (val: string) => {\n const view = editorRef.current?.view;\n if (!view) {\n return;\n }\n\n try {\n const currentLength = view.state.doc.length;\n const cursorPos = view.state.selection.main.head;\n const next = formatCode ? await formatCode(val) : val;\n\n // Formatting is async, so the document may have moved on while it ran\n // — writing back then would clobber keystrokes typed in the meantime.\n if (currentLength !== view.state.doc.length) {\n onFormatError?.(null);\n return;\n }\n\n // Skip the transaction when the text is unchanged (the common case\n // without a `formatCode`): replacing the doc with itself would push a\n // pointless undo entry. The callbacks still fire, so a save handler\n // sees every Cmd+S.\n if (next !== val) {\n view.dispatch(\n view.state.update({\n changes: { from: 0, to: currentLength, insert: next },\n selection: { anchor: Math.min(cursorPos, next.length) },\n }),\n );\n }\n\n onChange?.(next);\n onSave?.(next);\n onFormatError?.(null);\n } catch (e) {\n onFormatError?.(e instanceof Error ? e.message : String(e));\n }\n },\n [formatCode, onChange, onSave, onFormatError],\n );\n\n const onKeyDown = useCallback(\n (e: React.KeyboardEvent<HTMLDivElement>) => {\n if ((e.metaKey || e.ctrlKey) && e.key === 's') {\n e.preventDefault();\n save(editorRef.current?.view?.state.doc.toString() ?? value);\n }\n },\n [save, value],\n );\n\n return (\n <div\n data-slot=\"code-editor\"\n className={cn(\n className,\n // ...\n )}\n onKeyDown={onKeyDown}\n >\n <CodeMirror\n ref={editorRef}\n theme={resolvedTheme}\n height={height || '100%'}\n value={value}\n extensions={extensions}\n onChange={onChange}\n {...props}\n />\n </div>\n );\n};\n\nexport default CodeEditor;\n"],"mappings":";;;;;;;;;;;AA4EA,MAAM,cAAc,EAClB,OACA,QAAQ,QACR,QACA,WACA,YAAY,iBACZ,MACA,YACA,UACA,QACA,eACA,GAAG,YACQ;CACX,MAAM,YAAY,OAA2B,IAAI;CACjD,MAAM,eAAe,gBAAgB;CAErC,MAAM,gBAAgB,cAAc;EAClC,IAAI,UAAU,QACZ,OAAO,eAAe,aAAa;EAErC,IAAI,UAAU,SACZ,OAAO;EAET,IAAI,UAAU,QACZ,OAAO;EAKT,OAAO;CACT,GAAG,CAAC,OAAO,YAAY,CAAC;CAExB,MAAM,aAAa,cAAc;EAC/B,MAAM,OAAoB,CACxB,WAAW;GAAE,KAAK;GAAM,YAAY;EAAK,CAAC,GAC1C,WAAW,YACb;EAEA,IAAI,MACF,KAAK,KACH,iBAAiB;GACf,UAAU,KAAK;GACf,eAAe,KAAK,iBAAiB;EACvC,CAAC,CACH;EAGF,OAAO,CAAC,GAAG,MAAM,GAAI,mBAAmB,CAAC,CAAE;CAC7C,GAAG,CAAC,MAAM,eAAe,CAAC;CAE1B,MAAM,OAAO,YACX,OAAO,QAAgB;EACrB,MAAM,OAAO,UAAU,SAAS;EAChC,IAAI,CAAC,MACH;EAGF,IAAI;GACF,MAAM,gBAAgB,KAAK,MAAM,IAAI;GACrC,MAAM,YAAY,KAAK,MAAM,UAAU,KAAK;GAC5C,MAAM,OAAO,aAAa,MAAM,WAAW,GAAG,IAAI;GAIlD,IAAI,kBAAkB,KAAK,MAAM,IAAI,QAAQ;IAC3C,gBAAgB,IAAI;IACpB;GACF;GAMA,IAAI,SAAS,KACX,KAAK,SACH,KAAK,MAAM,OAAO;IAChB,SAAS;KAAE,MAAM;KAAG,IAAI;KAAe,QAAQ;IAAK;IACpD,WAAW,EAAE,QAAQ,KAAK,IAAI,WAAW,KAAK,MAAM,EAAE;GACxD,CAAC,CACH;GAGF,WAAW,IAAI;GACf,SAAS,IAAI;GACb,gBAAgB,IAAI;EACtB,SAAS,GAAG;GACV,gBAAgB,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC;EAC5D;CACF,GACA;EAAC;EAAY;EAAU;EAAQ;CAAa,CAC9C;CAEA,MAAM,YAAY,aACf,MAA2C;EAC1C,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,KAAK;GAC7C,EAAE,eAAe;GACjB,KAAK,UAAU,SAAS,MAAM,MAAM,IAAI,SAAS,KAAK,KAAK;EAC7D;CACF,GACA,CAAC,MAAM,KAAK,CACd;CAEA,OACE,oBAAC,OAAD;EACE,aAAU;EACV,WAAW,GACT,SAEF;EACW;EAEX,UAAA,oBAAC,YAAD;GACE,KAAK;GACL,OAAO;GACP,QAAQ,UAAU;GACX;GACK;GACF;GACV,GAAI;EACL,CAAA;CACE,CAAA;AAET"}
|
package/dist/Tag.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PresetColor } from "./lib/colors.mjs";
|
|
1
|
+
import { DeprecatedPresetColor, PresetColor } from "./lib/colors.mjs";
|
|
2
2
|
import * as React$1 from "react";
|
|
3
3
|
//#region src/components/atoms/tag.d.ts
|
|
4
4
|
type NativeSpanProps = React$1.ComponentProps<'span'> & {
|
|
@@ -14,13 +14,18 @@ interface Props extends Omit<NativeSpanProps, 'variant' | 'color'> {
|
|
|
14
14
|
/**
|
|
15
15
|
* Colour. Every value — the semantic states (`primary`/`success`/`warning`/
|
|
16
16
|
* `danger`) as well as the palette shared with `Button` (`blue`, `red`,
|
|
17
|
-
* `
|
|
17
|
+
* `amber`, …) — resolves through the same `data-color` + `--tag-*` system
|
|
18
18
|
* (#320), so any of them can be re-themed the same way.
|
|
19
19
|
*
|
|
20
20
|
* `success`/`warning` have no `Button` counterpart on purpose: a Tag marks
|
|
21
21
|
* state, which is why antd's Tag also carries them while its Button does not.
|
|
22
|
+
*
|
|
23
|
+
* Preset hues use Tailwind's colour names (#342). The pre-8.0 spellings
|
|
24
|
+
* `magenta`/`geekblue`/`gold`/`volcano` still work and render unchanged, but
|
|
25
|
+
* are deprecated — use `fuchsia`/`indigo`/`amber` instead (`volcano` has no
|
|
26
|
+
* successor; it is `orange` one lightness step darker).
|
|
22
27
|
*/
|
|
23
|
-
color?: 'default' | 'primary' | 'success' | 'warning' | 'danger' | PresetColor;
|
|
28
|
+
color?: 'default' | 'primary' | 'success' | 'warning' | 'danger' | PresetColor | DeprecatedPresetColor;
|
|
24
29
|
}
|
|
25
30
|
declare const Tag: ({ className, variant, color, asChild, children, ...props }: Props) => React$1.JSX.Element;
|
|
26
31
|
//#endregion
|
package/dist/Tag.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tag.d.mts","names":[],"sources":["../src/components/atoms/tag.tsx"],"mappings":";;;KAYK,kBAAkB,QAAM;EAC3B;;UAGe,cAAc,KAAK;;;;;;EAMlC
|
|
1
|
+
{"version":3,"file":"Tag.d.mts","names":[],"sources":["../src/components/atoms/tag.tsx"],"mappings":";;;KAYK,kBAAkB,QAAM;EAC3B;;UAGe,cAAc,KAAK;;;;;;EAMlC;;;;;;;;;;;;;;;EAeA,mEAMI,cACA;;cA+BA,QAAO,WAAA,SAAA,OAAA,SAAA,aAAA,SAOV,UAAK,QAAA,IAAA"}
|
package/dist/Tag.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tag.mjs","names":[],"sources":["../src/components/atoms/tag.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\n\nimport { Slot } from '@radix-ui/react-slot';\n\nimport { useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport { INTERACTIVE_CHASSIS } from '../../lib/chassis';\nimport { type PresetColor } from '../../lib/colors';\n\ntype NativeSpanProps = React.ComponentProps<'span'> & {\n asChild?: boolean;\n};\n\nexport interface Props extends Omit<NativeSpanProps, 'variant' | 'color'> {\n /**\n * Fill style, aligned with `Button`'s vocabulary (#320). The pre-7.0\n * `'default'` spelling was removed — use `'filled'`, which renders\n * identically.\n */\n variant?: 'filled' | 'outlined';\n /**\n * Colour. Every value — the semantic states (`primary`/`success`/`warning`/\n * `danger`) as well as the palette shared with `Button` (`blue`, `red`,\n * `
|
|
1
|
+
{"version":3,"file":"Tag.mjs","names":[],"sources":["../src/components/atoms/tag.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\n\nimport { Slot } from '@radix-ui/react-slot';\n\nimport { useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport { INTERACTIVE_CHASSIS } from '../../lib/chassis';\nimport { type DeprecatedPresetColor, type PresetColor } from '../../lib/colors';\n\ntype NativeSpanProps = React.ComponentProps<'span'> & {\n asChild?: boolean;\n};\n\nexport interface Props extends Omit<NativeSpanProps, 'variant' | 'color'> {\n /**\n * Fill style, aligned with `Button`'s vocabulary (#320). The pre-7.0\n * `'default'` spelling was removed — use `'filled'`, which renders\n * identically.\n */\n variant?: 'filled' | 'outlined';\n /**\n * Colour. Every value — the semantic states (`primary`/`success`/`warning`/\n * `danger`) as well as the palette shared with `Button` (`blue`, `red`,\n * `amber`, …) — resolves through the same `data-color` + `--tag-*` system\n * (#320), so any of them can be re-themed the same way.\n *\n * `success`/`warning` have no `Button` counterpart on purpose: a Tag marks\n * state, which is why antd's Tag also carries them while its Button does not.\n *\n * Preset hues use Tailwind's colour names (#342). The pre-8.0 spellings\n * `magenta`/`geekblue`/`gold`/`volcano` still work and render unchanged, but\n * are deprecated — use `fuchsia`/`indigo`/`amber` instead (`volcano` has no\n * successor; it is `orange` one lightness step darker).\n */\n color?:\n | 'default'\n | 'primary'\n | 'success'\n | 'warning'\n | 'danger'\n | PresetColor\n | DeprecatedPresetColor;\n}\n\n// Base absorbed from core/badge's cva base + its `outline` variant, which Tag\n// always rendered as (#278 ③ / #294 Phase 4). The shared focus/aria/centering\n// set lives in INTERACTIVE_CHASSIS; a Tag adds badge geometry and its svg\n// sizing but — unlike Button — no `disabled:*` (a Tag is not disableable). The\n// rounded/padding leftovers are overridden by the atom classes below exactly as\n// before, so the rendered output is unchanged (verified against a pre-refactor\n// render matrix).\nconst TAG_BASE = cn(\n INTERACTIVE_CHASSIS,\n 'border w-fit whitespace-nowrap overflow-hidden',\n 'text-xs font-medium gap-1 transition-[color,box-shadow]',\n '[&>svg]:size-3 [&>svg]:pointer-events-none',\n 'text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground',\n);\n\n// Every colour — semantic states and shared presets alike — resolves from the\n// `--tag-*` custom properties that `globals.css` sets per `data-color` (#320).\n// Before 7.0 only the presets took this path while the states carried bespoke\n// Tailwind classes, which meant `[data-color]` hooks and `--tag-*` overrides\n// worked on some colours but silently not on others.\n//\n// --tag-bg the hue; the border in `outlined`\n// --tag-fg text colour (defaults to --tag-bg)\n// --tag-tint the fill behind `filled` (defaults to a 10% --tag-bg wash)\nconst fillClasses = cn('border-transparent bg-(--tag-tint) text-(--tag-fg)');\n\nconst outlinedClasses = cn('border-(--tag-bg) text-(--tag-fg)');\n\nconst Tag = ({\n className,\n variant,\n color,\n asChild = false,\n children,\n ...props\n}: Props) => {\n const { defaultProps } = useConfig();\n const tagDefaults = defaultProps?.tag as\n Partial<Pick<Props, 'variant' | 'color'>> | undefined;\n // An explicit prop wins over a `Config` default, which wins over the built-in\n // — the same resolution order `Button` uses.\n const resolvedVariant = variant ?? tagDefaults?.variant ?? 'filled';\n const resolvedColor = color ?? tagDefaults?.color ?? 'default';\n const isOutlined = resolvedVariant === 'outlined';\n const Comp = asChild ? Slot : 'span';\n\n return (\n <Comp\n data-slot=\"badge\"\n // Emitted for every colour, not just the presets, so consumers can hook\n // `[data-color]` / `[data-variant]` uniformly. `data-variant` is what\n // lets `globals.css` give `default` a different text colour when\n // outlined without reintroducing a second class path here.\n data-color={resolvedColor}\n data-variant={resolvedVariant}\n className={cn(\n TAG_BASE,\n 'rounded-full px-2.5 py-1',\n 'text-xs font-medium',\n isOutlined ? outlinedClasses : fillClasses,\n className,\n //\n )}\n {...props}\n >\n {children}\n </Comp>\n );\n};\n\nexport default Tag;\n"],"mappings":";;;;;;;;AAsDA,MAAM,WAAW,GACf,qBACA,kDACA,2DACA,8CACA,wEACF;AAWA,MAAM,cAAc,GAAG,oDAAoD;AAE3E,MAAM,kBAAkB,GAAG,mCAAmC;AAE9D,MAAM,OAAO,EACX,WACA,SACA,OACA,UAAU,OACV,UACA,GAAG,YACQ;CACX,MAAM,EAAE,iBAAiB,UAAU;CACnC,MAAM,cAAc,cAAc;CAIlC,MAAM,kBAAkB,WAAW,aAAa,WAAW;CAC3D,MAAM,gBAAgB,SAAS,aAAa,SAAS;CAIrD,OACE,oBAHW,UAAU,OAAO,QAG5B;EACE,aAAU;EAKV,cAAY;EACZ,gBAAc;EACd,WAAW,GACT,UACA,4BACA,uBAfa,oBAAoB,aAgBpB,kBAAkB,aAC/B,SAEF;EACA,GAAI;EAEH;CACG,CAAA;AAEV"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"float-button.d.mts","names":[],"sources":["../../../../src/components/atoms/float-button/float-button.tsx"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"float-button.d.mts","names":[],"sources":["../../../../src/components/atoms/float-button/float-button.tsx"],"mappings":";;UAKiB,cAAc;cAEzB,gBAAe,WAAA,aAAA,SAAmC,0BAAK,IAAA"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { cn } from "../../../utils.mjs";
|
|
2
2
|
import Button from "../../../Button.mjs";
|
|
3
|
+
import { OVERLAY_LAYER } from "../../../lib/z-layers.mjs";
|
|
3
4
|
import { jsx } from "react/jsx-runtime";
|
|
4
5
|
//#region src/components/atoms/float-button/float-button.tsx
|
|
5
6
|
const FloatButton = ({ className, children, ...props }) => {
|
|
6
7
|
return /* @__PURE__ */ jsx(Button, {
|
|
7
|
-
className: cn("fixed right-5 bottom-5 rounded-full", "
|
|
8
|
+
className: cn("fixed right-5 bottom-5 rounded-full", OVERLAY_LAYER, "h-12 w-12", "shadow-md", className),
|
|
8
9
|
variant: "outlined",
|
|
9
10
|
...props,
|
|
10
11
|
children
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"float-button.mjs","names":[],"sources":["../../../../src/components/atoms/float-button/float-button.tsx"],"sourcesContent":["import { cn } from '@repo/ui/utils';\n\nimport Button, { Props as ButtonProps } from '../button';\n\nexport interface Props extends ButtonProps {}\n\nconst FloatButton = ({ className, children, ...props }: Props) => {\n return (\n <Button\n className={cn(\n 'fixed right-5 bottom-5 rounded-full',\n '
|
|
1
|
+
{"version":3,"file":"float-button.mjs","names":[],"sources":["../../../../src/components/atoms/float-button/float-button.tsx"],"sourcesContent":["import { cn } from '@repo/ui/utils';\n\nimport { OVERLAY_LAYER } from '../../../lib/z-layers';\nimport Button, { Props as ButtonProps } from '../button';\n\nexport interface Props extends ButtonProps {}\n\nconst FloatButton = ({ className, children, ...props }: Props) => {\n return (\n <Button\n className={cn(\n 'fixed right-5 bottom-5 rounded-full',\n OVERLAY_LAYER,\n 'h-12 w-12',\n 'shadow-md',\n className,\n )}\n variant=\"outlined\"\n {...props}\n >\n {children}\n </Button>\n );\n};\n\nexport default FloatButton;\n"],"mappings":";;;;;AAOA,MAAM,eAAe,EAAE,WAAW,UAAU,GAAG,YAAmB;CAChE,OACE,oBAAC,QAAD;EACE,WAAW,GACT,uCACA,eACA,aACA,aACA,SACF;EACA,SAAQ;EACR,GAAI;EAEH;CACK,CAAA;AAEZ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dropdown.d.mts","names":[],"sources":["../../../src/components/molecules/dropdown.tsx"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"dropdown.d.mts","names":[],"sources":["../../../src/components/molecules/dropdown.tsx"],"mappings":";;KAYK,sBAAsB;UAEV,cAAc,MAAM;EACnC;EACA;EACA,OAAO;EACP,eAAe;;cAGX,aAAY,UAAA,MAAA,SAAA,MAAA,OAAA,cAAA,eAAA,WAAA,cAAA,iBAAA,SAUf,0BAAK,IAAA"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "../../utils.mjs";
|
|
3
|
+
import "../../lib/z-layers.mjs";
|
|
3
4
|
import Menu from "./menu/menu.mjs";
|
|
4
5
|
import { useId, useRef } from "react";
|
|
5
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -60,7 +61,7 @@ const Dropdown = ({ children, menu, trigger = "hover", open: _open, onOpenChange
|
|
|
60
61
|
},
|
|
61
62
|
children
|
|
62
63
|
}), /* @__PURE__ */ jsx(AnimatePresence, { children: open && /* @__PURE__ */ jsx(motion.div, {
|
|
63
|
-
className: cn("absolute top-full
|
|
64
|
+
className: cn("absolute top-full pt-2", "z-1000"),
|
|
64
65
|
initial: {
|
|
65
66
|
opacity: 0,
|
|
66
67
|
y: -10
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dropdown.mjs","names":[],"sources":["../../../src/components/molecules/dropdown.tsx"],"sourcesContent":["'use client';\n\nimport { useId, useRef } from 'react';\n\nimport { useControllableState, useKeyPress } from '@jbpark/use-hooks';\nimport { AnimatePresence, motion } from 'motion/react';\n\nimport { cn } from '@repo/ui/utils';\n\nimport Menu, { MenuProps } from './menu';\n\ntype ChangeEventHandler = (open: boolean) => void;\n\nexport interface Props extends React.ComponentPropsWithoutRef<'div'> {\n open?: boolean;\n trigger?: 'hover' | 'click';\n menu?: MenuProps;\n onOpenChange?: ChangeEventHandler;\n}\n\nconst Dropdown = ({\n children,\n menu,\n trigger = 'hover',\n open: _open,\n onOpenChange: _onOpenChange = () => {},\n className,\n onMouseEnter,\n onMouseLeave,\n ...props\n}: Props) => {\n const [open, onOpenChange] = useControllableState<boolean>({\n value: _open,\n defaultValue: false,\n onChange: _onOpenChange,\n });\n\n const isClickTrigger = trigger === 'click';\n const menuId = useId();\n const containerRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLDivElement>(null);\n\n useKeyPress(\n 'esc',\n () => {\n onOpenChange(false);\n },\n { target: triggerRef },\n );\n\n useKeyPress(\n ['Enter', 'space'],\n event => {\n event.preventDefault();\n onOpenChange(!open);\n },\n { target: triggerRef, enabled: isClickTrigger },\n );\n\n return (\n <div\n {...props}\n ref={containerRef}\n className={cn(\n 'relative',\n 'inline-block cursor-pointer',\n className,\n //\n )}\n onMouseEnter={event => {\n onMouseEnter?.(event);\n if (isClickTrigger) {\n return;\n }\n onOpenChange(true);\n }}\n onMouseLeave={event => {\n onMouseLeave?.(event);\n if (isClickTrigger) {\n return;\n }\n onOpenChange(false);\n }}\n >\n <div\n ref={triggerRef}\n role={isClickTrigger ? 'button' : undefined}\n tabIndex={isClickTrigger ? 0 : undefined}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n aria-controls={open ? menuId : undefined}\n onClick={() => {\n if (!isClickTrigger) {\n return;\n }\n onOpenChange(!open);\n }}\n onFocus={() => {\n if (isClickTrigger) {\n return;\n }\n onOpenChange(true);\n }}\n onBlur={e => {\n // Without this check, moving focus from the trigger into the\n // menu itself (e.g. via Tab) closed the dropdown before the\n // user could ever reach it — the mouse path avoided the same\n // bug only by way of onMouseDown's preventDefault below, which\n // does nothing for keyboard focus. Mirrors the containment\n // check menu/item/item.tsx already uses for the same reason.\n if (!containerRef.current?.contains(e.relatedTarget as Node)) {\n onOpenChange(false);\n }\n }}\n >\n {children}\n </div>\n <AnimatePresence>\n {open && (\n <motion.div\n className={cn(\n 'absolute top-full
|
|
1
|
+
{"version":3,"file":"dropdown.mjs","names":[],"sources":["../../../src/components/molecules/dropdown.tsx"],"sourcesContent":["'use client';\n\nimport { useId, useRef } from 'react';\n\nimport { useControllableState, useKeyPress } from '@jbpark/use-hooks';\nimport { AnimatePresence, motion } from 'motion/react';\n\nimport { cn } from '@repo/ui/utils';\n\nimport { OVERLAY_LAYER } from '../../lib/z-layers';\nimport Menu, { MenuProps } from './menu';\n\ntype ChangeEventHandler = (open: boolean) => void;\n\nexport interface Props extends React.ComponentPropsWithoutRef<'div'> {\n open?: boolean;\n trigger?: 'hover' | 'click';\n menu?: MenuProps;\n onOpenChange?: ChangeEventHandler;\n}\n\nconst Dropdown = ({\n children,\n menu,\n trigger = 'hover',\n open: _open,\n onOpenChange: _onOpenChange = () => {},\n className,\n onMouseEnter,\n onMouseLeave,\n ...props\n}: Props) => {\n const [open, onOpenChange] = useControllableState<boolean>({\n value: _open,\n defaultValue: false,\n onChange: _onOpenChange,\n });\n\n const isClickTrigger = trigger === 'click';\n const menuId = useId();\n const containerRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLDivElement>(null);\n\n useKeyPress(\n 'esc',\n () => {\n onOpenChange(false);\n },\n { target: triggerRef },\n );\n\n useKeyPress(\n ['Enter', 'space'],\n event => {\n event.preventDefault();\n onOpenChange(!open);\n },\n { target: triggerRef, enabled: isClickTrigger },\n );\n\n return (\n <div\n {...props}\n ref={containerRef}\n className={cn(\n 'relative',\n 'inline-block cursor-pointer',\n className,\n //\n )}\n onMouseEnter={event => {\n onMouseEnter?.(event);\n if (isClickTrigger) {\n return;\n }\n onOpenChange(true);\n }}\n onMouseLeave={event => {\n onMouseLeave?.(event);\n if (isClickTrigger) {\n return;\n }\n onOpenChange(false);\n }}\n >\n <div\n ref={triggerRef}\n role={isClickTrigger ? 'button' : undefined}\n tabIndex={isClickTrigger ? 0 : undefined}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n aria-controls={open ? menuId : undefined}\n onClick={() => {\n if (!isClickTrigger) {\n return;\n }\n onOpenChange(!open);\n }}\n onFocus={() => {\n if (isClickTrigger) {\n return;\n }\n onOpenChange(true);\n }}\n onBlur={e => {\n // Without this check, moving focus from the trigger into the\n // menu itself (e.g. via Tab) closed the dropdown before the\n // user could ever reach it — the mouse path avoided the same\n // bug only by way of onMouseDown's preventDefault below, which\n // does nothing for keyboard focus. Mirrors the containment\n // check menu/item/item.tsx already uses for the same reason.\n if (!containerRef.current?.contains(e.relatedTarget as Node)) {\n onOpenChange(false);\n }\n }}\n >\n {children}\n </div>\n <AnimatePresence>\n {open && (\n <motion.div\n className={cn(\n 'absolute top-full pt-2',\n OVERLAY_LAYER,\n //\n )}\n initial={{ opacity: 0, y: -10 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -10 }}\n transition={{ duration: 0.1 }}\n >\n <Menu\n {...menu}\n id={menuId}\n onMouseDown={e => {\n e.preventDefault();\n }}\n />\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n );\n};\n\nexport default Dropdown;\n"],"mappings":";;;;;;;;;AAqBA,MAAM,YAAY,EAChB,UACA,MACA,UAAU,SACV,MAAM,OACN,cAAc,sBAAsB,CAAC,GACrC,WACA,cACA,cACA,GAAG,YACQ;CACX,MAAM,CAAC,MAAM,gBAAgB,qBAA8B;EACzD,OAAO;EACP,cAAc;EACd,UAAU;CACZ,CAAC;CAED,MAAM,iBAAiB,YAAY;CACnC,MAAM,SAAS,MAAM;CACrB,MAAM,eAAe,OAAuB,IAAI;CAChD,MAAM,aAAa,OAAuB,IAAI;CAE9C,YACE,aACM;EACJ,aAAa,KAAK;CACpB,GACA,EAAE,QAAQ,WAAW,CACvB;CAEA,YACE,CAAC,SAAS,OAAO,IACjB,UAAS;EACP,MAAM,eAAe;EACrB,aAAa,CAAC,IAAI;CACpB,GACA;EAAE,QAAQ;EAAY,SAAS;CAAe,CAChD;CAEA,OACE,qBAAC,OAAD;EACE,GAAI;EACJ,KAAK;EACL,WAAW,GACT,YACA,+BACA,SAEF;EACA,eAAc,UAAS;GACrB,eAAe,KAAK;GACpB,IAAI,gBACF;GAEF,aAAa,IAAI;EACnB;EACA,eAAc,UAAS;GACrB,eAAe,KAAK;GACpB,IAAI,gBACF;GAEF,aAAa,KAAK;EACpB;EAtBF,UAAA,CAwBE,oBAAC,OAAD;GACE,KAAK;GACL,MAAM,iBAAiB,WAAW,KAAA;GAClC,UAAU,iBAAiB,IAAI,KAAA;GAC/B,iBAAc;GACd,iBAAe;GACf,iBAAe,OAAO,SAAS,KAAA;GAC/B,eAAe;IACb,IAAI,CAAC,gBACH;IAEF,aAAa,CAAC,IAAI;GACpB;GACA,eAAe;IACb,IAAI,gBACF;IAEF,aAAa,IAAI;GACnB;GACA,SAAQ,MAAK;IAOX,IAAI,CAAC,aAAa,SAAS,SAAS,EAAE,aAAqB,GACzD,aAAa,KAAK;GAEtB;GAEC;EACE,CAAA,GACL,oBAAC,iBAAD,EAAA,UACG,QACC,oBAAC,OAAO,KAAR;GACE,WAAW,GACT,0BAAA,QAGF;GACA,SAAS;IAAE,SAAS;IAAG,GAAG;GAAI;GAC9B,SAAS;IAAE,SAAS;IAAG,GAAG;GAAE;GAC5B,MAAM;IAAE,SAAS;IAAG,GAAG;GAAI;GAC3B,YAAY,EAAE,UAAU,GAAI;GAE5B,UAAA,oBAAC,MAAD;IACE,GAAI;IACJ,IAAI;IACJ,cAAa,MAAK;KAChB,EAAE,eAAe;IACnB;GACD,CAAA;EACS,CAAA,EAEC,CAAA,CACd;;AAET"}
|
|
@@ -5,12 +5,13 @@ interface ItemProps {
|
|
|
5
5
|
autoFill?: boolean | number;
|
|
6
6
|
pause?: boolean;
|
|
7
7
|
pauseOnHover?: boolean;
|
|
8
|
+
gap?: number;
|
|
8
9
|
children?: React.ReactNode;
|
|
9
10
|
}
|
|
10
11
|
interface Props extends Omit<React.ComponentPropsWithoutRef<'div'>, 'key'>, ItemProps {
|
|
11
12
|
width: string | number;
|
|
12
13
|
}
|
|
13
|
-
declare const Item: ({ width: _width, speed, autoFill, pause: _pause, pauseOnHover, children }: Props) => import("react").JSX.Element;
|
|
14
|
+
declare const Item: ({ width: _width, speed, autoFill, pause: _pause, pauseOnHover, gap, children }: Props) => import("react").JSX.Element;
|
|
14
15
|
//#endregion
|
|
15
16
|
export { ItemProps, Props, Item as default };
|
|
16
17
|
//# sourceMappingURL=item.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"item.d.mts","names":[],"sources":["../../../../src/components/molecules/marquees/item.tsx"],"mappings":";UASiB;EACf,MAAM,MAAM;EACZ;EACA;EACA;EACA;EACA,WAAW,MAAM;;UAGF,cACP,KAAK,MAAM,yCAAyC;EAC5D;;
|
|
1
|
+
{"version":3,"file":"item.d.mts","names":[],"sources":["../../../../src/components/molecules/marquees/item.tsx"],"mappings":";UASiB;EACf,MAAM,MAAM;EACZ;EACA;EACA;EACA;EACA;EACA,WAAW,MAAM;;UAGF,cACP,KAAK,MAAM,yCAAyC;EAC5D;;cAoBI,SAAQ,OAAA,QAAA,OAAA,UAAA,OAAA,QAAA,cAAA,KAAA,YAQX,0BAAK,IAAA"}
|
|
@@ -6,22 +6,32 @@ import { useGSAP } from "@gsap/react";
|
|
|
6
6
|
import { gsap } from "gsap";
|
|
7
7
|
//#region src/components/molecules/marquees/item.tsx
|
|
8
8
|
const INITIAL_AUTO_FILL_GUESS = 10;
|
|
9
|
-
const
|
|
9
|
+
const resolveInitialRepeatCount = (autoFill) => {
|
|
10
|
+
if (!autoFill) return 0;
|
|
11
|
+
return typeof autoFill === "boolean" ? INITIAL_AUTO_FILL_GUESS : autoFill;
|
|
12
|
+
};
|
|
13
|
+
const Item = ({ width: _width, speed = 100, autoFill = false, pause: _pause = false, pauseOnHover = false, gap = 0, children }) => {
|
|
10
14
|
const itemRefs = useRef([]);
|
|
11
15
|
const containerRef = useRef(null);
|
|
12
16
|
const tweenRef = useRef(null);
|
|
13
17
|
const [width, setWidth] = useState(_width);
|
|
14
18
|
const { pause, hoverEvents } = usePauseOnHover(pauseOnHover);
|
|
15
|
-
const [repeatCount, setRepeatCount] = useState(autoFill
|
|
19
|
+
const [repeatCount, setRepeatCount] = useState(resolveInitialRepeatCount(autoFill));
|
|
16
20
|
const [prevWidthProp, setPrevWidthProp] = useState(_width);
|
|
17
21
|
const [prevAutoFill, setPrevAutoFill] = useState(autoFill);
|
|
22
|
+
const [prevGap, setPrevGap] = useState(gap);
|
|
18
23
|
if (_width !== prevWidthProp) {
|
|
19
24
|
setPrevWidthProp(_width);
|
|
20
25
|
setWidth(_width);
|
|
21
26
|
}
|
|
22
27
|
if (autoFill !== prevAutoFill) {
|
|
23
28
|
setPrevAutoFill(autoFill);
|
|
24
|
-
setRepeatCount(autoFill
|
|
29
|
+
setRepeatCount(resolveInitialRepeatCount(autoFill));
|
|
30
|
+
}
|
|
31
|
+
if (gap !== prevGap) {
|
|
32
|
+
setPrevGap(gap);
|
|
33
|
+
setWidth(_width);
|
|
34
|
+
setRepeatCount(resolveInitialRepeatCount(autoFill));
|
|
25
35
|
}
|
|
26
36
|
const isPaused = _pause || pause;
|
|
27
37
|
useEffect(() => {
|
|
@@ -43,7 +53,8 @@ const Item = ({ width: _width, speed = 100, autoFill = false, pause: _pause = fa
|
|
|
43
53
|
}, [
|
|
44
54
|
autoFill,
|
|
45
55
|
width,
|
|
46
|
-
children
|
|
56
|
+
children,
|
|
57
|
+
gap
|
|
47
58
|
]);
|
|
48
59
|
useGSAP(() => {
|
|
49
60
|
if (!containerRef.current || typeof width === "string") return;
|
|
@@ -79,8 +90,12 @@ const Item = ({ width: _width, speed = 100, autoFill = false, pause: _pause = fa
|
|
|
79
90
|
ref: (el) => {
|
|
80
91
|
if (el) itemRefs.current[index] = el;
|
|
81
92
|
},
|
|
93
|
+
style: { paddingRight: gap },
|
|
94
|
+
children
|
|
95
|
+
}), [...Array(typeof autoFill === "number" ? autoFill : repeatCount)].map((_, i) => /* @__PURE__ */ jsx("div", {
|
|
96
|
+
style: { paddingRight: gap },
|
|
82
97
|
children
|
|
83
|
-
}
|
|
98
|
+
}, i))]
|
|
84
99
|
}, index))
|
|
85
100
|
})
|
|
86
101
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"item.mjs","names":[],"sources":["../../../../src/components/molecules/marquees/item.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef, useState } from 'react';\n\nimport { useGSAP } from '@gsap/react';\nimport { gsap } from 'gsap';\n\nimport usePauseOnHover from './use-pause-on-hover';\n\nexport interface ItemProps {\n key?: React.Key;\n speed?: number;\n autoFill?: boolean | number;\n pause?: boolean;\n pauseOnHover?: boolean;\n children?: React.ReactNode;\n}\n\nexport interface Props\n extends Omit<React.ComponentPropsWithoutRef<'div'>, 'key'>, ItemProps {\n width: string | number;\n}\n\n// `autoFill={true}` (the default on `Marquees`) needs *some* repeat count\n// before the item can be measured and the real count computed — this is\n// only ever visible for that one render, but rendering 100 copies (200\n// with the 2x loop duplication) up front was needlessly heavy for the\n// common case. A modest guess still avoids visible gaps for typical\n// item/container width ratios while cutting the worst-case default\n// render by ~90%.\nconst INITIAL_AUTO_FILL_GUESS = 10;\n\nconst Item = ({\n width: _width,\n speed = 100,\n autoFill = false,\n pause: _pause = false,\n pauseOnHover = false,\n children,\n}: Props) => {\n const itemRefs = useRef<HTMLDivElement[]>([]);\n const containerRef = useRef<HTMLDivElement>(null);\n const tweenRef = useRef<gsap.core.Tween | null>(null);\n\n const [width, setWidth] = useState<string | number>(_width);\n const { pause, hoverEvents } = usePauseOnHover(pauseOnHover);\n const [repeatCount, setRepeatCount] = useState(\n autoFill
|
|
1
|
+
{"version":3,"file":"item.mjs","names":[],"sources":["../../../../src/components/molecules/marquees/item.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef, useState } from 'react';\n\nimport { useGSAP } from '@gsap/react';\nimport { gsap } from 'gsap';\n\nimport usePauseOnHover from './use-pause-on-hover';\n\nexport interface ItemProps {\n key?: React.Key;\n speed?: number;\n autoFill?: boolean | number;\n pause?: boolean;\n pauseOnHover?: boolean;\n gap?: number;\n children?: React.ReactNode;\n}\n\nexport interface Props\n extends Omit<React.ComponentPropsWithoutRef<'div'>, 'key'>, ItemProps {\n width: string | number;\n}\n\n// `autoFill={true}` (the default on `Marquees`) needs *some* repeat count\n// before the item can be measured and the real count computed — this is\n// only ever visible for that one render, but rendering 100 copies (200\n// with the 2x loop duplication) up front was needlessly heavy for the\n// common case. A modest guess still avoids visible gaps for typical\n// item/container width ratios while cutting the worst-case default\n// render by ~90%.\nconst INITIAL_AUTO_FILL_GUESS = 10;\n\nconst resolveInitialRepeatCount = (autoFill: boolean | number) => {\n if (!autoFill) {\n return 0;\n }\n\n return typeof autoFill === 'boolean' ? INITIAL_AUTO_FILL_GUESS : autoFill;\n};\n\nconst Item = ({\n width: _width,\n speed = 100,\n autoFill = false,\n pause: _pause = false,\n pauseOnHover = false,\n gap = 0,\n children,\n}: Props) => {\n const itemRefs = useRef<HTMLDivElement[]>([]);\n const containerRef = useRef<HTMLDivElement>(null);\n const tweenRef = useRef<gsap.core.Tween | null>(null);\n\n const [width, setWidth] = useState<string | number>(_width);\n const { pause, hoverEvents } = usePauseOnHover(pauseOnHover);\n const [repeatCount, setRepeatCount] = useState(\n resolveInitialRepeatCount(autoFill),\n );\n const [prevWidthProp, setPrevWidthProp] = useState(_width);\n const [prevAutoFill, setPrevAutoFill] = useState(autoFill);\n const [prevGap, setPrevGap] = useState(gap);\n\n // Adjusted directly in render (React's \"adjust state during render\"\n // pattern) instead of an effect, so `width`/`repeatCount` re-sync with\n // the prop in the same render it changes rather than the render after.\n if (_width !== prevWidthProp) {\n setPrevWidthProp(_width);\n setWidth(_width);\n }\n\n if (autoFill !== prevAutoFill) {\n setPrevAutoFill(autoFill);\n setRepeatCount(resolveInitialRepeatCount(autoFill));\n }\n\n // `gap` widens every copy, so both the measured item width and the repeat\n // count derived from it are stale. Rewind `width` to the container width\n // so the measuring effect below recomputes the loop distance from scratch.\n if (gap !== prevGap) {\n setPrevGap(gap);\n setWidth(_width);\n setRepeatCount(resolveInitialRepeatCount(autoFill));\n }\n\n const isPaused = _pause || pause;\n\n useEffect(() => {\n if (\n !autoFill ||\n !children ||\n !itemRefs.current[0] ||\n typeof width === 'string'\n ) {\n return;\n }\n\n // `offsetWidth` is the border-box width, so it already includes the\n // `gap` padding each copy carries — the loop distance stays exact\n // without adding `gap` back in here.\n const itemWidth = itemRefs.current[0].offsetWidth;\n\n if (itemWidth >= width) {\n setWidth(itemWidth);\n setRepeatCount(0);\n return;\n }\n\n let totalWidth = 0;\n let repeatCount = 0;\n\n while (totalWidth < width) {\n totalWidth += itemWidth;\n repeatCount += 1;\n }\n\n setWidth(totalWidth);\n setRepeatCount(repeatCount - 1);\n }, [autoFill, width, children, gap]);\n\n useGSAP(\n () => {\n if (!containerRef.current || typeof width === 'string') {\n return;\n }\n\n if (tweenRef.current) {\n tweenRef.current.kill();\n }\n\n tweenRef.current = gsap.fromTo(\n containerRef.current,\n { x: 0 },\n {\n x: -width,\n duration: width / speed,\n repeat: -1,\n ease: 'linear',\n },\n );\n\n return () => {\n tweenRef.current?.kill();\n };\n },\n {\n scope: containerRef,\n dependencies: [width, speed],\n },\n );\n\n useEffect(() => {\n if (!tweenRef.current) {\n return;\n }\n\n if (isPaused) {\n tweenRef.current.pause();\n } else {\n tweenRef.current.play();\n }\n }, [isPaused]);\n\n return (\n <div\n className=\"flex overflow-hidden\"\n {...hoverEvents}\n //\n >\n <div ref={containerRef} className=\"flex flex-nowrap\">\n {[...Array(2)].map((_, index) => (\n <div\n key={index}\n style={{ minWidth: width }}\n className=\"flex flex-nowrap\"\n >\n {/* Every copy carries the same trailing `gap`, so the spacing is\n uniform at the seam between copies and between the two loop\n halves — no special-casing at either boundary. */}\n <div\n ref={el => {\n if (el) {\n itemRefs.current[index] = el;\n }\n }}\n style={{ paddingRight: gap }}\n >\n {children}\n </div>\n {[\n ...Array(typeof autoFill === 'number' ? autoFill : repeatCount),\n ].map((_, i) => (\n <div key={i} style={{ paddingRight: gap }}>\n {children}\n </div>\n ))}\n </div>\n ))}\n </div>\n </div>\n );\n};\n\nexport default Item;\n"],"mappings":";;;;;;;AA+BA,MAAM,0BAA0B;AAEhC,MAAM,6BAA6B,aAA+B;CAChE,IAAI,CAAC,UACH,OAAO;CAGT,OAAO,OAAO,aAAa,YAAY,0BAA0B;AACnE;AAEA,MAAM,QAAQ,EACZ,OAAO,QACP,QAAQ,KACR,WAAW,OACX,OAAO,SAAS,OAChB,eAAe,OACf,MAAM,GACN,eACW;CACX,MAAM,WAAW,OAAyB,CAAC,CAAC;CAC5C,MAAM,eAAe,OAAuB,IAAI;CAChD,MAAM,WAAW,OAA+B,IAAI;CAEpD,MAAM,CAAC,OAAO,YAAY,SAA0B,MAAM;CAC1D,MAAM,EAAE,OAAO,gBAAgB,gBAAgB,YAAY;CAC3D,MAAM,CAAC,aAAa,kBAAkB,SACpC,0BAA0B,QAAQ,CACpC;CACA,MAAM,CAAC,eAAe,oBAAoB,SAAS,MAAM;CACzD,MAAM,CAAC,cAAc,mBAAmB,SAAS,QAAQ;CACzD,MAAM,CAAC,SAAS,cAAc,SAAS,GAAG;CAK1C,IAAI,WAAW,eAAe;EAC5B,iBAAiB,MAAM;EACvB,SAAS,MAAM;CACjB;CAEA,IAAI,aAAa,cAAc;EAC7B,gBAAgB,QAAQ;EACxB,eAAe,0BAA0B,QAAQ,CAAC;CACpD;CAKA,IAAI,QAAQ,SAAS;EACnB,WAAW,GAAG;EACd,SAAS,MAAM;EACf,eAAe,0BAA0B,QAAQ,CAAC;CACpD;CAEA,MAAM,WAAW,UAAU;CAE3B,gBAAgB;EACd,IACE,CAAC,YACD,CAAC,YACD,CAAC,SAAS,QAAQ,MAClB,OAAO,UAAU,UAEjB;EAMF,MAAM,YAAY,SAAS,QAAQ,EAAE,CAAC;EAEtC,IAAI,aAAa,OAAO;GACtB,SAAS,SAAS;GAClB,eAAe,CAAC;GAChB;EACF;EAEA,IAAI,aAAa;EACjB,IAAI,cAAc;EAElB,OAAO,aAAa,OAAO;GACzB,cAAc;GACd,eAAe;EACjB;EAEA,SAAS,UAAU;EACnB,eAAe,cAAc,CAAC;CAChC,GAAG;EAAC;EAAU;EAAO;EAAU;CAAG,CAAC;CAEnC,cACQ;EACJ,IAAI,CAAC,aAAa,WAAW,OAAO,UAAU,UAC5C;EAGF,IAAI,SAAS,SACX,SAAS,QAAQ,KAAK;EAGxB,SAAS,UAAU,KAAK,OACtB,aAAa,SACb,EAAE,GAAG,EAAE,GACP;GACE,GAAG,CAAC;GACJ,UAAU,QAAQ;GAClB,QAAQ;GACR,MAAM;EACR,CACF;EAEA,aAAa;GACX,SAAS,SAAS,KAAK;EACzB;CACF,GACA;EACE,OAAO;EACP,cAAc,CAAC,OAAO,KAAK;CAC7B,CACF;CAEA,gBAAgB;EACd,IAAI,CAAC,SAAS,SACZ;EAGF,IAAI,UACF,SAAS,QAAQ,MAAM;OAEvB,SAAS,QAAQ,KAAK;CAE1B,GAAG,CAAC,QAAQ,CAAC;CAEb,OACE,oBAAC,OAAD;EACE,WAAU;EACV,GAAI;EAGJ,UAAA,oBAAC,OAAD;GAAK,KAAK;GAAc,WAAU;GAC/B,UAAA,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,UACrB,qBAAC,OAAD;IAEE,OAAO,EAAE,UAAU,MAAM;IACzB,WAAU;IAHZ,UAAA,CAQE,oBAAC,OAAD;KACE,MAAK,OAAM;MACT,IAAI,IACF,SAAS,QAAQ,SAAS;KAE9B;KACA,OAAO,EAAE,cAAc,IAAI;KAE1B;IACE,CAAA,GACJ,CACC,GAAG,MAAM,OAAO,aAAa,WAAW,WAAW,WAAW,CAChE,CAAC,CAAC,KAAK,GAAG,MACR,oBAAC,OAAD;KAAa,OAAO,EAAE,cAAc,IAAI;KACrC;IACE,GAFK,CAEL,CACN,CACE;GAxBE,GAAA,KAwBF,CACN;EACE,CAAA;CACF,CAAA;AAET"}
|
|
@@ -4,9 +4,10 @@ interface Props extends React.ComponentPropsWithoutRef<'div'> {
|
|
|
4
4
|
speed?: number;
|
|
5
5
|
autoFill?: boolean | number;
|
|
6
6
|
pauseOnHover?: boolean;
|
|
7
|
+
gap?: number;
|
|
7
8
|
items?: ItemProps[];
|
|
8
9
|
}
|
|
9
|
-
declare const Marquees: ({ className, items, speed, pauseOnHover, autoFill, ...props }: Props) => import("react").JSX.Element;
|
|
10
|
+
declare const Marquees: ({ className, items, speed, pauseOnHover, autoFill, gap, ...props }: Props) => import("react").JSX.Element;
|
|
10
11
|
//#endregion
|
|
11
12
|
export { Props, Marquees as default };
|
|
12
13
|
//# sourceMappingURL=marquees.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"marquees.d.mts","names":[],"sources":["../../../../src/components/molecules/marquees/marquees.tsx"],"mappings":";;UAeiB,cAAc,MAAM;EACnC;EACA;EACA;EACA,QAAQ;;cAGJ,aAAY,WAAA,OAAA,OAAA,cAAA,
|
|
1
|
+
{"version":3,"file":"marquees.d.mts","names":[],"sources":["../../../../src/components/molecules/marquees/marquees.tsx"],"mappings":";;UAeiB,cAAc,MAAM;EACnC;EACA;EACA;EACA;EACA,QAAQ;;cAGJ,aAAY,WAAA,OAAA,OAAA,cAAA,UAAA,QAAA,SAQf,0BAAK,IAAA"}
|
|
@@ -6,8 +6,8 @@ import { useEffect, useRef, useState } from "react";
|
|
|
6
6
|
import { jsx } from "react/jsx-runtime";
|
|
7
7
|
import { useMergedRef, useResponsiveSize, useThrottledValue } from "@jbpark/use-hooks";
|
|
8
8
|
//#region src/components/molecules/marquees/marquees.tsx
|
|
9
|
-
const Marquees = ({ className, items, speed, pauseOnHover = true, autoFill = true, ...props }) => {
|
|
10
|
-
const [width, setWidth] = useState("
|
|
9
|
+
const Marquees = ({ className, items, speed, pauseOnHover = true, autoFill = true, gap, ...props }) => {
|
|
10
|
+
const [width, setWidth] = useState("100%");
|
|
11
11
|
const [padding, setPadding] = useState(0);
|
|
12
12
|
const { pause, hoverEvents } = usePauseOnHover(pauseOnHover);
|
|
13
13
|
const throttledWidth = useThrottledValue(width, 200);
|
|
@@ -35,6 +35,7 @@ const Marquees = ({ className, items, speed, pauseOnHover = true, autoFill = tru
|
|
|
35
35
|
pause,
|
|
36
36
|
speed,
|
|
37
37
|
autoFill,
|
|
38
|
+
gap,
|
|
38
39
|
...item,
|
|
39
40
|
children
|
|
40
41
|
}, itemKey || key))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"marquees.mjs","names":[],"sources":["../../../../src/components/molecules/marquees/marquees.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef, useState } from 'react';\n\nimport {\n useMergedRef,\n useResponsiveSize,\n useThrottledValue,\n} from '@jbpark/use-hooks';\n\nimport { cn } from '@repo/ui/utils';\n\nimport Item, { type ItemProps } from './item';\nimport usePauseOnHover from './use-pause-on-hover';\n\nexport interface Props extends React.ComponentPropsWithoutRef<'div'> {\n speed?: number;\n autoFill?: boolean | number;\n pauseOnHover?: boolean;\n items?: ItemProps[];\n}\n\nconst Marquees = ({\n className,\n items,\n speed,\n pauseOnHover = true,\n autoFill = true,\n ...props\n}: Props) => {\n const [width, setWidth] = useState<string | number>(\n '
|
|
1
|
+
{"version":3,"file":"marquees.mjs","names":[],"sources":["../../../../src/components/molecules/marquees/marquees.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef, useState } from 'react';\n\nimport {\n useMergedRef,\n useResponsiveSize,\n useThrottledValue,\n} from '@jbpark/use-hooks';\n\nimport { cn } from '@repo/ui/utils';\n\nimport Item, { type ItemProps } from './item';\nimport usePauseOnHover from './use-pause-on-hover';\n\nexport interface Props extends React.ComponentPropsWithoutRef<'div'> {\n speed?: number;\n autoFill?: boolean | number;\n pauseOnHover?: boolean;\n gap?: number;\n items?: ItemProps[];\n}\n\nconst Marquees = ({\n className,\n items,\n speed,\n pauseOnHover = true,\n autoFill = true,\n gap,\n ...props\n}: Props) => {\n // The container's own width, not the viewport's. `useResponsiveSize`\n // measures in a layout effect, but this component copies the result into\n // state in a passive effect and then throttles it, so the seed is what\n // paints for the first ~200ms. `100vw` overflowed every padded or columned\n // layout for that window — on the docs page it drew the track 1280px wide\n // inside a 703px column, briefly showing the whole track and giving the\n // page a horizontal scrollbar. `100%` resolves to the same value the\n // measurement lands on, so there is nothing left to snap.\n const [width, setWidth] = useState<string | number>(\n '100%',\n //\n );\n const [padding, setPadding] = useState(0);\n const { pause, hoverEvents } = usePauseOnHover(pauseOnHover);\n\n const throttledWidth = useThrottledValue(width, 200);\n\n const { size, ref: responsiveRef } = useResponsiveSize<HTMLDivElement>();\n const containerRef = useRef<HTMLDivElement | null>(null);\n const setContainerRef = useMergedRef(containerRef, responsiveRef);\n\n useEffect(() => {\n if (!size.width || !containerRef.current) {\n return;\n }\n\n const computedStyle = getComputedStyle(containerRef.current);\n const paddingLeft = parseFloat(computedStyle.paddingLeft) || 0;\n const paddingRight = parseFloat(computedStyle.paddingRight) || 0;\n\n setPadding(paddingLeft + paddingRight);\n\n setWidth(size.width);\n }, [size]);\n\n return (\n <div\n ref={setContainerRef}\n className={cn(\n className,\n //\n )}\n {...hoverEvents}\n {...props}\n >\n <div\n className={cn(\n 'flex flex-col gap-y-5',\n 'overflow-hidden',\n 'whitespace-nowrap',\n 'bg-inherit',\n //\n )}\n style={{\n width:\n typeof throttledWidth === 'number'\n ? throttledWidth - padding\n : throttledWidth,\n }}\n >\n {items?.map(({ children, key: itemKey, ...item }: ItemProps, key) => (\n <Item\n key={itemKey || key}\n width={throttledWidth}\n pause={pause}\n speed={speed}\n autoFill={autoFill}\n gap={gap}\n {...item}\n >\n {children}\n </Item>\n ))}\n </div>\n </div>\n );\n};\n\nexport default Marquees;\n"],"mappings":";;;;;;;;AAuBA,MAAM,YAAY,EAChB,WACA,OACA,OACA,eAAe,MACf,WAAW,MACX,KACA,GAAG,YACQ;CASX,MAAM,CAAC,OAAO,YAAY,SACxB,MAEF;CACA,MAAM,CAAC,SAAS,cAAc,SAAS,CAAC;CACxC,MAAM,EAAE,OAAO,gBAAgB,gBAAgB,YAAY;CAE3D,MAAM,iBAAiB,kBAAkB,OAAO,GAAG;CAEnD,MAAM,EAAE,MAAM,KAAK,kBAAkB,kBAAkC;CACvE,MAAM,eAAe,OAA8B,IAAI;CACvD,MAAM,kBAAkB,aAAa,cAAc,aAAa;CAEhE,gBAAgB;EACd,IAAI,CAAC,KAAK,SAAS,CAAC,aAAa,SAC/B;EAGF,MAAM,gBAAgB,iBAAiB,aAAa,OAAO;EAC3D,MAAM,cAAc,WAAW,cAAc,WAAW,KAAK;EAC7D,MAAM,eAAe,WAAW,cAAc,YAAY,KAAK;EAE/D,WAAW,cAAc,YAAY;EAErC,SAAS,KAAK,KAAK;CACrB,GAAG,CAAC,IAAI,CAAC;CAET,OACE,oBAAC,OAAD;EACE,KAAK;EACL,WAAW,GACT,SAEF;EACA,GAAI;EACJ,GAAI;EAEJ,UAAA,oBAAC,OAAD;GACE,WAAW,GACT,yBACA,mBACA,qBACA,YAEF;GACA,OAAO,EACL,OACE,OAAO,mBAAmB,WACtB,iBAAiB,UACjB,eACR;GAEC,UAAA,OAAO,KAAK,EAAE,UAAU,KAAK,SAAS,GAAG,QAAmB,QAC3D,oBAAC,MAAD;IAEE,OAAO;IACA;IACA;IACG;IACL;IACL,GAAI;IAEH;GACG,GATC,WAAW,GASZ,CACP;EACE,CAAA;CACF,CAAA;AAET"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { cn
|
|
2
|
+
import { cn } from "../../utils.mjs";
|
|
3
3
|
import { DEFAULT_LOCALE, useConfig } from "../../providers/config/context.mjs";
|
|
4
4
|
import Button from "../../Button.mjs";
|
|
5
5
|
import { drawer_exports } from "../../core/drawer.mjs";
|
|
@@ -104,10 +104,10 @@ const Drawer = ({ open, title, extra, footer, closable = true, closeIcon, direct
|
|
|
104
104
|
children: [/* @__PURE__ */ jsx(DrawerTitle, {
|
|
105
105
|
className: cn(classNames?.title),
|
|
106
106
|
children: title
|
|
107
|
-
}),
|
|
107
|
+
}), extra != null && /* @__PURE__ */ jsx("div", {
|
|
108
108
|
className: cn("shrink-0", classNames?.extra),
|
|
109
|
-
children:
|
|
110
|
-
})
|
|
109
|
+
children: extra
|
|
110
|
+
})]
|
|
111
111
|
})]
|
|
112
112
|
}), /* @__PURE__ */ jsx(DrawerDescription, { className: "hidden" })]
|
|
113
113
|
}),
|
|
@@ -115,10 +115,10 @@ const Drawer = ({ open, title, extra, footer, closable = true, closeIcon, direct
|
|
|
115
115
|
className: cn("overflow-auto p-5", classNames?.body),
|
|
116
116
|
children
|
|
117
117
|
}),
|
|
118
|
-
|
|
118
|
+
footer != null && /* @__PURE__ */ jsx(DrawerFooter, {
|
|
119
119
|
className: cn(classNames?.footer),
|
|
120
|
-
children:
|
|
121
|
-
})
|
|
120
|
+
children: footer
|
|
121
|
+
})
|
|
122
122
|
]
|
|
123
123
|
})
|
|
124
124
|
});
|