@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.
Files changed (58) hide show
  1. package/dist/Button.d.mts +7 -2
  2. package/dist/Button.d.mts.map +1 -1
  3. package/dist/Button.mjs.map +1 -1
  4. package/dist/CodeEditor.d.mts +51 -0
  5. package/dist/CodeEditor.d.mts.map +1 -0
  6. package/dist/CodeEditor.mjs +87 -0
  7. package/dist/CodeEditor.mjs.map +1 -0
  8. package/dist/Tag.d.mts +8 -3
  9. package/dist/Tag.d.mts.map +1 -1
  10. package/dist/Tag.mjs.map +1 -1
  11. package/dist/components/atoms/float-button/float-button.d.mts.map +1 -1
  12. package/dist/components/atoms/float-button/float-button.mjs +2 -1
  13. package/dist/components/atoms/float-button/float-button.mjs.map +1 -1
  14. package/dist/components/molecules/dropdown.d.mts.map +1 -1
  15. package/dist/components/molecules/dropdown.mjs +2 -1
  16. package/dist/components/molecules/dropdown.mjs.map +1 -1
  17. package/dist/components/molecules/marquees/item.d.mts +2 -1
  18. package/dist/components/molecules/marquees/item.d.mts.map +1 -1
  19. package/dist/components/molecules/marquees/item.mjs +20 -5
  20. package/dist/components/molecules/marquees/item.mjs.map +1 -1
  21. package/dist/components/molecules/marquees/marquees.d.mts +2 -1
  22. package/dist/components/molecules/marquees/marquees.d.mts.map +1 -1
  23. package/dist/components/molecules/marquees/marquees.mjs +3 -2
  24. package/dist/components/molecules/marquees/marquees.mjs.map +1 -1
  25. package/dist/components/organisms/drawer.mjs +7 -7
  26. package/dist/components/organisms/drawer.mjs.map +1 -1
  27. package/dist/components/templates/layout/header.d.mts.map +1 -1
  28. package/dist/components/templates/layout/header.mjs +2 -1
  29. package/dist/components/templates/layout/header.mjs.map +1 -1
  30. package/dist/components/templates/layout/sider.mjs.map +1 -1
  31. package/dist/components/templates/page-header.mjs +4 -4
  32. package/dist/components/templates/page-header.mjs.map +1 -1
  33. package/dist/core/dialog.mjs +5 -5
  34. package/dist/core/dialog.mjs.map +1 -1
  35. package/dist/core/drawer.mjs +3 -2
  36. package/dist/core/drawer.mjs.map +1 -1
  37. package/dist/core/popover.mjs +3 -2
  38. package/dist/core/popover.mjs.map +1 -1
  39. package/dist/core/select.d.mts.map +1 -1
  40. package/dist/core/select.mjs +3 -2
  41. package/dist/core/select.mjs.map +1 -1
  42. package/dist/lib/colors.d.mts +36 -3
  43. package/dist/lib/colors.d.mts.map +1 -1
  44. package/dist/lib/z-layers.mjs +33 -0
  45. package/dist/lib/z-layers.mjs.map +1 -0
  46. package/dist/providers/config/config.d.mts.map +1 -1
  47. package/dist/providers/config/config.mjs +5 -32
  48. package/dist/providers/config/config.mjs.map +1 -1
  49. package/dist/providers/config/dark-mode.mjs +39 -0
  50. package/dist/providers/config/dark-mode.mjs.map +1 -0
  51. package/dist/providers/config/types.d.mts +25 -18
  52. package/dist/providers/config/types.d.mts.map +1 -1
  53. package/dist/style.css +90 -90
  54. package/dist/utils.d.mts +12 -0
  55. package/dist/utils.d.mts.map +1 -1
  56. package/dist/utils.mjs +12 -0
  57. package/dist/utils.mjs.map +1 -1
  58. package/package.json +33 -1
@@ -1 +1 @@
1
- {"version":3,"file":"config.mjs","names":[],"sources":["../../../src/providers/config/config.tsx"],"sourcesContent":["'use client';\n\nimport {\n useCallback,\n useEffect,\n useMemo,\n useState,\n useSyncExternalStore,\n} from 'react';\n\nimport { DirectionProvider } from '@radix-ui/react-direction';\n\nimport { cn } from '@repo/ui/utils';\n\nimport { Context, DEFAULT_LOCALE, useConfig } from './context';\nimport { registerRootConfig } from './registry';\nimport type {\n ComponentSize,\n DefaultProps,\n Locale,\n ThemeConfig,\n ThemeToken,\n} from './types';\n\nconst DARK_MEDIA_QUERY = '(prefers-color-scheme: dark)';\n\nconst subscribeToSystemColorScheme = (callback: () => void) => {\n if (typeof window === 'undefined') {\n return () => {};\n }\n\n const mql = window.matchMedia(DARK_MEDIA_QUERY);\n mql.addEventListener('change', callback);\n return () => mql.removeEventListener('change', callback);\n};\n\nconst getSystemPrefersDark = () =>\n typeof window !== 'undefined' && window.matchMedia(DARK_MEDIA_QUERY).matches;\n\n// No `window` during SSR, so there's no way to know the visitor's actual\n// preference before hydration — assume light (the conservative default)\n// and let useSyncExternalStore correct it client-side once matchMedia is\n// available, same flash-of-incorrect-guess tradeoff already accepted for\n// Sider's breakpoint prop elsewhere in this library.\nconst getServerSnapshot = () => false;\n\nconst useSystemPrefersDark = () =>\n useSyncExternalStore(\n subscribeToSystemColorScheme,\n getSystemPrefersDark,\n getServerSnapshot,\n );\n\nconst tokenToCssVar: Record<keyof ThemeToken, string> = {\n colorPrimary: '--primary',\n colorPrimaryForeground: '--primary-foreground',\n colorBackground: '--background',\n colorForeground: '--foreground',\n colorCard: '--card',\n colorCardForeground: '--card-foreground',\n colorPopover: '--popover',\n colorPopoverForeground: '--popover-foreground',\n colorSecondary: '--secondary',\n colorSecondaryForeground: '--secondary-foreground',\n colorMuted: '--muted',\n colorMutedForeground: '--muted-foreground',\n colorAccent: '--accent',\n colorAccentForeground: '--accent-foreground',\n colorDestructive: '--destructive',\n colorBorder: '--border',\n colorInput: '--input',\n colorRing: '--ring',\n borderRadius: '--radius',\n\n btnBackground: '--btn-bg',\n btnBackgroundHover: '--btn-bg-hover',\n btnBackgroundActive: '--btn-bg-active',\n btnBorder: '--btn-border',\n btnForeground: '--btn-fg',\n\n sidebar: '--sidebar',\n sidebarForeground: '--sidebar-foreground',\n sidebarPrimary: '--sidebar-primary',\n sidebarPrimaryForeground: '--sidebar-primary-foreground',\n sidebarAccent: '--sidebar-accent',\n sidebarAccentForeground: '--sidebar-accent-foreground',\n sidebarBorder: '--sidebar-border',\n sidebarRing: '--sidebar-ring',\n\n chart1: '--chart-1',\n chart2: '--chart-2',\n chart3: '--chart-3',\n chart4: '--chart-4',\n chart5: '--chart-5',\n\n fontSans: '--font-sans',\n fontMono: '--font-mono',\n};\n\nconst buildCssVars = (token?: ThemeToken): React.CSSProperties => {\n if (!token) {\n return {};\n }\n\n const vars: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(token)) {\n if (value === undefined) {\n continue;\n }\n const cssVar = tokenToCssVar[key as keyof ThemeToken];\n if (cssVar) {\n vars[cssVar] = value;\n }\n }\n\n return vars as React.CSSProperties;\n};\n\ninterface Props {\n theme?: ThemeConfig;\n locale?: Locale;\n getContainer?: () => HTMLElement;\n componentSize?: ComponentSize;\n direction?: 'ltr' | 'rtl';\n defaultProps?: DefaultProps;\n className?: string;\n style?: React.CSSProperties;\n children: React.ReactNode;\n}\n\nconst Config = ({\n theme = {},\n locale = {},\n getContainer,\n componentSize,\n direction,\n defaultProps,\n className,\n style,\n children,\n}: Props) => {\n const parent = useConfig();\n const [wrapperEl, setWrapperEl] = useState<HTMLDivElement | null>(null);\n\n // Only a Config with no Config ancestor of its own registers for the\n // imperative stack (Modal.*/Toast.*) to inherit — see registry.tsx.\n const isRoot = !parent.isConfigured;\n\n // `theme` is typically a fresh inline object literal at the call site\n // (and `parent.theme` inherits the same problem from its own Config\n // ancestor), so depending on them directly meant mergedTheme/contextValue\n // got a new identity on every render regardless of whether the actual\n // values changed — every useConfig() consumer in the tree re-rendered on\n // every render of whatever's above this Config, which for the common\n // app-root usage is everything. Depend on a serialized key instead so\n // the memo only invalidates when the content actually changes; these\n // are small, string/boolean-only objects, so JSON.stringify is cheap.\n const themeKey = JSON.stringify(theme);\n const parentThemeKey = JSON.stringify(parent.theme);\n const localeKey = JSON.stringify(locale);\n const parentLocaleKey = JSON.stringify(parent.locale);\n\n const mergedTheme = useMemo<ThemeConfig>(\n () => ({\n ...parent.theme,\n ...theme,\n token: {\n ...parent.theme.token,\n ...theme.token,\n },\n darkToken: {\n ...parent.theme.darkToken,\n ...theme.darkToken,\n },\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [parentThemeKey, themeKey],\n );\n\n const mergedLocale = useMemo<Locale>(\n () => ({\n ...parent.locale,\n ...locale,\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [parentLocaleKey, localeKey],\n );\n\n const systemPrefersDark = useSystemPrefersDark();\n const isDarkActive =\n mergedTheme.dark === 'system'\n ? systemPrefersDark\n : mergedTheme.dark === 'dark';\n\n // `darkToken` overrides individual keys of `token` (not a full swap)\n // only while dark mode is actually active.\n const cssVars = useMemo(\n () =>\n buildCssVars(\n isDarkActive\n ? { ...mergedTheme.token, ...mergedTheme.darkToken }\n : mergedTheme.token,\n ),\n [mergedTheme.token, mergedTheme.darkToken, isDarkActive],\n );\n\n const hasCssVars = Object.keys(cssVars).length > 0;\n const hasDarkMode = mergedTheme.dark !== undefined;\n const needsWrapper =\n hasCssVars || hasDarkMode || !!className || !!style || !!direction;\n\n // Resolution order: an explicit `getContainer` prop on this Config wins;\n // otherwise, if this Config renders its own themed wrapper below, that's\n // the default (portaled content appended into it still inherits its CSS\n // custom properties and matches the `.dark` selector — `display:\n // contents` only removes the element from the layout box tree, not from\n // the DOM/cascade); otherwise defer to the parent Config's resolution,\n // so an inner Config that only sets e.g. `locale` doesn't shadow an\n // outer one's theme container.\n const resolvedGetContainer = useCallback((): HTMLElement | undefined => {\n if (getContainer) {\n return getContainer();\n }\n if (needsWrapper && wrapperEl) {\n return wrapperEl;\n }\n return parent.getContainer();\n }, [getContainer, needsWrapper, wrapperEl, parent]);\n\n const resolvedComponentSize = componentSize ?? parent.componentSize;\n const resolvedDirection = direction ?? parent.direction;\n\n const defaultPropsKey = JSON.stringify(defaultProps);\n const parentDefaultPropsKey = JSON.stringify(parent.defaultProps);\n\n const mergedDefaultProps = useMemo<DefaultProps | undefined>(\n () =>\n parent.defaultProps || defaultProps\n ? { ...parent.defaultProps, ...defaultProps }\n : undefined,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [parentDefaultPropsKey, defaultPropsKey],\n );\n\n const contextValue = useMemo(\n () => ({\n theme: mergedTheme,\n locale: mergedLocale,\n getContainer: resolvedGetContainer,\n componentSize: resolvedComponentSize,\n direction: resolvedDirection,\n defaultProps: mergedDefaultProps,\n isConfigured: true,\n }),\n [\n mergedTheme,\n mergedLocale,\n resolvedGetContainer,\n resolvedComponentSize,\n resolvedDirection,\n mergedDefaultProps,\n ],\n );\n\n useEffect(() => {\n if (!isRoot) {\n return;\n }\n return registerRootConfig(contextValue);\n }, [isRoot, contextValue]);\n\n const content = (\n <Context.Provider value={contextValue}>\n {needsWrapper ? (\n <div\n ref={setWrapperEl}\n dir={direction}\n className={cn(isDarkActive && 'dark', className)}\n style={{\n ...(hasCssVars ? cssVars : undefined),\n ...style,\n // Keeps this element out of the layout box tree — so it\n // doesn't intercept flex/grid child relationships or break\n // height chains like min-h-screen — while custom properties\n // and the `dark` class selector still cascade to children\n // normally (inheritance and box generation are separate\n // mechanisms in CSS). Note this means className/style\n // shouldn't be used here for anything that needs a real box\n // (padding, background, borders); Config's className/style are\n // meant for theming hooks (the `dark` class, arbitrary\n // selector hooks, custom properties), not layout — spread\n // after the caller's `style` so it can't be overridden into\n // something that reintroduces the layout-breaking wrapper this\n // was added to fix in the first place.\n display: 'contents',\n }}\n >\n {children}\n </div>\n ) : (\n children\n )}\n </Context.Provider>\n );\n\n // DirectionProvider is Radix's own mechanism (@radix-ui/react-direction)\n // for propagating `dir` to every primitive that calls its useDirection()\n // internally — Select, Menu (Dropdown/Menu), Accordion (Collapse),\n // RadioGroup, ScrollArea all pick up RTL automatically this way, no\n // per-component wiring needed. Only wraps when this Config's own\n // `direction` prop is set (not the merely-inherited resolved value) —\n // context already cascades on its own, so re-wrapping at every nested\n // Config that doesn't override direction would just be redundant.\n // Components with hardcoded LTR-only layout logic that Radix's Direction\n // context doesn't reach (Popover placement, Splitter, Sider, Drawer)\n // aren't covered by this and would need dedicated follow-up work.\n return direction ? (\n <DirectionProvider dir={direction}>{content}</DirectionProvider>\n ) : (\n content\n );\n};\n\nexport default Config;\nexport { useConfig, DEFAULT_LOCALE };\nexport type {\n Props,\n ComponentSize,\n DefaultProps,\n Locale,\n ThemeConfig,\n ThemeToken,\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"config.mjs","names":[],"sources":["../../../src/providers/config/config.tsx"],"sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useMemo, useState } from 'react';\n\nimport { DirectionProvider } from '@radix-ui/react-direction';\n\nimport { cn } from '@repo/ui/utils';\n\nimport { Context, DEFAULT_LOCALE, useConfig } from './context';\nimport { resolveDark, useSystemPrefersDark } from './dark-mode';\nimport { registerRootConfig } from './registry';\nimport type {\n ComponentSize,\n DefaultProps,\n Locale,\n ThemeConfig,\n ThemeToken,\n} from './types';\n\nconst tokenToCssVar: Record<keyof ThemeToken, string> = {\n colorPrimary: '--primary',\n colorPrimaryForeground: '--primary-foreground',\n colorBackground: '--background',\n colorForeground: '--foreground',\n colorCard: '--card',\n colorCardForeground: '--card-foreground',\n colorPopover: '--popover',\n colorPopoverForeground: '--popover-foreground',\n colorSecondary: '--secondary',\n colorSecondaryForeground: '--secondary-foreground',\n colorMuted: '--muted',\n colorMutedForeground: '--muted-foreground',\n colorAccent: '--accent',\n colorAccentForeground: '--accent-foreground',\n colorDestructive: '--destructive',\n colorBorder: '--border',\n colorInput: '--input',\n colorRing: '--ring',\n borderRadius: '--radius',\n\n // Not `--font-sans`/`--font-mono`: those are `@theme inline` entries, so\n // Tailwind substitutes their value into each utility rather than referencing\n // them, and a wrapper-level override never applied. `globals.css` reads these\n // hooks ahead of the app's own font variable instead (#343).\n fontSans: '--ui-font-sans',\n fontMono: '--ui-font-mono',\n};\n\nconst buildCssVars = (token?: ThemeToken): React.CSSProperties => {\n if (!token) {\n return {};\n }\n\n const vars: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(token)) {\n if (value === undefined) {\n continue;\n }\n const cssVar = tokenToCssVar[key as keyof ThemeToken];\n if (cssVar) {\n vars[cssVar] = value;\n }\n }\n\n return vars as React.CSSProperties;\n};\n\ninterface Props {\n theme?: ThemeConfig;\n locale?: Locale;\n getContainer?: () => HTMLElement;\n componentSize?: ComponentSize;\n direction?: 'ltr' | 'rtl';\n defaultProps?: DefaultProps;\n className?: string;\n style?: React.CSSProperties;\n children: React.ReactNode;\n}\n\nconst Config = ({\n theme = {},\n locale = {},\n getContainer,\n componentSize,\n direction,\n defaultProps,\n className,\n style,\n children,\n}: Props) => {\n const parent = useConfig();\n const [wrapperEl, setWrapperEl] = useState<HTMLDivElement | null>(null);\n\n // Only a Config with no Config ancestor of its own registers for the\n // imperative stack (Modal.*/Toast.*) to inherit — see registry.tsx.\n const isRoot = !parent.isConfigured;\n\n // `theme` is typically a fresh inline object literal at the call site\n // (and `parent.theme` inherits the same problem from its own Config\n // ancestor), so depending on them directly meant mergedTheme/contextValue\n // got a new identity on every render regardless of whether the actual\n // values changed — every useConfig() consumer in the tree re-rendered on\n // every render of whatever's above this Config, which for the common\n // app-root usage is everything. Depend on a serialized key instead so\n // the memo only invalidates when the content actually changes; these\n // are small, string/boolean-only objects, so JSON.stringify is cheap.\n const themeKey = JSON.stringify(theme);\n const parentThemeKey = JSON.stringify(parent.theme);\n const localeKey = JSON.stringify(locale);\n const parentLocaleKey = JSON.stringify(parent.locale);\n\n const mergedTheme = useMemo<ThemeConfig>(\n () => ({\n ...parent.theme,\n ...theme,\n token: {\n ...parent.theme.token,\n ...theme.token,\n },\n darkToken: {\n ...parent.theme.darkToken,\n ...theme.darkToken,\n },\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [parentThemeKey, themeKey],\n );\n\n const mergedLocale = useMemo<Locale>(\n () => ({\n ...parent.locale,\n ...locale,\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [parentLocaleKey, localeKey],\n );\n\n const systemPrefersDark = useSystemPrefersDark();\n const isDarkActive = resolveDark(mergedTheme.dark, systemPrefersDark);\n\n // `darkToken` overrides individual keys of `token` (not a full swap)\n // only while dark mode is actually active.\n const cssVars = useMemo(\n () =>\n buildCssVars(\n isDarkActive\n ? { ...mergedTheme.token, ...mergedTheme.darkToken }\n : mergedTheme.token,\n ),\n [mergedTheme.token, mergedTheme.darkToken, isDarkActive],\n );\n\n const hasCssVars = Object.keys(cssVars).length > 0;\n const hasDarkMode = mergedTheme.dark !== undefined;\n const needsWrapper =\n hasCssVars || hasDarkMode || !!className || !!style || !!direction;\n\n // Resolution order: an explicit `getContainer` prop on this Config wins;\n // otherwise, if this Config renders its own themed wrapper below, that's\n // the default (portaled content appended into it still inherits its CSS\n // custom properties and matches the `.dark` selector — `display:\n // contents` only removes the element from the layout box tree, not from\n // the DOM/cascade); otherwise defer to the parent Config's resolution,\n // so an inner Config that only sets e.g. `locale` doesn't shadow an\n // outer one's theme container.\n const resolvedGetContainer = useCallback((): HTMLElement | undefined => {\n if (getContainer) {\n return getContainer();\n }\n if (needsWrapper && wrapperEl) {\n return wrapperEl;\n }\n return parent.getContainer();\n }, [getContainer, needsWrapper, wrapperEl, parent]);\n\n const resolvedComponentSize = componentSize ?? parent.componentSize;\n const resolvedDirection = direction ?? parent.direction;\n\n const defaultPropsKey = JSON.stringify(defaultProps);\n const parentDefaultPropsKey = JSON.stringify(parent.defaultProps);\n\n const mergedDefaultProps = useMemo<DefaultProps | undefined>(\n () =>\n parent.defaultProps || defaultProps\n ? { ...parent.defaultProps, ...defaultProps }\n : undefined,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [parentDefaultPropsKey, defaultPropsKey],\n );\n\n const contextValue = useMemo(\n () => ({\n theme: mergedTheme,\n locale: mergedLocale,\n getContainer: resolvedGetContainer,\n componentSize: resolvedComponentSize,\n direction: resolvedDirection,\n defaultProps: mergedDefaultProps,\n isConfigured: true,\n }),\n [\n mergedTheme,\n mergedLocale,\n resolvedGetContainer,\n resolvedComponentSize,\n resolvedDirection,\n mergedDefaultProps,\n ],\n );\n\n useEffect(() => {\n if (!isRoot) {\n return;\n }\n return registerRootConfig(contextValue);\n }, [isRoot, contextValue]);\n\n const content = (\n <Context.Provider value={contextValue}>\n {needsWrapper ? (\n <div\n ref={setWrapperEl}\n dir={direction}\n className={cn(isDarkActive && 'dark', className)}\n style={{\n ...(hasCssVars ? cssVars : undefined),\n ...style,\n // Keeps this element out of the layout box tree — so it\n // doesn't intercept flex/grid child relationships or break\n // height chains like min-h-screen — while custom properties\n // and the `dark` class selector still cascade to children\n // normally (inheritance and box generation are separate\n // mechanisms in CSS). Note this means className/style\n // shouldn't be used here for anything that needs a real box\n // (padding, background, borders); Config's className/style are\n // meant for theming hooks (the `dark` class, arbitrary\n // selector hooks, custom properties), not layout — spread\n // after the caller's `style` so it can't be overridden into\n // something that reintroduces the layout-breaking wrapper this\n // was added to fix in the first place.\n display: 'contents',\n }}\n >\n {children}\n </div>\n ) : (\n children\n )}\n </Context.Provider>\n );\n\n // DirectionProvider is Radix's own mechanism (@radix-ui/react-direction)\n // for propagating `dir` to every primitive that calls its useDirection()\n // internally — Select, Menu (Dropdown/Menu), Accordion (Collapse),\n // RadioGroup, ScrollArea all pick up RTL automatically this way, no\n // per-component wiring needed. Only wraps when this Config's own\n // `direction` prop is set (not the merely-inherited resolved value) —\n // context already cascades on its own, so re-wrapping at every nested\n // Config that doesn't override direction would just be redundant.\n // Components with hardcoded LTR-only layout logic that Radix's Direction\n // context doesn't reach (Popover placement, Splitter, Sider, Drawer)\n // aren't covered by this and would need dedicated follow-up work.\n return direction ? (\n <DirectionProvider dir={direction}>{content}</DirectionProvider>\n ) : (\n content\n );\n};\n\nexport default Config;\nexport { useConfig, DEFAULT_LOCALE };\nexport type {\n Props,\n ComponentSize,\n DefaultProps,\n Locale,\n ThemeConfig,\n ThemeToken,\n};\n"],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ "use client";
2
+ import { useConfig } from "./context.mjs";
3
+ import { useSyncExternalStore } from "react";
4
+ //#region src/providers/config/dark-mode.ts
5
+ const DARK_MEDIA_QUERY = "(prefers-color-scheme: dark)";
6
+ const subscribeToSystemColorScheme = (callback) => {
7
+ if (typeof window === "undefined") return () => {};
8
+ const mql = window.matchMedia(DARK_MEDIA_QUERY);
9
+ mql.addEventListener("change", callback);
10
+ return () => mql.removeEventListener("change", callback);
11
+ };
12
+ const getSystemPrefersDark = () => typeof window !== "undefined" && window.matchMedia(DARK_MEDIA_QUERY).matches;
13
+ const getServerSnapshot = () => false;
14
+ const useSystemPrefersDark = () => useSyncExternalStore(subscribeToSystemColorScheme, getSystemPrefersDark, getServerSnapshot);
15
+ /**
16
+ * Turns the three-valued `theme.dark` setting into the boolean that decides
17
+ * whether dark treatments actually apply. Kept as a pure function so `Config`
18
+ * can resolve the theme it is about to publish (its merged value isn't in
19
+ * context yet) while consumers resolve the one already in context, without the
20
+ * two drifting apart.
21
+ */
22
+ const resolveDark = (dark, systemPrefersDark) => dark === "system" ? systemPrefersDark : dark === "dark";
23
+ /**
24
+ * Whether dark mode is active for the nearest `Config`, with `'system'`
25
+ * resolved against `prefers-color-scheme`.
26
+ *
27
+ * Internal to the package: components that render *CSS* can just use Tailwind's
28
+ * `dark:` variants against the `.dark` class `Config` puts on its wrapper. This
29
+ * exists for the ones that have to hand a light/dark choice to a third party in
30
+ * JS instead — `CodeEditor` picking a CodeMirror theme extension.
31
+ */
32
+ const useIsDarkActive = () => {
33
+ const { theme } = useConfig();
34
+ return resolveDark(theme.dark, useSystemPrefersDark());
35
+ };
36
+ //#endregion
37
+ export { resolveDark, useIsDarkActive, useSystemPrefersDark };
38
+
39
+ //# sourceMappingURL=dark-mode.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dark-mode.mjs","names":[],"sources":["../../../src/providers/config/dark-mode.ts"],"sourcesContent":["'use client';\n\nimport { useSyncExternalStore } from 'react';\n\nimport { useConfig } from './context';\nimport type { ThemeConfig } from './types';\n\nconst DARK_MEDIA_QUERY = '(prefers-color-scheme: dark)';\n\nconst subscribeToSystemColorScheme = (callback: () => void) => {\n if (typeof window === 'undefined') {\n return () => {};\n }\n\n const mql = window.matchMedia(DARK_MEDIA_QUERY);\n mql.addEventListener('change', callback);\n return () => mql.removeEventListener('change', callback);\n};\n\nconst getSystemPrefersDark = () =>\n typeof window !== 'undefined' && window.matchMedia(DARK_MEDIA_QUERY).matches;\n\n// No `window` during SSR, so there's no way to know the visitor's actual\n// preference before hydration — assume light (the conservative default)\n// and let useSyncExternalStore correct it client-side once matchMedia is\n// available, same flash-of-incorrect-guess tradeoff already accepted for\n// Sider's breakpoint prop elsewhere in this library.\nconst getServerSnapshot = () => false;\n\nexport const useSystemPrefersDark = () =>\n useSyncExternalStore(\n subscribeToSystemColorScheme,\n getSystemPrefersDark,\n getServerSnapshot,\n );\n\n/**\n * Turns the three-valued `theme.dark` setting into the boolean that decides\n * whether dark treatments actually apply. Kept as a pure function so `Config`\n * can resolve the theme it is about to publish (its merged value isn't in\n * context yet) while consumers resolve the one already in context, without the\n * two drifting apart.\n */\nexport const resolveDark = (\n dark: ThemeConfig['dark'],\n systemPrefersDark: boolean,\n) => (dark === 'system' ? systemPrefersDark : dark === 'dark');\n\n/**\n * Whether dark mode is active for the nearest `Config`, with `'system'`\n * resolved against `prefers-color-scheme`.\n *\n * Internal to the package: components that render *CSS* can just use Tailwind's\n * `dark:` variants against the `.dark` class `Config` puts on its wrapper. This\n * exists for the ones that have to hand a light/dark choice to a third party in\n * JS instead — `CodeEditor` picking a CodeMirror theme extension.\n */\nexport const useIsDarkActive = () => {\n const { theme } = useConfig();\n\n return resolveDark(theme.dark, useSystemPrefersDark());\n};\n"],"mappings":";;;;AAOA,MAAM,mBAAmB;AAEzB,MAAM,gCAAgC,aAAyB;CAC7D,IAAI,OAAO,WAAW,aACpB,aAAa,CAAC;CAGhB,MAAM,MAAM,OAAO,WAAW,gBAAgB;CAC9C,IAAI,iBAAiB,UAAU,QAAQ;CACvC,aAAa,IAAI,oBAAoB,UAAU,QAAQ;AACzD;AAEA,MAAM,6BACJ,OAAO,WAAW,eAAe,OAAO,WAAW,gBAAgB,CAAC,CAAC;AAOvE,MAAM,0BAA0B;AAEhC,MAAa,6BACX,qBACE,8BACA,sBACA,iBACF;;;;;;;;AASF,MAAa,eACX,MACA,sBACI,SAAS,WAAW,oBAAoB,SAAS;;;;;;;;;;AAWvD,MAAa,wBAAwB;CACnC,MAAM,EAAE,UAAU,UAAU;CAE5B,OAAO,YAAY,MAAM,MAAM,qBAAqB,CAAC;AACvD"}
@@ -1,4 +1,29 @@
1
1
  //#region src/providers/config/types.d.ts
2
+ /**
3
+ * Theme overrides applied as CSS custom properties on `Config`'s wrapper
4
+ * element, inherited by everything inside it.
5
+ *
6
+ * Every token here is verified to actually reach a component. That is worth
7
+ * stating because it did not use to be true: 19 of the previous 40 tokens were
8
+ * inert (#343). Three groups were dropped in 8.0 —
9
+ *
10
+ * - `btnBackground`/`btnBackgroundHover`/`btnBackgroundActive`/`btnBorder`/
11
+ * `btnForeground` — `globals.css` declares `--btn-*` on the button element
12
+ * itself (keyed off `data-color`), and an element's own declaration always
13
+ * beats an inherited one, so a wrapper value could never win. Re-theming a
14
+ * button is done through `colorPrimary`/`colorDestructive` for the semantic
15
+ * colours, or by overriding `--preset-color` in CSS for the preset hues.
16
+ * - `sidebar*` and `chart*` — no component in the library reads them, and the
17
+ * matching utilities (`bg-sidebar`, `bg-chart-1`) are never generated into
18
+ * the published stylesheet, so nothing consumed them. The underlying
19
+ * `--sidebar-*`/`--chart-*` custom properties are still declared in
20
+ * `globals.css` and remain overridable in plain CSS; only the inert token
21
+ * API is gone. They should come back alongside an actual Sidebar/Chart
22
+ * component.
23
+ *
24
+ * `fontSans`/`fontMono` were inert for a different reason and were fixed
25
+ * rather than removed — see below.
26
+ */
2
27
  interface ThemeToken {
3
28
  colorPrimary?: string;
4
29
  colorPrimaryForeground?: string;
@@ -19,24 +44,6 @@ interface ThemeToken {
19
44
  colorInput?: string;
20
45
  colorRing?: string;
21
46
  borderRadius?: string;
22
- btnBackground?: string;
23
- btnBackgroundHover?: string;
24
- btnBackgroundActive?: string;
25
- btnBorder?: string;
26
- btnForeground?: string;
27
- sidebar?: string;
28
- sidebarForeground?: string;
29
- sidebarPrimary?: string;
30
- sidebarPrimaryForeground?: string;
31
- sidebarAccent?: string;
32
- sidebarAccentForeground?: string;
33
- sidebarBorder?: string;
34
- sidebarRing?: string;
35
- chart1?: string;
36
- chart2?: string;
37
- chart3?: string;
38
- chart4?: string;
39
- chart5?: string;
40
47
  fontSans?: string;
41
48
  fontMono?: string;
42
49
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.mts","names":[],"sources":["../../../src/providers/config/types.ts"],"mappings":";UAAiB;EAEf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EASA;EAIA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;EACA;EAGA;EACA;;UAGe;EACf,QAAQ;EAKR,YAAY;EASZ;;UAQe;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;KAMU;KAYA,eAAe,eAAe;UAEzB;EACf,OAAO;EACP,QAAQ;EACR;EAUA,oBAAoB;EACpB,gBAAgB;EAChB,eAAe;EAKf"}
1
+ {"version":3,"file":"types.d.mts","names":[],"sources":["../../../src/providers/config/types.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;UAyBiB;EAEf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EASA;EAMA;EACA;;UAGe;EACf,QAAQ;EAKR,YAAY;EASZ;;UAQe;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;KAMU;KAYA,eAAe,eAAe;UAEzB;EACf,OAAO;EACP,QAAQ;EACR;EAUA,oBAAoB;EACpB,gBAAgB;EAChB,eAAe;EAKf"}
package/dist/style.css CHANGED
@@ -2,13 +2,28 @@
2
2
  @layer properties;
3
3
  @layer theme {
4
4
  :root, :host {
5
- --font-sans: var(--font-geist-sans);
6
- --font-mono: var(--font-geist-mono);
5
+ --font-sans: var(--ui-font-sans, var(--font-geist-sans));
6
+ --font-mono: var(--ui-font-mono, var(--font-geist-mono));
7
7
  --color-red-400: oklch(70.4% .191 22.216);
8
+ --color-red-500: oklch(63.7% .237 25.331);
9
+ --color-orange-500: oklch(70.5% .213 47.604);
10
+ --color-orange-600: oklch(64.6% .222 41.116);
11
+ --color-amber-500: oklch(76.9% .188 70.08);
8
12
  --color-yellow-400: oklch(85.2% .199 91.936);
13
+ --color-yellow-500: oklch(79.5% .184 86.047);
14
+ --color-yellow-600: oklch(68.1% .162 75.834);
15
+ --color-lime-500: oklch(76.8% .233 130.85);
9
16
  --color-green-400: oklch(79.2% .209 151.711);
17
+ --color-green-500: oklch(72.3% .219 149.579);
18
+ --color-green-600: oklch(62.7% .194 149.214);
19
+ --color-cyan-500: oklch(71.5% .143 215.221);
10
20
  --color-blue-400: oklch(70.7% .165 254.624);
21
+ --color-blue-500: oklch(62.3% .214 259.815);
11
22
  --color-blue-600: oklch(54.6% .245 262.881);
23
+ --color-indigo-500: oklch(58.5% .233 277.117);
24
+ --color-purple-500: oklch(62.7% .265 303.9);
25
+ --color-fuchsia-500: oklch(66.7% .295 322.15);
26
+ --color-pink-500: oklch(65.6% .241 354.308);
12
27
  --color-gray-100: oklch(96.7% .003 264.542);
13
28
  --color-gray-200: oklch(92.8% .006 264.531);
14
29
  --color-gray-300: oklch(87.2% .01 258.338);
@@ -88,7 +103,56 @@
88
103
  }
89
104
  }
90
105
  @layer components {
106
+ :is([data-slot="button"], [data-slot="badge"])[data-color="blue"] {
107
+ --preset-color: var(--color-blue-500);
108
+ }
109
+ :is([data-slot="button"], [data-slot="badge"])[data-color="purple"] {
110
+ --preset-color: var(--color-purple-500);
111
+ }
112
+ :is([data-slot="button"], [data-slot="badge"])[data-color="cyan"] {
113
+ --preset-color: var(--color-cyan-500);
114
+ }
115
+ :is([data-slot="button"], [data-slot="badge"])[data-color="green"] {
116
+ --preset-color: var(--color-green-500);
117
+ }
118
+ :is([data-slot="button"], [data-slot="badge"])[data-color="fuchsia"] {
119
+ --preset-color: var(--color-fuchsia-500);
120
+ }
121
+ :is([data-slot="button"], [data-slot="badge"])[data-color="pink"] {
122
+ --preset-color: var(--color-pink-500);
123
+ }
124
+ :is([data-slot="button"], [data-slot="badge"])[data-color="red"] {
125
+ --preset-color: var(--color-red-500);
126
+ }
127
+ :is([data-slot="button"], [data-slot="badge"])[data-color="orange"] {
128
+ --preset-color: var(--color-orange-500);
129
+ }
130
+ :is([data-slot="button"], [data-slot="badge"])[data-color="yellow"] {
131
+ --preset-color: var(--color-yellow-500);
132
+ }
133
+ :is([data-slot="button"], [data-slot="badge"])[data-color="indigo"] {
134
+ --preset-color: var(--color-indigo-500);
135
+ }
136
+ :is([data-slot="button"], [data-slot="badge"])[data-color="lime"] {
137
+ --preset-color: var(--color-lime-500);
138
+ }
139
+ :is([data-slot="button"], [data-slot="badge"])[data-color="amber"] {
140
+ --preset-color: var(--color-amber-500);
141
+ }
142
+ :is([data-slot="button"], [data-slot="badge"])[data-color="magenta"] {
143
+ --preset-color: var(--color-fuchsia-500);
144
+ }
145
+ :is([data-slot="button"], [data-slot="badge"])[data-color="geekblue"] {
146
+ --preset-color: var(--color-indigo-500);
147
+ }
148
+ :is([data-slot="button"], [data-slot="badge"])[data-color="gold"] {
149
+ --preset-color: var(--color-amber-500);
150
+ }
151
+ :is([data-slot="button"], [data-slot="badge"])[data-color="volcano"] {
152
+ --preset-color: var(--color-orange-600);
153
+ }
91
154
  [data-slot="button"][data-color] {
155
+ --btn-bg: var(--preset-color);
92
156
  --btn-fg: white;
93
157
  --btn-border: var(--btn-bg);
94
158
  --btn-bg-hover: var(--btn-bg);
@@ -100,7 +164,7 @@
100
164
  --btn-bg-active: color-mix(in oklch, var(--btn-bg), black 22%);
101
165
  }
102
166
  }
103
- [data-slot="button"][data-color="yellow"], [data-slot="button"][data-color="lime"], [data-slot="button"][data-color="gold"] {
167
+ [data-slot="button"][data-color="yellow"], [data-slot="button"][data-color="lime"], [data-slot="button"][data-color="amber"], [data-slot="button"][data-color="gold"] {
104
168
  --btn-fg: black;
105
169
  }
106
170
  [data-slot="button"][data-color="default"] {
@@ -114,46 +178,8 @@
114
178
  [data-slot="button"][data-color="danger"] {
115
179
  --btn-bg: var(--destructive);
116
180
  }
117
- [data-slot="button"][data-color="blue"] {
118
- --btn-bg: #3b82f6;
119
- }
120
- [data-slot="button"][data-color="purple"] {
121
- --btn-bg: #a855f7;
122
- }
123
- [data-slot="button"][data-color="cyan"] {
124
- --btn-bg: #06b6d4;
125
- }
126
- [data-slot="button"][data-color="green"] {
127
- --btn-bg: #22c55e;
128
- }
129
- [data-slot="button"][data-color="magenta"] {
130
- --btn-bg: #d946ef;
131
- }
132
- [data-slot="button"][data-color="pink"] {
133
- --btn-bg: #ec4899;
134
- }
135
- [data-slot="button"][data-color="red"] {
136
- --btn-bg: #ef4444;
137
- }
138
- [data-slot="button"][data-color="orange"] {
139
- --btn-bg: #f97316;
140
- }
141
- [data-slot="button"][data-color="yellow"] {
142
- --btn-bg: #eab308;
143
- }
144
- [data-slot="button"][data-color="volcano"] {
145
- --btn-bg: #ea580c;
146
- }
147
- [data-slot="button"][data-color="geekblue"] {
148
- --btn-bg: #6366f1;
149
- }
150
- [data-slot="button"][data-color="lime"] {
151
- --btn-bg: #84cc16;
152
- }
153
- [data-slot="button"][data-color="gold"] {
154
- --btn-bg: #f59e0b;
155
- }
156
181
  [data-slot="badge"][data-color] {
182
+ --tag-bg: var(--preset-color);
157
183
  --tag-fg: var(--tag-bg);
158
184
  --tag-tint: var(--tag-bg);
159
185
  @supports (color: color-mix(in lab, red, red)) {
@@ -175,57 +201,18 @@
175
201
  --tag-bg: var(--destructive);
176
202
  }
177
203
  [data-slot="badge"][data-color="success"] {
178
- --tag-bg: oklch(72.3% .219 149.579);
179
- --tag-fg: oklch(62.7% .194 149.214);
204
+ --tag-bg: var(--color-green-500);
205
+ --tag-fg: var(--color-green-600);
180
206
  }
181
207
  [data-slot="badge"][data-color="warning"] {
182
- --tag-bg: oklch(79.5% .184 86.047);
183
- --tag-fg: oklch(68.1% .162 75.834);
208
+ --tag-bg: var(--color-yellow-500);
209
+ --tag-fg: var(--color-yellow-600);
184
210
  }
185
211
  :is(.dark, [data-theme="dark"]) [data-slot="badge"][data-color="success"] {
186
- --tag-fg: oklch(79.2% .209 151.711);
212
+ --tag-fg: var(--color-green-400);
187
213
  }
188
214
  :is(.dark, [data-theme="dark"]) [data-slot="badge"][data-color="warning"] {
189
- --tag-fg: oklch(85.2% .199 91.936);
190
- }
191
- [data-slot="badge"][data-color="blue"] {
192
- --tag-bg: #3b82f6;
193
- }
194
- [data-slot="badge"][data-color="purple"] {
195
- --tag-bg: #a855f7;
196
- }
197
- [data-slot="badge"][data-color="cyan"] {
198
- --tag-bg: #06b6d4;
199
- }
200
- [data-slot="badge"][data-color="green"] {
201
- --tag-bg: #22c55e;
202
- }
203
- [data-slot="badge"][data-color="magenta"] {
204
- --tag-bg: #d946ef;
205
- }
206
- [data-slot="badge"][data-color="pink"] {
207
- --tag-bg: #ec4899;
208
- }
209
- [data-slot="badge"][data-color="red"] {
210
- --tag-bg: #ef4444;
211
- }
212
- [data-slot="badge"][data-color="orange"] {
213
- --tag-bg: #f97316;
214
- }
215
- [data-slot="badge"][data-color="yellow"] {
216
- --tag-bg: #eab308;
217
- }
218
- [data-slot="badge"][data-color="volcano"] {
219
- --tag-bg: #ea580c;
220
- }
221
- [data-slot="badge"][data-color="geekblue"] {
222
- --tag-bg: #6366f1;
223
- }
224
- [data-slot="badge"][data-color="lime"] {
225
- --tag-bg: #84cc16;
226
- }
227
- [data-slot="badge"][data-color="gold"] {
228
- --tag-bg: #f59e0b;
215
+ --tag-fg: var(--color-yellow-400);
229
216
  }
230
217
  [data-slot="col"] {
231
218
  flex-basis: calc(var(--col-span-xs) / 24 * 100%);
@@ -380,6 +367,9 @@
380
367
  .z-50 {
381
368
  z-index: 50;
382
369
  }
370
+ .z-1000 {
371
+ z-index: 1000;
372
+ }
383
373
  .z-10000 {
384
374
  z-index: 10000;
385
375
  }
@@ -1192,6 +1182,9 @@
1192
1182
  .bg-card {
1193
1183
  background-color: var(--card);
1194
1184
  }
1185
+ .bg-chart-1 {
1186
+ background-color: var(--chart-1);
1187
+ }
1195
1188
  .bg-destructive {
1196
1189
  background-color: var(--destructive);
1197
1190
  }
@@ -1219,6 +1212,9 @@
1219
1212
  .bg-secondary {
1220
1213
  background-color: var(--secondary);
1221
1214
  }
1215
+ .bg-sidebar {
1216
+ background-color: var(--sidebar);
1217
+ }
1222
1218
  .bg-transparent {
1223
1219
  background-color: transparent;
1224
1220
  }
@@ -1346,7 +1342,7 @@
1346
1342
  text-align: start;
1347
1343
  }
1348
1344
  .font-mono {
1349
- font-family: var(--font-geist-mono);
1345
+ font-family: var(--ui-font-mono, var(--font-geist-mono));
1350
1346
  }
1351
1347
  .text-2xl {
1352
1348
  font-size: var(--text-2xl);
@@ -1567,6 +1563,10 @@
1567
1563
  --tw-blur: blur(8px);
1568
1564
  filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
1569
1565
  }
1566
+ .invert {
1567
+ --tw-invert: invert(100%);
1568
+ filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
1569
+ }
1570
1570
  .filter {
1571
1571
  filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
1572
1572
  }
@@ -2722,7 +2722,7 @@
2722
2722
  padding-block: calc(var(--spacing) * 0.5);
2723
2723
  }
2724
2724
  .\[\&_\.tiptap_code\]\:font-mono .tiptap code {
2725
- font-family: var(--font-geist-mono);
2725
+ font-family: var(--ui-font-mono, var(--font-geist-mono));
2726
2726
  }
2727
2727
  .\[\&_\.tiptap_code\]\:text-\[0\.85em\] .tiptap code {
2728
2728
  font-size: 0.85em;
@@ -2833,7 +2833,7 @@
2833
2833
  padding: calc(var(--spacing) * 3);
2834
2834
  }
2835
2835
  .\[\&_\.tiptap_pre\]\:font-mono .tiptap pre {
2836
- font-family: var(--font-geist-mono);
2836
+ font-family: var(--ui-font-mono, var(--font-geist-mono));
2837
2837
  }
2838
2838
  .\[\&_\.tiptap_pre\]\:text-sm .tiptap pre {
2839
2839
  font-size: var(--text-sm);
package/dist/utils.d.mts CHANGED
@@ -2,6 +2,18 @@ import * as React$1 from "react";
2
2
  import { ClassValue } from "clsx";
3
3
  //#region src/lib/utils/index.d.ts
4
4
  declare const cn: (...inputs: ClassValue[]) => string;
5
+ /**
6
+ * Renders a slot value, applying `wrapper` only to values that aren't already
7
+ * a React element. Passing an element opts out of the wrapper entirely.
8
+ *
9
+ * **Only use this for content slots where that opt-out is the point** — a
10
+ * `title` the caller may want to supply as their own heading, so we don't end
11
+ * up nesting `<h6><h3>`. Do *not* use it for a wrapper that carries layout the
12
+ * component owns (padding, `shrink-0`, a footer bar): the common case is an
13
+ * element, so the wrapper would be skipped exactly when it's needed, and any
14
+ * `classNames.*` threaded through it would silently do nothing. Write those as
15
+ * a plain `{value != null && <Wrapper>{value}</Wrapper>}`.
16
+ */
5
17
  declare const renderConditional: <T extends React$1.ReactNode>(value: T, wrapper?: (value: NonNullable<T>) => React$1.ReactNode) => React$1.ReactNode;
6
18
  //#endregion
7
19
  export { cn, renderConditional };
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.mts","names":[],"sources":["../src/lib/utils/index.ts"],"mappings":";;;cAKa,QAAS,QAAQ;cAIjB,oBAAqB,UAAU,QAAM,WAChD,OAAO,GACP,WAAW,OAAO,YAAY,OAAO,QAAM,cAC1C,QAAM"}
1
+ {"version":3,"file":"utils.d.mts","names":[],"sources":["../src/lib/utils/index.ts"],"mappings":";;;cAKa,QAAS,QAAQ;;;;;;;;;;;;;cAgBjB,oBAAqB,UAAU,QAAM,WAChD,OAAO,GACP,WAAW,OAAO,YAAY,OAAO,QAAM,cAC1C,QAAM"}
package/dist/utils.mjs CHANGED
@@ -5,6 +5,18 @@ import { twMerge } from "tailwind-merge";
5
5
  const cn = (...inputs) => {
6
6
  return twMerge(clsx(inputs));
7
7
  };
8
+ /**
9
+ * Renders a slot value, applying `wrapper` only to values that aren't already
10
+ * a React element. Passing an element opts out of the wrapper entirely.
11
+ *
12
+ * **Only use this for content slots where that opt-out is the point** — a
13
+ * `title` the caller may want to supply as their own heading, so we don't end
14
+ * up nesting `<h6><h3>`. Do *not* use it for a wrapper that carries layout the
15
+ * component owns (padding, `shrink-0`, a footer bar): the common case is an
16
+ * element, so the wrapper would be skipped exactly when it's needed, and any
17
+ * `classNames.*` threaded through it would silently do nothing. Write those as
18
+ * a plain `{value != null && <Wrapper>{value}</Wrapper>}`.
19
+ */
8
20
  const renderConditional = (value, wrapper) => {
9
21
  if (value === null || value === void 0) return null;
10
22
  if (React$1.isValidElement(value)) return value;
@@ -1 +1 @@
1
- {"version":3,"file":"utils.mjs","names":["React"],"sources":["../src/lib/utils/index.ts"],"sourcesContent":["import * as React from 'react';\n\nimport { type ClassValue, clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport const cn = (...inputs: ClassValue[]) => {\n return twMerge(clsx(inputs));\n};\n\nexport const renderConditional = <T extends React.ReactNode>(\n value: T,\n wrapper?: (value: NonNullable<T>) => React.ReactNode,\n): React.ReactNode => {\n if (value === null || value === undefined) {\n return null;\n }\n\n if (React.isValidElement(value)) {\n return value;\n }\n\n return wrapper ? wrapper(value as NonNullable<T>) : value;\n};\n"],"mappings":";;;;AAKA,MAAa,MAAM,GAAG,WAAyB;CAC7C,OAAO,QAAQ,KAAK,MAAM,CAAC;AAC7B;AAEA,MAAa,qBACX,OACA,YACoB;CACpB,IAAI,UAAU,QAAQ,UAAU,KAAA,GAC9B,OAAO;CAGT,IAAIA,QAAM,eAAe,KAAK,GAC5B,OAAO;CAGT,OAAO,UAAU,QAAQ,KAAuB,IAAI;AACtD"}
1
+ {"version":3,"file":"utils.mjs","names":["React"],"sources":["../src/lib/utils/index.ts"],"sourcesContent":["import * as React from 'react';\n\nimport { type ClassValue, clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport const cn = (...inputs: ClassValue[]) => {\n return twMerge(clsx(inputs));\n};\n\n/**\n * Renders a slot value, applying `wrapper` only to values that aren't already\n * a React element. Passing an element opts out of the wrapper entirely.\n *\n * **Only use this for content slots where that opt-out is the point** — a\n * `title` the caller may want to supply as their own heading, so we don't end\n * up nesting `<h6><h3>`. Do *not* use it for a wrapper that carries layout the\n * component owns (padding, `shrink-0`, a footer bar): the common case is an\n * element, so the wrapper would be skipped exactly when it's needed, and any\n * `classNames.*` threaded through it would silently do nothing. Write those as\n * a plain `{value != null && <Wrapper>{value}</Wrapper>}`.\n */\nexport const renderConditional = <T extends React.ReactNode>(\n value: T,\n wrapper?: (value: NonNullable<T>) => React.ReactNode,\n): React.ReactNode => {\n if (value === null || value === undefined) {\n return null;\n }\n\n if (React.isValidElement(value)) {\n return value;\n }\n\n return wrapper ? wrapper(value as NonNullable<T>) : value;\n};\n"],"mappings":";;;;AAKA,MAAa,MAAM,GAAG,WAAyB;CAC7C,OAAO,QAAQ,KAAK,MAAM,CAAC;AAC7B;;;;;;;;;;;;;AAcA,MAAa,qBACX,OACA,YACoB;CACpB,IAAI,UAAU,QAAQ,UAAU,KAAA,GAC9B,OAAO;CAGT,IAAIA,QAAM,eAAe,KAAK,GAC5B,OAAO;CAGT,OAAO,UAAU,QAAQ,KAAuB,IAAI;AACtD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jbpark/ui-kit",
3
- "version": "7.1.0",
3
+ "version": "8.1.0",
4
4
  "description": "Modern React UI component library built with TypeScript, Tailwind CSS, and Radix UI. Featuring atoms, molecules, organisms and layout templates for building beautiful interfaces.",
5
5
  "keywords": [
6
6
  "react",
@@ -54,6 +54,11 @@
54
54
  "import": "./dist/Button.mjs",
55
55
  "default": "./dist/Button.mjs"
56
56
  },
57
+ "./CodeEditor": {
58
+ "types": "./dist/CodeEditor.d.mts",
59
+ "import": "./dist/CodeEditor.mjs",
60
+ "default": "./dist/CodeEditor.mjs"
61
+ },
57
62
  "./Tag": {
58
63
  "types": "./dist/Tag.d.mts",
59
64
  "import": "./dist/Tag.mjs",
@@ -113,6 +118,8 @@
113
118
  "postpublish": "node scripts/restore-package.mjs"
114
119
  },
115
120
  "devDependencies": {
121
+ "@codemirror/lang-javascript": "^6.2.5",
122
+ "@codemirror/merge": "^6.12.2",
116
123
  "@repo/eslint-config": "workspace:*",
117
124
  "@repo/typescript-config": "workspace:*",
118
125
  "@tailwindcss/postcss": "^4.3.3",
@@ -120,6 +127,9 @@
120
127
  "@types/node": "^26.2.0",
121
128
  "@types/react": "19.2.18",
122
129
  "@types/react-dom": "19.2.4",
130
+ "@uiw/codemirror-theme-vscode": "^4.25.11",
131
+ "@uiw/react-codemirror": "^4.25.11",
132
+ "codemirror": "^6.0.0",
123
133
  "eslint": "^9.39.5",
124
134
  "postcss": "^8.5.26",
125
135
  "rollup-plugin-postcss": "^4.0.2",
@@ -162,7 +172,29 @@
162
172
  "vaul": "^1.1.2"
163
173
  },
164
174
  "peerDependencies": {
175
+ "@codemirror/lang-javascript": "^6.2.5",
176
+ "@codemirror/merge": "^6.12.2",
177
+ "@uiw/codemirror-theme-vscode": "^4.25.11",
178
+ "@uiw/react-codemirror": "^4.25.11",
179
+ "codemirror": "^6.0.0",
165
180
  "react": "^19.0.0",
166
181
  "react-dom": "^19.0.0"
182
+ },
183
+ "peerDependenciesMeta": {
184
+ "@codemirror/lang-javascript": {
185
+ "optional": true
186
+ },
187
+ "@codemirror/merge": {
188
+ "optional": true
189
+ },
190
+ "@uiw/codemirror-theme-vscode": {
191
+ "optional": true
192
+ },
193
+ "@uiw/react-codemirror": {
194
+ "optional": true
195
+ },
196
+ "codemirror": {
197
+ "optional": true
198
+ }
167
199
  }
168
200
  }