@lobehub/ui 6.0.0 → 6.0.1

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 (46) hide show
  1. package/es/Accordion/style.d.mts +1 -1
  2. package/es/Drawer/atoms.mjs +2 -1
  3. package/es/Drawer/atoms.mjs.map +1 -1
  4. package/es/DropdownMenu/atoms.mjs +2 -1
  5. package/es/DropdownMenu/atoms.mjs.map +1 -1
  6. package/es/Modal/atoms.mjs +2 -1
  7. package/es/Modal/atoms.mjs.map +1 -1
  8. package/es/Popover/PopoverInGroup.mjs +2 -1
  9. package/es/Popover/PopoverInGroup.mjs.map +1 -1
  10. package/es/Popover/atoms.mjs +2 -1
  11. package/es/Popover/atoms.mjs.map +1 -1
  12. package/es/Segmented/Segmented.mjs +2 -1
  13. package/es/Segmented/Segmented.mjs.map +1 -1
  14. package/es/Segmented/type.d.mts +7 -0
  15. package/es/Select/atoms.mjs +2 -1
  16. package/es/Select/atoms.mjs.map +1 -1
  17. package/es/Text/style.d.mts +3 -3
  18. package/es/Tooltip/TooltipInGroup.mjs +2 -1
  19. package/es/Tooltip/TooltipInGroup.mjs.map +1 -1
  20. package/es/Tooltip/TooltipStandalone.mjs +2 -1
  21. package/es/Tooltip/TooltipStandalone.mjs.map +1 -1
  22. package/es/hooks/useNativeButton.mjs +1 -0
  23. package/es/hooks/useNativeButton.mjs.map +1 -1
  24. package/es/index.d.mts +3 -2
  25. package/es/index.mjs +3 -2
  26. package/es/styles/index.d.mts +3 -2
  27. package/es/styles/index.mjs +3 -2
  28. package/es/styles/theme/createLobeToken.d.mts +2 -2
  29. package/es/styles/theme/createLobeToken.mjs.map +1 -1
  30. package/es/styles/theme/customNeutral.d.mts +28 -0
  31. package/es/styles/theme/customNeutral.mjs +84 -0
  32. package/es/styles/theme/customNeutral.mjs.map +1 -0
  33. package/es/styles/theme/customPalette.mjs +5 -1
  34. package/es/styles/theme/customPalette.mjs.map +1 -1
  35. package/es/styles/theme/scope.d.mts +3 -3
  36. package/es/styles/theme/scope.mjs +55 -5
  37. package/es/styles/theme/scope.mjs.map +1 -1
  38. package/es/styles/theme/themeCss.d.mts +2 -0
  39. package/es/styles/theme/themeCss.mjs +31 -1
  40. package/es/styles/theme/themeCss.mjs.map +1 -1
  41. package/es/styles/theme/token/neutral.mjs +3 -5
  42. package/es/styles/theme/token/neutral.mjs.map +1 -1
  43. package/es/styles/theme/token/neutralText.d.mts +4 -0
  44. package/es/utils/getElementRef.mjs +17 -0
  45. package/es/utils/getElementRef.mjs.map +1 -0
  46. package/package.json +1 -1
@@ -0,0 +1,84 @@
1
+ import { notifyPaletteChange } from "./customPalette.mjs";
2
+ import gray from "../../color/colors/gray.mjs";
3
+ import { neutralColorScales } from "../../color/neutrals/index.mjs";
4
+ import { neutralText } from "./token/neutralText.mjs";
5
+ import chroma from "chroma-js";
6
+ //#region src/styles/theme/customNeutral.ts
7
+ const HEX = /^#[\da-f]{6}$/i;
8
+ const NAME = /^[\w-]+$/;
9
+ const MAX_CHROMA = .05;
10
+ const isCustomNeutralColor = (value) => !!value && HEX.test(value);
11
+ const registry = /* @__PURE__ */ new Map();
12
+ const isRegisteredNeutral = (name) => !!name && registry.has(name);
13
+ /**
14
+ * Register a full custom neutral scale under `name`, then select it with
15
+ * `setLobeTheme({ neutralColor: name })` / `ThemeScope` / `LobeThemeScript`.
16
+ */
17
+ const registerLobeNeutral = (name, neutral) => {
18
+ if (!NAME.test(name)) throw new Error(`Invalid neutral name "${name}": use letters, digits, _ or -`);
19
+ if (name in neutralColorScales) throw new Error(`"${name}" is a built-in neutral preset`);
20
+ registry.set(name, "scale" in neutral ? neutral : { scale: neutral });
21
+ notifyPaletteChange();
22
+ };
23
+ const tint = (color, h, c) => {
24
+ const source = chroma(color);
25
+ const [l] = source.oklch();
26
+ return chroma.oklch(l, c, h).alpha(source.alpha());
27
+ };
28
+ const tintSteps = (steps, h, c, alpha) => steps.map((step) => alpha ? tint(step, h, c).css() : tint(step, h, c).hex());
29
+ const cache = /* @__PURE__ */ new Map();
30
+ /** Same construction as the presets: the gray ladder's lightness, retinted to the hex's hue. */
31
+ const generateNeutralScale = (color) => {
32
+ const hex = color.toLowerCase();
33
+ const cached = cache.get(hex);
34
+ if (cached) return cached;
35
+ const [, rawC, rawH] = chroma(hex).oklch();
36
+ const h = Number.isNaN(rawH) ? 0 : rawH;
37
+ const c = Math.min(rawC || 0, MAX_CHROMA);
38
+ const base = neutralText.gray;
39
+ const result = {
40
+ scale: {
41
+ dark: tintSteps(gray.dark, h, c, false),
42
+ darkA: tintSteps(gray.darkA, h, c, true),
43
+ light: tintSteps(gray.light, h, c, false),
44
+ lightA: tintSteps(gray.lightA, h, c, true)
45
+ },
46
+ text: {
47
+ dark: tintSteps(base.dark, h, c, false),
48
+ light: tintSteps(base.light, h, c, false)
49
+ }
50
+ };
51
+ cache.set(hex, result);
52
+ return result;
53
+ };
54
+ const ladderOf = (scale, appearance) => {
55
+ const s = scale[appearance];
56
+ return [
57
+ s[12],
58
+ s[10],
59
+ s[8],
60
+ s[6]
61
+ ];
62
+ };
63
+ const resolveNeutral = (neutralColor) => {
64
+ if (isCustomNeutralColor(neutralColor)) return generateNeutralScale(neutralColor);
65
+ const registered = neutralColor ? registry.get(neutralColor) : void 0;
66
+ if (registered) return {
67
+ scale: registered.scale,
68
+ text: {
69
+ dark: registered.text?.dark ?? ladderOf(registered.scale, "dark"),
70
+ light: registered.text?.light ?? ladderOf(registered.scale, "light")
71
+ }
72
+ };
73
+ const preset = neutralColor && neutralColor in neutralColorScales ? neutralColor : void 0;
74
+ return {
75
+ scale: preset && neutralColorScales[preset] || gray,
76
+ text: neutralText[preset || "gray"]
77
+ };
78
+ };
79
+ /** True when the value needs its own injected stylesheet (not a built-in preset). */
80
+ const isCustomNeutral = (value) => isCustomNeutralColor(value) || isRegisteredNeutral(value);
81
+ //#endregion
82
+ export { generateNeutralScale, isCustomNeutral, isCustomNeutralColor, isRegisteredNeutral, registerLobeNeutral, resolveNeutral };
83
+
84
+ //# sourceMappingURL=customNeutral.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"customNeutral.mjs","names":[],"sources":["../../../src/styles/theme/customNeutral.ts"],"sourcesContent":["import chroma from 'chroma-js';\n\nimport { gray } from '@/color/colors';\nimport { neutralColorScales } from '@/color/neutrals';\nimport type { ColorScaleItem } from '@/color/types';\n\nimport type { NeutralColors } from '../customTheme';\nimport { notifyPaletteChange } from './customPalette';\nimport { neutralText,type NeutralTextLadder } from './token/neutralText';\n\n/** Any 6-digit hex: tints the default gray ladder with its hue and chroma. */\nexport type CustomNeutralColor = `#${string}`;\n/** A preset, a 6-digit hex, or a name registered with `registerLobeNeutral`. */\nexport type LobeNeutralColor = NeutralColors | CustomNeutralColor | (string & {});\n\nexport interface LobeNeutralScale {\n /** Full 13-step scale (light / dark solid + alpha), same shape as the presets. */\n scale: ColorScaleItem;\n /** Text ladder [text, secondary, tertiary, quaternary]; defaults to the scale's steps 12/10/8/6. */\n text?: Partial<NeutralTextLadder>;\n}\n\nexport interface ResolvedNeutral {\n scale: ColorScaleItem;\n text: NeutralTextLadder;\n}\n\nconst HEX = /^#[\\da-f]{6}$/i;\nconst NAME = /^[\\w-]+$/;\n// Neutrals stay neutral: presets peak around 0.02 oklch chroma.\nconst MAX_CHROMA = 0.05;\n\nexport const isCustomNeutralColor = (value?: string): value is CustomNeutralColor =>\n !!value && HEX.test(value);\n\nconst registry = new Map<string, LobeNeutralScale>();\n\nexport const isRegisteredNeutral = (name?: string): name is string => !!name && registry.has(name);\n\n/**\n * Register a full custom neutral scale under `name`, then select it with\n * `setLobeTheme({ neutralColor: name })` / `ThemeScope` / `LobeThemeScript`.\n */\nexport const registerLobeNeutral = (name: string, neutral: LobeNeutralScale | ColorScaleItem) => {\n if (!NAME.test(name))\n throw new Error(`Invalid neutral name \"${name}\": use letters, digits, _ or -`);\n if (name in neutralColorScales) throw new Error(`\"${name}\" is a built-in neutral preset`);\n registry.set(name, 'scale' in neutral ? neutral : { scale: neutral });\n notifyPaletteChange();\n};\n\nconst tint = (color: string, h: number, c: number) => {\n const source = chroma(color);\n const [l] = source.oklch();\n return chroma.oklch(l, c, h).alpha(source.alpha());\n};\n\nconst tintSteps = <T extends readonly string[]>(steps: T, h: number, c: number, alpha: boolean) =>\n steps.map((step) => (alpha ? tint(step, h, c).css() : tint(step, h, c).hex())) as unknown as T;\n\nconst cache = new Map<string, ResolvedNeutral>();\n\n/** Same construction as the presets: the gray ladder's lightness, retinted to the hex's hue. */\nexport const generateNeutralScale = (color: CustomNeutralColor): ResolvedNeutral => {\n const hex = color.toLowerCase();\n const cached = cache.get(hex);\n if (cached) return cached;\n\n const [, rawC, rawH] = chroma(hex).oklch();\n const h = Number.isNaN(rawH) ? 0 : rawH;\n const c = Math.min(rawC || 0, MAX_CHROMA);\n const base = neutralText.gray;\n const result: ResolvedNeutral = {\n scale: {\n dark: tintSteps(gray.dark, h, c, false),\n darkA: tintSteps(gray.darkA, h, c, true),\n light: tintSteps(gray.light, h, c, false),\n lightA: tintSteps(gray.lightA, h, c, true),\n },\n text: {\n dark: tintSteps(base.dark, h, c, false),\n light: tintSteps(base.light, h, c, false),\n },\n };\n\n cache.set(hex, result);\n return result;\n};\n\nconst ladderOf = (scale: ColorScaleItem, appearance: 'light' | 'dark') => {\n const s = scale[appearance];\n return [s[12], s[10], s[8], s[6]] as [string, string, string, string];\n};\n\nexport const resolveNeutral = (neutralColor?: LobeNeutralColor): ResolvedNeutral => {\n if (isCustomNeutralColor(neutralColor)) return generateNeutralScale(neutralColor);\n const registered = neutralColor ? registry.get(neutralColor) : undefined;\n if (registered)\n return {\n scale: registered.scale,\n text: {\n dark: registered.text?.dark ?? ladderOf(registered.scale, 'dark'),\n light: registered.text?.light ?? ladderOf(registered.scale, 'light'),\n },\n };\n const preset =\n neutralColor && neutralColor in neutralColorScales\n ? (neutralColor as NeutralColors)\n : undefined;\n return {\n scale: (preset && neutralColorScales[preset]) || gray,\n text: neutralText[preset || 'gray'],\n };\n};\n\n/** True when the value needs its own injected stylesheet (not a built-in preset). */\nexport const isCustomNeutral = (value?: string): value is string =>\n isCustomNeutralColor(value) || isRegisteredNeutral(value);\n"],"mappings":";;;;;;AA2BA,MAAM,MAAM;AACZ,MAAM,OAAO;AAEb,MAAM,aAAa;AAEnB,MAAa,wBAAwB,UACnC,CAAC,CAAC,SAAS,IAAI,KAAK,KAAK;AAE3B,MAAM,2BAAW,IAAI,IAA8B;AAEnD,MAAa,uBAAuB,SAAkC,CAAC,CAAC,QAAQ,SAAS,IAAI,IAAI;;;;;AAMjG,MAAa,uBAAuB,MAAc,YAA+C;CAC/F,IAAI,CAAC,KAAK,KAAK,IAAI,GACjB,MAAM,IAAI,MAAM,yBAAyB,KAAK,+BAA+B;CAC/E,IAAI,QAAQ,oBAAoB,MAAM,IAAI,MAAM,IAAI,KAAK,+BAA+B;CACxF,SAAS,IAAI,MAAM,WAAW,UAAU,UAAU,EAAE,OAAO,QAAQ,CAAC;CACpE,oBAAoB;AACtB;AAEA,MAAM,QAAQ,OAAe,GAAW,MAAc;CACpD,MAAM,SAAS,OAAO,KAAK;CAC3B,MAAM,CAAC,KAAK,OAAO,MAAM;CACzB,OAAO,OAAO,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,MAAM,OAAO,MAAM,CAAC;AACnD;AAEA,MAAM,aAA0C,OAAU,GAAW,GAAW,UAC9E,MAAM,KAAK,SAAU,QAAQ,KAAK,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,KAAK,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAAE;AAE/E,MAAM,wBAAQ,IAAI,IAA6B;;AAG/C,MAAa,wBAAwB,UAA+C;CAClF,MAAM,MAAM,MAAM,YAAY;CAC9B,MAAM,SAAS,MAAM,IAAI,GAAG;CAC5B,IAAI,QAAQ,OAAO;CAEnB,MAAM,GAAG,MAAM,QAAQ,OAAO,GAAG,CAAC,CAAC,MAAM;CACzC,MAAM,IAAI,OAAO,MAAM,IAAI,IAAI,IAAI;CACnC,MAAM,IAAI,KAAK,IAAI,QAAQ,GAAG,UAAU;CACxC,MAAM,OAAO,YAAY;CACzB,MAAM,SAA0B;EAC9B,OAAO;GACL,MAAM,UAAU,KAAK,MAAM,GAAG,GAAG,KAAK;GACtC,OAAO,UAAU,KAAK,OAAO,GAAG,GAAG,IAAI;GACvC,OAAO,UAAU,KAAK,OAAO,GAAG,GAAG,KAAK;GACxC,QAAQ,UAAU,KAAK,QAAQ,GAAG,GAAG,IAAI;EAC3C;EACA,MAAM;GACJ,MAAM,UAAU,KAAK,MAAM,GAAG,GAAG,KAAK;GACtC,OAAO,UAAU,KAAK,OAAO,GAAG,GAAG,KAAK;EAC1C;CACF;CAEA,MAAM,IAAI,KAAK,MAAM;CACrB,OAAO;AACT;AAEA,MAAM,YAAY,OAAuB,eAAiC;CACxE,MAAM,IAAI,MAAM;CAChB,OAAO;EAAC,EAAE;EAAK,EAAE;EAAK,EAAE;EAAI,EAAE;CAAE;AAClC;AAEA,MAAa,kBAAkB,iBAAqD;CAClF,IAAI,qBAAqB,YAAY,GAAG,OAAO,qBAAqB,YAAY;CAChF,MAAM,aAAa,eAAe,SAAS,IAAI,YAAY,IAAI,KAAA;CAC/D,IAAI,YACF,OAAO;EACL,OAAO,WAAW;EAClB,MAAM;GACJ,MAAM,WAAW,MAAM,QAAQ,SAAS,WAAW,OAAO,MAAM;GAChE,OAAO,WAAW,MAAM,SAAS,SAAS,WAAW,OAAO,OAAO;EACrE;CACF;CACF,MAAM,SACJ,gBAAgB,gBAAgB,qBAC3B,eACD,KAAA;CACN,OAAO;EACL,OAAQ,UAAU,mBAAmB,WAAY;EACjD,MAAM,YAAY,UAAU;CAC9B;AACF;;AAGA,MAAa,mBAAmB,UAC9B,qBAAqB,KAAK,KAAK,oBAAoB,KAAK"}
@@ -7,6 +7,10 @@ let revision = 0;
7
7
  const registerLobePalette = (name, palette) => {
8
8
  if (!NAME.test(name)) throw new Error(`Invalid palette name "${name}": use letters, digits, _ or -`);
9
9
  registry.set(name, palette);
10
+ notifyPaletteChange();
11
+ };
12
+ /** Bumps the revision so mounted scopes / useTheme pick up a re-registered palette or neutral. */
13
+ const notifyPaletteChange = () => {
10
14
  revision++;
11
15
  for (const listener of listeners) listener();
12
16
  };
@@ -23,6 +27,6 @@ const resolveStatusScales = (name) => isRegisteredPalette(name) ? {
23
27
  ...registry.get(name)
24
28
  } : statusColorScales;
25
29
  //#endregion
26
- export { getPaletteRevision, isRegisteredPalette, registerLobePalette, resolveStatusScales, subscribePalettes };
30
+ export { getPaletteRevision, isRegisteredPalette, notifyPaletteChange, registerLobePalette, resolveStatusScales, subscribePalettes };
27
31
 
28
32
  //# sourceMappingURL=customPalette.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"customPalette.mjs","names":[],"sources":["../../../src/styles/theme/customPalette.ts"],"sourcesContent":["import { type StatusColorScales, statusColorScales } from '@/color/status';\n\nexport type LobeStatusPalette = Partial<StatusColorScales>;\n\nconst NAME = /^[\\w-]+$/;\n\nconst registry = new Map<string, LobeStatusPalette>();\nconst listeners = new Set<() => void>();\nlet revision = 0;\n\nexport const registerLobePalette = (name: string, palette: LobeStatusPalette) => {\n if (!NAME.test(name))\n throw new Error(`Invalid palette name \"${name}\": use letters, digits, _ or -`);\n registry.set(name, palette);\n revision++;\n for (const listener of listeners) listener();\n};\n\nexport const getPaletteRevision = () => revision;\n\nexport const subscribePalettes = (listener: () => void) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n};\n\nexport const isRegisteredPalette = (name?: string): name is string => !!name && registry.has(name);\n\nexport const resolveStatusScales = (name?: string): StatusColorScales =>\n isRegisteredPalette(name) ? { ...statusColorScales, ...registry.get(name) } : statusColorScales;\n"],"mappings":";;AAIA,MAAM,OAAO;AAEb,MAAM,2BAAW,IAAI,IAA+B;AACpD,MAAM,4BAAY,IAAI,IAAgB;AACtC,IAAI,WAAW;AAEf,MAAa,uBAAuB,MAAc,YAA+B;CAC/E,IAAI,CAAC,KAAK,KAAK,IAAI,GACjB,MAAM,IAAI,MAAM,yBAAyB,KAAK,+BAA+B;CAC/E,SAAS,IAAI,MAAM,OAAO;CAC1B;CACA,KAAK,MAAM,YAAY,WAAW,SAAS;AAC7C;AAEA,MAAa,2BAA2B;AAExC,MAAa,qBAAqB,aAAyB;CACzD,UAAU,IAAI,QAAQ;CACtB,aAAa;EACX,UAAU,OAAO,QAAQ;CAC3B;AACF;AAEA,MAAa,uBAAuB,SAAkC,CAAC,CAAC,QAAQ,SAAS,IAAI,IAAI;AAEjG,MAAa,uBAAuB,SAClC,oBAAoB,IAAI,IAAI;CAAE,GAAG;CAAmB,GAAG,SAAS,IAAI,IAAI;AAAE,IAAI"}
1
+ {"version":3,"file":"customPalette.mjs","names":[],"sources":["../../../src/styles/theme/customPalette.ts"],"sourcesContent":["import { type StatusColorScales, statusColorScales } from '@/color/status';\n\nexport type LobeStatusPalette = Partial<StatusColorScales>;\n\nconst NAME = /^[\\w-]+$/;\n\nconst registry = new Map<string, LobeStatusPalette>();\nconst listeners = new Set<() => void>();\nlet revision = 0;\n\nexport const registerLobePalette = (name: string, palette: LobeStatusPalette) => {\n if (!NAME.test(name))\n throw new Error(`Invalid palette name \"${name}\": use letters, digits, _ or -`);\n registry.set(name, palette);\n notifyPaletteChange();\n};\n\n/** Bumps the revision so mounted scopes / useTheme pick up a re-registered palette or neutral. */\nexport const notifyPaletteChange = () => {\n revision++;\n for (const listener of listeners) listener();\n};\n\nexport const getPaletteRevision = () => revision;\n\nexport const subscribePalettes = (listener: () => void) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n};\n\nexport const isRegisteredPalette = (name?: string): name is string => !!name && registry.has(name);\n\nexport const resolveStatusScales = (name?: string): StatusColorScales =>\n isRegisteredPalette(name) ? { ...statusColorScales, ...registry.get(name) } : statusColorScales;\n"],"mappings":";;AAIA,MAAM,OAAO;AAEb,MAAM,2BAAW,IAAI,IAA+B;AACpD,MAAM,4BAAY,IAAI,IAAgB;AACtC,IAAI,WAAW;AAEf,MAAa,uBAAuB,MAAc,YAA+B;CAC/E,IAAI,CAAC,KAAK,KAAK,IAAI,GACjB,MAAM,IAAI,MAAM,yBAAyB,KAAK,+BAA+B;CAC/E,SAAS,IAAI,MAAM,OAAO;CAC1B,oBAAoB;AACtB;;AAGA,MAAa,4BAA4B;CACvC;CACA,KAAK,MAAM,YAAY,WAAW,SAAS;AAC7C;AAEA,MAAa,2BAA2B;AAExC,MAAa,qBAAqB,aAAyB;CACzD,UAAU,IAAI,QAAQ;CACtB,aAAa;EACX,UAAU,OAAO,QAAQ;CAC3B;AACF;AAEA,MAAa,uBAAuB,SAAkC,CAAC,CAAC,QAAQ,SAAS,IAAI,IAAI;AAEjG,MAAa,uBAAuB,SAClC,oBAAoB,IAAI,IAAI;CAAE,GAAG;CAAmB,GAAG,SAAS,IAAI,IAAI;AAAE,IAAI"}
@@ -1,4 +1,4 @@
1
- import { NeutralColors } from "../customTheme.mjs";
1
+ import { LobeNeutralColor } from "./customNeutral.mjs";
2
2
  import { LobePrimaryColor } from "./customPrimary.mjs";
3
3
  import { LobeToken } from "./createLobeToken.mjs";
4
4
  import { CSSProperties, ReactNode } from "react";
@@ -6,7 +6,7 @@ import { CSSProperties, ReactNode } from "react";
6
6
  export type LobeAppearance = 'light' | 'dark';
7
7
  export interface LobeThemeState {
8
8
  appearance: LobeAppearance;
9
- neutralColor?: NeutralColors;
9
+ neutralColor?: LobeNeutralColor;
10
10
  palette?: string;
11
11
  primaryColor?: LobePrimaryColor;
12
12
  }
@@ -40,7 +40,7 @@ export type ResponsiveState = Record<Breakpoint | 'mobile' | 'tablet' | 'laptop'
40
40
  export declare const useResponsive: () => ResponsiveState;
41
41
  export interface LobeThemeScriptProps {
42
42
  appearance?: LobeAppearance | 'system';
43
- neutralColor?: NeutralColors;
43
+ neutralColor?: LobeNeutralColor;
44
44
  nonce?: string;
45
45
  palette?: string;
46
46
  primaryColor?: LobePrimaryColor;
@@ -1,9 +1,10 @@
1
1
  "use client";
2
2
  import { getPaletteRevision, isRegisteredPalette, subscribePalettes } from "./customPalette.mjs";
3
+ import { isCustomNeutral } from "./customNeutral.mjs";
3
4
  import { isCustomPrimaryColor } from "./customPrimary.mjs";
4
5
  import { staticToken } from "./token/static.mjs";
5
6
  import { createLobeToken } from "./createLobeToken.mjs";
6
- import { getCustomPaletteCss, getCustomPrimaryCss } from "./themeCss.mjs";
7
+ import { getCustomNeutralCss, getCustomPaletteCss, getCustomPrimaryCss, getPaletteNeutralCss } from "./themeCss.mjs";
7
8
  import { createContext, use, useMemo, useSyncExternalStore } from "react";
8
9
  import { jsx, jsxs } from "react/jsx-runtime";
9
10
  //#region src/styles/theme/scope.tsx
@@ -29,6 +30,22 @@ const ensureCustomPrimaryStyle = (color, doc) => {
29
30
  style.textContent = getCustomPrimaryCss(color);
30
31
  doc.head.append(style);
31
32
  };
33
+ const normalizeNeutral = (color) => color?.startsWith("#") ? color.toLowerCase() : color;
34
+ /** Encodes hex vs registered name so `#0e1010` and a neutral named `0e1010` never collide. */
35
+ const neutralKey = (value) => value.startsWith("#") ? `hex-${value.slice(1)}` : `name-${value}`;
36
+ const neutralStyleId = (value) => `lobe-neutral-${neutralKey(value)}`;
37
+ /** Written every time (a re-registered neutral must replace its old rules). */
38
+ const ensureNeutralStyle = (value, doc) => {
39
+ const id = neutralStyleId(value);
40
+ let style = doc.getElementById(id);
41
+ if (!style) {
42
+ style = doc.createElement("style");
43
+ style.id = id;
44
+ doc.head.append(style);
45
+ }
46
+ const css = getCustomNeutralCss(value);
47
+ if (style.textContent !== css) style.textContent = css;
48
+ };
32
49
  const paletteStyleId = (name) => `lobe-palette-${name}`;
33
50
  const ensurePaletteStyle = (name, doc) => {
34
51
  const id = paletteStyleId(name);
@@ -41,6 +58,18 @@ const ensurePaletteStyle = (name, doc) => {
41
58
  const css = getCustomPaletteCss(name);
42
59
  if (style.textContent !== css) style.textContent = css;
43
60
  };
61
+ const paletteNeutralStyleId = (name, neutral) => `lobe-palette-${name}--neutral-${neutralKey(neutral)}`;
62
+ const ensurePaletteNeutralStyle = (name, neutral, doc) => {
63
+ const id = paletteNeutralStyleId(name, neutral);
64
+ let style = doc.getElementById(id);
65
+ if (!style) {
66
+ style = doc.createElement("style");
67
+ style.id = id;
68
+ doc.head.append(style);
69
+ }
70
+ const css = getPaletteNeutralCss(name, neutral);
71
+ if (style.textContent !== css) style.textContent = css;
72
+ };
44
73
  const setLobeTheme = (state, root = document.documentElement) => {
45
74
  const next = {
46
75
  ...readThemeState(root),
@@ -48,7 +77,12 @@ const setLobeTheme = (state, root = document.documentElement) => {
48
77
  };
49
78
  next.primaryColor = normalizePrimary(next.primaryColor);
50
79
  if (isCustomPrimaryColor(next.primaryColor)) ensureCustomPrimaryStyle(next.primaryColor, root.ownerDocument);
51
- if (isRegisteredPalette(next.palette)) ensurePaletteStyle(next.palette, root.ownerDocument);
80
+ next.neutralColor = normalizeNeutral(next.neutralColor);
81
+ if (isCustomNeutral(next.neutralColor)) ensureNeutralStyle(next.neutralColor, root.ownerDocument);
82
+ if (isRegisteredPalette(next.palette)) {
83
+ ensurePaletteStyle(next.palette, root.ownerDocument);
84
+ if (next.neutralColor) ensurePaletteNeutralStyle(next.palette, next.neutralColor, root.ownerDocument);
85
+ }
52
86
  for (const [name, value] of Object.entries(toAttributes(next))) if (value) root.setAttribute(name, value);
53
87
  else root.removeAttribute(name);
54
88
  };
@@ -85,7 +119,7 @@ const ThemeScope = ({ appearance, children, className, neutralColor, palette, pr
85
119
  const parent = useThemeState();
86
120
  const state = useMemo(() => ({
87
121
  appearance: appearance ?? parent.appearance,
88
- neutralColor: neutralColor ?? parent.neutralColor,
122
+ neutralColor: normalizeNeutral(neutralColor) ?? parent.neutralColor,
89
123
  palette: palette ?? parent.palette,
90
124
  primaryColor: normalizePrimary(primaryColor) ?? parent.primaryColor
91
125
  }), [
@@ -98,6 +132,7 @@ const ThemeScope = ({ appearance, children, className, neutralColor, palette, pr
98
132
  const customPrimary = isCustomPrimaryColor(state.primaryColor) ? state.primaryColor : void 0;
99
133
  const paletteRevision = usePaletteRevision();
100
134
  const customPalette = isRegisteredPalette(state.palette) ? state.palette : void 0;
135
+ const customNeutral = isCustomNeutral(state.neutralColor) ? state.neutralColor : void 0;
101
136
  return /* @__PURE__ */ jsxs(ThemeScopeContext, {
102
137
  value: state,
103
138
  children: [
@@ -106,11 +141,21 @@ const ThemeScope = ({ appearance, children, className, neutralColor, palette, pr
106
141
  precedence: "lobe-theme",
107
142
  children: getCustomPrimaryCss(customPrimary)
108
143
  }),
144
+ customNeutral && /* @__PURE__ */ jsx("style", {
145
+ href: `${neutralStyleId(customNeutral)}-${paletteRevision}`,
146
+ precedence: "lobe-theme",
147
+ children: getCustomNeutralCss(customNeutral)
148
+ }),
109
149
  customPalette && /* @__PURE__ */ jsx("style", {
110
150
  href: `${paletteStyleId(customPalette)}-${paletteRevision}`,
111
151
  precedence: "lobe-theme",
112
152
  children: getCustomPaletteCss(customPalette)
113
153
  }),
154
+ customPalette && state.neutralColor && /* @__PURE__ */ jsx("style", {
155
+ href: `${paletteNeutralStyleId(customPalette, state.neutralColor)}-${paletteRevision}`,
156
+ precedence: "lobe-theme",
157
+ children: getPaletteNeutralCss(customPalette, state.neutralColor)
158
+ }),
114
159
  /* @__PURE__ */ jsx("div", {
115
160
  className,
116
161
  style,
@@ -185,11 +230,16 @@ const LobeThemeScript = ({ appearance, neutralColor, nonce, palette, primaryColo
185
230
  const primary = normalizePrimary(primaryColor);
186
231
  const styles = [];
187
232
  if (isCustomPrimaryColor(primary)) styles.push([customPrimaryStyleId(primary), getCustomPrimaryCss(primary)]);
188
- if (isRegisteredPalette(palette)) styles.push([paletteStyleId(palette), getCustomPaletteCss(palette)]);
233
+ const neutral = normalizeNeutral(neutralColor);
234
+ if (isCustomNeutral(neutral)) styles.push([neutralStyleId(neutral), getCustomNeutralCss(neutral)]);
235
+ if (isRegisteredPalette(palette)) {
236
+ styles.push([paletteStyleId(palette), getCustomPaletteCss(palette)]);
237
+ if (neutral) styles.push([paletteNeutralStyleId(palette, neutral), getPaletteNeutralCss(palette, neutral)]);
238
+ }
189
239
  const script = `(function(a,p,n,q,t){var d=document.documentElement;if(a==='system')a=matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light';if(a)d.setAttribute('data-theme',a);else if(!d.hasAttribute('data-theme'))d.setAttribute('data-theme','light');if(p)d.setAttribute('data-primary-color',p);if(n)d.setAttribute('data-neutral-color',n);if(q)d.setAttribute('data-palette',q);t.forEach(function(x){if(document.getElementById(x[0]))return;var s=document.createElement('style');s.id=x[0];s.textContent=x[1];document.head.appendChild(s);});}).apply(null,${JSON.stringify([
190
240
  appearance ?? null,
191
241
  primary ?? null,
192
- neutralColor ?? null,
242
+ neutral ?? null,
193
243
  palette ?? null,
194
244
  styles
195
245
  ])})`;
@@ -1 +1 @@
1
- {"version":3,"file":"scope.mjs","names":[],"sources":["../../../src/styles/theme/scope.tsx"],"sourcesContent":["'use client';\n\nimport {\n createContext,\n type CSSProperties,\n type ReactNode,\n use,\n useMemo,\n useSyncExternalStore,\n} from 'react';\n\nimport type { NeutralColors } from '../customTheme';\nimport { createLobeToken, type LobeToken } from './createLobeToken';\nimport { getPaletteRevision, isRegisteredPalette, subscribePalettes } from './customPalette';\nimport {\n type CustomPrimaryColor,\n isCustomPrimaryColor,\n type LobePrimaryColor,\n} from './customPrimary';\nimport { getCustomPaletteCss, getCustomPrimaryCss } from './themeCss';\nimport { staticToken } from './token/static';\n\nexport type LobeAppearance = 'light' | 'dark';\n\nexport interface LobeThemeState {\n appearance: LobeAppearance;\n neutralColor?: NeutralColors;\n palette?: string;\n primaryColor?: LobePrimaryColor;\n}\n\nconst ATTRIBUTES = ['data-theme', 'data-primary-color', 'data-neutral-color', 'data-palette'];\n\nconst toAttributes = ({ appearance, neutralColor, palette, primaryColor }: LobeThemeState) => ({\n 'data-neutral-color': neutralColor,\n 'data-palette': palette,\n 'data-primary-color': primaryColor,\n 'data-theme': appearance,\n});\n\nconst normalizePrimary = (color?: LobePrimaryColor) =>\n isCustomPrimaryColor(color) ? (color.toLowerCase() as CustomPrimaryColor) : color;\n\nconst customPrimaryStyleId = (color: CustomPrimaryColor) => `lobe-primary-${color.slice(1)}`;\n\nconst ensureCustomPrimaryStyle = (color: CustomPrimaryColor, doc: Document) => {\n const id = customPrimaryStyleId(color);\n if (doc.getElementById(id)) return;\n const style = doc.createElement('style');\n style.id = id;\n style.textContent = getCustomPrimaryCss(color);\n doc.head.append(style);\n};\n\nconst paletteStyleId = (name: string) => `lobe-palette-${name}`;\n\nconst ensurePaletteStyle = (name: string, doc: Document) => {\n const id = paletteStyleId(name);\n let style = doc.getElementById(id);\n if (!style) {\n style = doc.createElement('style');\n style.id = id;\n doc.head.append(style);\n }\n const css = getCustomPaletteCss(name);\n if (style.textContent !== css) style.textContent = css;\n};\n\nexport const setLobeTheme = (\n state: Partial<LobeThemeState>,\n root: HTMLElement = document.documentElement,\n) => {\n const next = { ...readThemeState(root), ...state };\n next.primaryColor = normalizePrimary(next.primaryColor);\n if (isCustomPrimaryColor(next.primaryColor))\n ensureCustomPrimaryStyle(next.primaryColor, root.ownerDocument);\n if (isRegisteredPalette(next.palette)) ensurePaletteStyle(next.palette, root.ownerDocument);\n for (const [name, value] of Object.entries(toAttributes(next))) {\n if (value) root.setAttribute(name, value);\n else root.removeAttribute(name);\n }\n};\n\nconst readThemeState = (el: HTMLElement): LobeThemeState => ({\n appearance: el.dataset.theme === 'dark' ? 'dark' : 'light',\n neutralColor: (el.dataset.neutralColor as NeutralColors) || undefined,\n palette: el.dataset.palette || undefined,\n primaryColor: (el.dataset.primaryColor as LobePrimaryColor) || undefined,\n});\n\nconst stateKey = (state: LobeThemeState) =>\n `${state.appearance}|${state.primaryColor ?? ''}|${state.neutralColor ?? ''}|${state.palette ?? ''}`;\n\nconst usePaletteRevision = () =>\n useSyncExternalStore(subscribePalettes, getPaletteRevision, getPaletteRevision);\n\nconst SERVER_STATE: LobeThemeState = { appearance: 'light' };\nlet rootSnapshot: LobeThemeState = SERVER_STATE;\n\nconst getRootSnapshot = () => {\n const next = readThemeState(document.documentElement);\n if (stateKey(next) !== stateKey(rootSnapshot)) rootSnapshot = next;\n return rootSnapshot;\n};\n\nconst subscribeRoot = (onChange: () => void) => {\n const observer = new MutationObserver(onChange);\n observer.observe(document.documentElement, { attributeFilter: ATTRIBUTES, attributes: true });\n return () => observer.disconnect();\n};\n\nconst ThemeScopeContext = createContext<LobeThemeState | null>(null);\n\nconst useThemeState = () => {\n const scoped = use(ThemeScopeContext);\n const root = useSyncExternalStore(subscribeRoot, getRootSnapshot, () => SERVER_STATE);\n return scoped ?? root;\n};\n\nexport interface ThemeScopeProps extends Partial<LobeThemeState> {\n children?: ReactNode;\n className?: string;\n style?: CSSProperties;\n}\n\nexport const ThemeScope = ({\n appearance,\n children,\n className,\n neutralColor,\n palette,\n primaryColor,\n style,\n}: ThemeScopeProps) => {\n const parent = useThemeState();\n const state = useMemo<LobeThemeState>(\n () => ({\n appearance: appearance ?? parent.appearance,\n neutralColor: neutralColor ?? parent.neutralColor,\n palette: palette ?? parent.palette,\n primaryColor: normalizePrimary(primaryColor) ?? parent.primaryColor,\n }),\n [appearance, neutralColor, palette, primaryColor, parent],\n );\n const customPrimary = isCustomPrimaryColor(state.primaryColor) ? state.primaryColor : undefined;\n const paletteRevision = usePaletteRevision();\n const customPalette = isRegisteredPalette(state.palette) ? state.palette : undefined;\n\n return (\n <ThemeScopeContext value={state}>\n {customPrimary && (\n <style href={customPrimaryStyleId(customPrimary)} precedence=\"lobe-theme\">\n {getCustomPrimaryCss(customPrimary)}\n </style>\n )}\n {customPalette && (\n <style href={`${paletteStyleId(customPalette)}-${paletteRevision}`} precedence=\"lobe-theme\">\n {getCustomPaletteCss(customPalette)}\n </style>\n )}\n <div className={className} style={style} {...toAttributes(state)}>\n {children}\n </div>\n </ThemeScopeContext>\n );\n};\n\nexport const useThemeMode = () => {\n const { appearance } = useThemeState();\n return useMemo(() => ({ appearance, isDarkMode: appearance === 'dark' }), [appearance]);\n};\n\nexport type LobeTheme = LobeToken & { appearance: LobeAppearance; isDarkMode: boolean };\n\nconst themeCache = new Map<string, LobeTheme>();\n\nexport const useTheme = (): LobeTheme => {\n const state = useThemeState();\n const key = `${stateKey(state)}@${usePaletteRevision()}`;\n let theme = themeCache.get(key);\n if (!theme) {\n theme = {\n ...createLobeToken(state),\n appearance: state.appearance,\n isDarkMode: state.appearance === 'dark',\n };\n themeCache.set(key, theme);\n }\n return theme;\n};\n\nconst breakpointQueries = {\n lg: `(min-width: ${staticToken.screenLG}px)`,\n md: `(min-width: ${staticToken.screenMD}px)`,\n sm: `(min-width: ${staticToken.screenSM}px)`,\n xl: `(min-width: ${staticToken.screenXL}px)`,\n xs: `(max-width: ${staticToken.screenXSMax}px)`,\n xxl: `(min-width: ${staticToken.screenXXL}px)`,\n xxxl: `(min-width: ${staticToken.screenXXXL}px)`,\n};\n\ntype Breakpoint = keyof typeof breakpointQueries;\nexport type ResponsiveState = Record<\n Breakpoint | 'mobile' | 'tablet' | 'laptop' | 'desktop',\n boolean\n>;\n\nconst toResponsive = (matches: Record<Breakpoint, boolean>): ResponsiveState => ({\n ...matches,\n desktop: matches.xxl,\n laptop: matches.lg,\n mobile: matches.xs,\n tablet: matches.md,\n});\n\nconst SERVER_RESPONSIVE = toResponsive({\n lg: false,\n md: false,\n sm: false,\n xl: false,\n xs: false,\n xxl: false,\n xxxl: false,\n});\nlet responsiveSnapshot = SERVER_RESPONSIVE;\n\nconst getResponsiveSnapshot = () => {\n const matches = Object.fromEntries(\n Object.entries(breakpointQueries).map(([key, query]) => [key, matchMedia(query).matches]),\n ) as Record<Breakpoint, boolean>;\n const changed = (Object.keys(matches) as Breakpoint[]).some(\n (key) => matches[key] !== responsiveSnapshot[key],\n );\n if (changed) responsiveSnapshot = toResponsive(matches);\n return responsiveSnapshot;\n};\n\nconst subscribeResponsive = (onChange: () => void) => {\n const lists = Object.values(breakpointQueries).map((query) => matchMedia(query));\n for (const list of lists) list.addEventListener('change', onChange);\n return () => {\n for (const list of lists) list.removeEventListener('change', onChange);\n };\n};\n\nexport const useResponsive = () =>\n useSyncExternalStore(subscribeResponsive, getResponsiveSnapshot, () => SERVER_RESPONSIVE);\n\nexport interface LobeThemeScriptProps {\n appearance?: LobeAppearance | 'system';\n neutralColor?: NeutralColors;\n nonce?: string;\n palette?: string;\n primaryColor?: LobePrimaryColor;\n}\n\nexport const LobeThemeScript = ({\n appearance,\n neutralColor,\n nonce,\n palette,\n primaryColor,\n}: LobeThemeScriptProps) => {\n const primary = normalizePrimary(primaryColor);\n const styles: [string, string][] = [];\n if (isCustomPrimaryColor(primary))\n styles.push([customPrimaryStyleId(primary), getCustomPrimaryCss(primary)]);\n if (isRegisteredPalette(palette))\n styles.push([paletteStyleId(palette), getCustomPaletteCss(palette)]);\n const args = JSON.stringify([\n appearance ?? null,\n primary ?? null,\n neutralColor ?? null,\n palette ?? null,\n styles,\n ]);\n const script = `(function(a,p,n,q,t){var d=document.documentElement;if(a==='system')a=matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light';if(a)d.setAttribute('data-theme',a);else if(!d.hasAttribute('data-theme'))d.setAttribute('data-theme','light');if(p)d.setAttribute('data-primary-color',p);if(n)d.setAttribute('data-neutral-color',n);if(q)d.setAttribute('data-palette',q);t.forEach(function(x){if(document.getElementById(x[0]))return;var s=document.createElement('style');s.id=x[0];s.textContent=x[1];document.head.appendChild(s);});}).apply(null,${args})`;\n\n return (\n <script suppressHydrationWarning dangerouslySetInnerHTML={{ __html: script }} nonce={nonce} />\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"scope.mjs","names":[],"sources":["../../../src/styles/theme/scope.tsx"],"sourcesContent":["'use client';\n\nimport {\n createContext,\n type CSSProperties,\n type ReactNode,\n use,\n useMemo,\n useSyncExternalStore,\n} from 'react';\n\nimport { createLobeToken, type LobeToken } from './createLobeToken';\nimport { isCustomNeutral, type LobeNeutralColor } from './customNeutral';\nimport { getPaletteRevision, isRegisteredPalette, subscribePalettes } from './customPalette';\nimport {\n type CustomPrimaryColor,\n isCustomPrimaryColor,\n type LobePrimaryColor,\n} from './customPrimary';\nimport {\n getCustomNeutralCss,\n getCustomPaletteCss,\n getCustomPrimaryCss,\n getPaletteNeutralCss,\n} from './themeCss';\nimport { staticToken } from './token/static';\n\nexport type LobeAppearance = 'light' | 'dark';\n\nexport interface LobeThemeState {\n appearance: LobeAppearance;\n neutralColor?: LobeNeutralColor;\n palette?: string;\n primaryColor?: LobePrimaryColor;\n}\n\nconst ATTRIBUTES = ['data-theme', 'data-primary-color', 'data-neutral-color', 'data-palette'];\n\nconst toAttributes = ({ appearance, neutralColor, palette, primaryColor }: LobeThemeState) => ({\n 'data-neutral-color': neutralColor,\n 'data-palette': palette,\n 'data-primary-color': primaryColor,\n 'data-theme': appearance,\n});\n\nconst normalizePrimary = (color?: LobePrimaryColor) =>\n isCustomPrimaryColor(color) ? (color.toLowerCase() as CustomPrimaryColor) : color;\n\nconst customPrimaryStyleId = (color: CustomPrimaryColor) => `lobe-primary-${color.slice(1)}`;\n\nconst ensureCustomPrimaryStyle = (color: CustomPrimaryColor, doc: Document) => {\n const id = customPrimaryStyleId(color);\n if (doc.getElementById(id)) return;\n const style = doc.createElement('style');\n style.id = id;\n style.textContent = getCustomPrimaryCss(color);\n doc.head.append(style);\n};\n\nconst normalizeNeutral = (color?: LobeNeutralColor) =>\n color?.startsWith('#') ? color.toLowerCase() : color;\n\n/** Encodes hex vs registered name so `#0e1010` and a neutral named `0e1010` never collide. */\nconst neutralKey = (value: string) =>\n value.startsWith('#') ? `hex-${value.slice(1)}` : `name-${value}`;\n\nconst neutralStyleId = (value: string) => `lobe-neutral-${neutralKey(value)}`;\n\n/** Written every time (a re-registered neutral must replace its old rules). */\nconst ensureNeutralStyle = (value: string, doc: Document) => {\n const id = neutralStyleId(value);\n let style = doc.getElementById(id);\n if (!style) {\n style = doc.createElement('style');\n style.id = id;\n doc.head.append(style);\n }\n const css = getCustomNeutralCss(value);\n if (style.textContent !== css) style.textContent = css;\n};\n\nconst paletteStyleId = (name: string) => `lobe-palette-${name}`;\n\nconst ensurePaletteStyle = (name: string, doc: Document) => {\n const id = paletteStyleId(name);\n let style = doc.getElementById(id);\n if (!style) {\n style = doc.createElement('style');\n style.id = id;\n doc.head.append(style);\n }\n const css = getCustomPaletteCss(name);\n if (style.textContent !== css) style.textContent = css;\n};\n\nconst paletteNeutralStyleId = (name: string, neutral: string) =>\n `lobe-palette-${name}--neutral-${neutralKey(neutral)}`;\n\nconst ensurePaletteNeutralStyle = (name: string, neutral: string, doc: Document) => {\n const id = paletteNeutralStyleId(name, neutral);\n let style = doc.getElementById(id);\n if (!style) {\n style = doc.createElement('style');\n style.id = id;\n doc.head.append(style);\n }\n const css = getPaletteNeutralCss(name, neutral);\n if (style.textContent !== css) style.textContent = css;\n};\n\nexport const setLobeTheme = (\n state: Partial<LobeThemeState>,\n root: HTMLElement = document.documentElement,\n) => {\n const next = { ...readThemeState(root), ...state };\n next.primaryColor = normalizePrimary(next.primaryColor);\n if (isCustomPrimaryColor(next.primaryColor))\n ensureCustomPrimaryStyle(next.primaryColor, root.ownerDocument);\n next.neutralColor = normalizeNeutral(next.neutralColor);\n if (isCustomNeutral(next.neutralColor)) ensureNeutralStyle(next.neutralColor, root.ownerDocument);\n if (isRegisteredPalette(next.palette)) {\n ensurePaletteStyle(next.palette, root.ownerDocument);\n if (next.neutralColor)\n ensurePaletteNeutralStyle(next.palette, next.neutralColor, root.ownerDocument);\n }\n for (const [name, value] of Object.entries(toAttributes(next))) {\n if (value) root.setAttribute(name, value);\n else root.removeAttribute(name);\n }\n};\n\nconst readThemeState = (el: HTMLElement): LobeThemeState => ({\n appearance: el.dataset.theme === 'dark' ? 'dark' : 'light',\n neutralColor: el.dataset.neutralColor || undefined,\n palette: el.dataset.palette || undefined,\n primaryColor: (el.dataset.primaryColor as LobePrimaryColor) || undefined,\n});\n\nconst stateKey = (state: LobeThemeState) =>\n `${state.appearance}|${state.primaryColor ?? ''}|${state.neutralColor ?? ''}|${state.palette ?? ''}`;\n\nconst usePaletteRevision = () =>\n useSyncExternalStore(subscribePalettes, getPaletteRevision, getPaletteRevision);\n\nconst SERVER_STATE: LobeThemeState = { appearance: 'light' };\nlet rootSnapshot: LobeThemeState = SERVER_STATE;\n\nconst getRootSnapshot = () => {\n const next = readThemeState(document.documentElement);\n if (stateKey(next) !== stateKey(rootSnapshot)) rootSnapshot = next;\n return rootSnapshot;\n};\n\nconst subscribeRoot = (onChange: () => void) => {\n const observer = new MutationObserver(onChange);\n observer.observe(document.documentElement, { attributeFilter: ATTRIBUTES, attributes: true });\n return () => observer.disconnect();\n};\n\nconst ThemeScopeContext = createContext<LobeThemeState | null>(null);\n\nconst useThemeState = () => {\n const scoped = use(ThemeScopeContext);\n const root = useSyncExternalStore(subscribeRoot, getRootSnapshot, () => SERVER_STATE);\n return scoped ?? root;\n};\n\nexport interface ThemeScopeProps extends Partial<LobeThemeState> {\n children?: ReactNode;\n className?: string;\n style?: CSSProperties;\n}\n\nexport const ThemeScope = ({\n appearance,\n children,\n className,\n neutralColor,\n palette,\n primaryColor,\n style,\n}: ThemeScopeProps) => {\n const parent = useThemeState();\n const state = useMemo<LobeThemeState>(\n () => ({\n appearance: appearance ?? parent.appearance,\n neutralColor: normalizeNeutral(neutralColor) ?? parent.neutralColor,\n palette: palette ?? parent.palette,\n primaryColor: normalizePrimary(primaryColor) ?? parent.primaryColor,\n }),\n [appearance, neutralColor, palette, primaryColor, parent],\n );\n const customPrimary = isCustomPrimaryColor(state.primaryColor) ? state.primaryColor : undefined;\n const paletteRevision = usePaletteRevision();\n const customPalette = isRegisteredPalette(state.palette) ? state.palette : undefined;\n const customNeutral = isCustomNeutral(state.neutralColor) ? state.neutralColor : undefined;\n\n return (\n <ThemeScopeContext value={state}>\n {customPrimary && (\n <style href={customPrimaryStyleId(customPrimary)} precedence=\"lobe-theme\">\n {getCustomPrimaryCss(customPrimary)}\n </style>\n )}\n {customNeutral && (\n <style href={`${neutralStyleId(customNeutral)}-${paletteRevision}`} precedence=\"lobe-theme\">\n {getCustomNeutralCss(customNeutral)}\n </style>\n )}\n {customPalette && (\n <style href={`${paletteStyleId(customPalette)}-${paletteRevision}`} precedence=\"lobe-theme\">\n {getCustomPaletteCss(customPalette)}\n </style>\n )}\n {customPalette && state.neutralColor && (\n <style\n href={`${paletteNeutralStyleId(customPalette, state.neutralColor)}-${paletteRevision}`}\n precedence=\"lobe-theme\"\n >\n {getPaletteNeutralCss(customPalette, state.neutralColor)}\n </style>\n )}\n <div className={className} style={style} {...toAttributes(state)}>\n {children}\n </div>\n </ThemeScopeContext>\n );\n};\n\nexport const useThemeMode = () => {\n const { appearance } = useThemeState();\n return useMemo(() => ({ appearance, isDarkMode: appearance === 'dark' }), [appearance]);\n};\n\nexport type LobeTheme = LobeToken & { appearance: LobeAppearance; isDarkMode: boolean };\n\nconst themeCache = new Map<string, LobeTheme>();\n\nexport const useTheme = (): LobeTheme => {\n const state = useThemeState();\n const key = `${stateKey(state)}@${usePaletteRevision()}`;\n let theme = themeCache.get(key);\n if (!theme) {\n theme = {\n ...createLobeToken(state),\n appearance: state.appearance,\n isDarkMode: state.appearance === 'dark',\n };\n themeCache.set(key, theme);\n }\n return theme;\n};\n\nconst breakpointQueries = {\n lg: `(min-width: ${staticToken.screenLG}px)`,\n md: `(min-width: ${staticToken.screenMD}px)`,\n sm: `(min-width: ${staticToken.screenSM}px)`,\n xl: `(min-width: ${staticToken.screenXL}px)`,\n xs: `(max-width: ${staticToken.screenXSMax}px)`,\n xxl: `(min-width: ${staticToken.screenXXL}px)`,\n xxxl: `(min-width: ${staticToken.screenXXXL}px)`,\n};\n\ntype Breakpoint = keyof typeof breakpointQueries;\nexport type ResponsiveState = Record<\n Breakpoint | 'mobile' | 'tablet' | 'laptop' | 'desktop',\n boolean\n>;\n\nconst toResponsive = (matches: Record<Breakpoint, boolean>): ResponsiveState => ({\n ...matches,\n desktop: matches.xxl,\n laptop: matches.lg,\n mobile: matches.xs,\n tablet: matches.md,\n});\n\nconst SERVER_RESPONSIVE = toResponsive({\n lg: false,\n md: false,\n sm: false,\n xl: false,\n xs: false,\n xxl: false,\n xxxl: false,\n});\nlet responsiveSnapshot = SERVER_RESPONSIVE;\n\nconst getResponsiveSnapshot = () => {\n const matches = Object.fromEntries(\n Object.entries(breakpointQueries).map(([key, query]) => [key, matchMedia(query).matches]),\n ) as Record<Breakpoint, boolean>;\n const changed = (Object.keys(matches) as Breakpoint[]).some(\n (key) => matches[key] !== responsiveSnapshot[key],\n );\n if (changed) responsiveSnapshot = toResponsive(matches);\n return responsiveSnapshot;\n};\n\nconst subscribeResponsive = (onChange: () => void) => {\n const lists = Object.values(breakpointQueries).map((query) => matchMedia(query));\n for (const list of lists) list.addEventListener('change', onChange);\n return () => {\n for (const list of lists) list.removeEventListener('change', onChange);\n };\n};\n\nexport const useResponsive = () =>\n useSyncExternalStore(subscribeResponsive, getResponsiveSnapshot, () => SERVER_RESPONSIVE);\n\nexport interface LobeThemeScriptProps {\n appearance?: LobeAppearance | 'system';\n neutralColor?: LobeNeutralColor;\n nonce?: string;\n palette?: string;\n primaryColor?: LobePrimaryColor;\n}\n\nexport const LobeThemeScript = ({\n appearance,\n neutralColor,\n nonce,\n palette,\n primaryColor,\n}: LobeThemeScriptProps) => {\n const primary = normalizePrimary(primaryColor);\n const styles: [string, string][] = [];\n if (isCustomPrimaryColor(primary))\n styles.push([customPrimaryStyleId(primary), getCustomPrimaryCss(primary)]);\n const neutral = normalizeNeutral(neutralColor);\n if (isCustomNeutral(neutral))\n styles.push([neutralStyleId(neutral), getCustomNeutralCss(neutral)]);\n if (isRegisteredPalette(palette)) {\n styles.push([paletteStyleId(palette), getCustomPaletteCss(palette)]);\n if (neutral)\n styles.push([\n paletteNeutralStyleId(palette, neutral),\n getPaletteNeutralCss(palette, neutral),\n ]);\n }\n const args = JSON.stringify([\n appearance ?? null,\n primary ?? null,\n neutral ?? null,\n palette ?? null,\n styles,\n ]);\n const script = `(function(a,p,n,q,t){var d=document.documentElement;if(a==='system')a=matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light';if(a)d.setAttribute('data-theme',a);else if(!d.hasAttribute('data-theme'))d.setAttribute('data-theme','light');if(p)d.setAttribute('data-primary-color',p);if(n)d.setAttribute('data-neutral-color',n);if(q)d.setAttribute('data-palette',q);t.forEach(function(x){if(document.getElementById(x[0]))return;var s=document.createElement('style');s.id=x[0];s.textContent=x[1];document.head.appendChild(s);});}).apply(null,${args})`;\n\n return (\n <script suppressHydrationWarning dangerouslySetInnerHTML={{ __html: script }} nonce={nonce} />\n );\n};\n"],"mappings":"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"}
@@ -1,6 +1,8 @@
1
1
  import { CustomPrimaryColor } from "./customPrimary.mjs";
2
2
  //#region src/styles/theme/themeCss.d.ts
3
3
  export declare const getCustomPrimaryCss: (color: CustomPrimaryColor) => string;
4
+ /** CSS for a custom neutral (6-digit hex or a `registerLobeNeutral` name); '' for presets. */
5
+ export declare const getCustomNeutralCss: (value: string) => string;
4
6
  export declare const getCustomPaletteCss: (name: string) => string;
5
7
  export declare const getThemeCss: () => string;
6
8
  export declare const getGlobalCss: () => string;
@@ -1,4 +1,5 @@
1
1
  import { isRegisteredPalette } from "./customPalette.mjs";
2
+ import { isCustomNeutral } from "./customNeutral.mjs";
2
3
  import { neutralColors, primaryColors } from "../customTheme.mjs";
3
4
  import { createLobeTokenGroups } from "./createLobeToken.mjs";
4
5
  import { toCssVarName, toCssVariables } from "../css.mjs";
@@ -57,6 +58,15 @@ const getCustomPrimaryCss = (color) => {
57
58
  primaryColor: hex
58
59
  }).primary))).join("\n\n");
59
60
  };
61
+ /** CSS for a custom neutral (6-digit hex or a `registerLobeNeutral` name); '' for presets. */
62
+ const getCustomNeutralCss = (value) => {
63
+ const key = value.startsWith("#") ? value.toLowerCase() : value;
64
+ if (!isCustomNeutral(key)) return "";
65
+ return ["light", "dark"].map((appearance) => rule(`[data-neutral-color='${key}']${appearanceSelector(appearance)}`, toCssVariables(createLobeTokenGroups({
66
+ appearance,
67
+ neutralColor: key
68
+ }).neutral))).join("\n\n");
69
+ };
60
70
  const getCustomPaletteCss = (name) => {
61
71
  if (!isRegisteredPalette(name)) return "";
62
72
  return ["light", "dark"].map((appearance) => {
@@ -71,6 +81,26 @@ const getCustomPaletteCss = (name) => {
71
81
  }));
72
82
  }).join("\n\n");
73
83
  };
84
+ /**
85
+ * The outline tokens depend on both the neutral and the status palette, so the standalone
86
+ * neutral / palette stylesheets each compute them against the other's default. This rule
87
+ * (higher specificity) recomputes them for the actual combination.
88
+ */
89
+ const getPaletteNeutralCss = (name, neutralColor) => {
90
+ if (!isRegisteredPalette(name) || !neutralColor) return "";
91
+ const key = neutralColor.startsWith("#") ? neutralColor.toLowerCase() : neutralColor;
92
+ return ["light", "dark"].map((appearance) => {
93
+ const { neutral } = createLobeTokenGroups({
94
+ appearance,
95
+ neutralColor: key,
96
+ palette: name
97
+ });
98
+ return rule(`[data-palette='${name}'][data-neutral-color='${key}']${appearanceSelector(appearance)}`, toCssVariables({
99
+ colorErrorOutline: neutral.colorErrorOutline,
100
+ colorWarningOutline: neutral.colorWarningOutline
101
+ }));
102
+ }).join("\n\n");
103
+ };
74
104
  const essentialCss = `@layer lobe-ui {
75
105
  .lobe-brand-loading path {
76
106
  fill: currentcolor;
@@ -350,6 +380,6 @@ const getThemeCss = () => cached ??= [
350
380
  ].join("\n\n");
351
381
  const getGlobalCss = () => `${layerOrder}\n\n${globalCss}`;
352
382
  //#endregion
353
- export { getCustomPaletteCss, getCustomPrimaryCss, getGlobalCss, getThemeCss };
383
+ export { getCustomNeutralCss, getCustomPaletteCss, getCustomPrimaryCss, getGlobalCss, getPaletteNeutralCss, getThemeCss };
354
384
 
355
385
  //# sourceMappingURL=themeCss.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"themeCss.mjs","names":[],"sources":["../../../src/styles/theme/themeCss.ts"],"sourcesContent":["import { toCssVariables, toCssVarName } from '../css';\nimport { neutralColors, primaryColors } from '../customTheme';\nimport { createLobeTokenGroups } from './createLobeToken';\nimport { isRegisteredPalette } from './customPalette';\nimport type { CustomPrimaryColor } from './customPrimary';\nimport type { LobeAppearance } from './scope';\n\nconst v = (key: string) => `var(${toCssVarName(key)})`;\n\nconst ring: Record<LobeAppearance, string> = {\n dark: '0 0 0 1px rgb(255 255 255 / 18%)',\n light: '0 0 0 1px rgb(0 0 0 / 5%)',\n};\n\n/* `initial` makes a light scope nested in a dark one fall back to each variant's own fill\n instead of inheriting the dark table surface. */\nconst surface: Record<LobeAppearance, Record<string, string>> = {\n dark: {\n '--lobe-fill-border': v('colorFillSecondary'),\n '--lobe-fill-border-strong': v('colorFill'),\n '--lobe-table-cell-bg': 'transparent',\n '--lobe-table-surface': v('colorFillQuaternary'),\n },\n light: {\n '--lobe-fill-border': v('colorFill'),\n '--lobe-fill-border-strong': `color-mix(in srgb, ${v('colorFill')}, ${v('colorText')} 15%)`,\n '--lobe-table-cell-bg': 'initial',\n '--lobe-table-surface': 'initial',\n },\n};\n\nconst rule = (selector: string, declarations: Record<string, number | string>) =>\n `${selector} {\\n${Object.entries(declarations)\n .map(([name, value]) => ` ${name}: ${value};`)\n .join('\\n')}\\n}`;\n\nconst appearanceRule = (selector: string, appearance: LobeAppearance) => {\n const { neutral, palette, primary, static: base } = createLobeTokenGroups({ appearance });\n const token =\n appearance === 'light'\n ? { ...base, ...palette, ...primary, ...neutral }\n : { ...palette, ...primary, ...neutral };\n\n return rule(selector, {\n ...toCssVariables(token),\n '--lobe-ring': ring[appearance],\n ...surface[appearance],\n 'color-scheme': appearance,\n });\n};\n\nconst appearanceSelector = (appearance: LobeAppearance) =>\n appearance === 'dark' ? \"[data-theme='dark']\" : \":not([data-theme='dark'])\";\n\nconst colorRules = (appearance: LobeAppearance) => [\n ...(Object.keys(primaryColors) as (keyof typeof primaryColors)[]).map((primaryColor) =>\n rule(\n `[data-primary-color='${primaryColor}']${appearanceSelector(appearance)}`,\n toCssVariables(createLobeTokenGroups({ appearance, primaryColor }).primary),\n ),\n ),\n ...(Object.keys(neutralColors) as (keyof typeof neutralColors)[]).map((neutralColor) =>\n rule(\n `[data-neutral-color='${neutralColor}']${appearanceSelector(appearance)}`,\n toCssVariables(createLobeTokenGroups({ appearance, neutralColor }).neutral),\n ),\n ),\n];\n\nexport const getCustomPrimaryCss = (color: CustomPrimaryColor) => {\n const hex = color.toLowerCase();\n return (['light', 'dark'] as const)\n .map((appearance) =>\n rule(\n `[data-primary-color='${hex}']${appearanceSelector(appearance)}`,\n toCssVariables(\n createLobeTokenGroups({ appearance, primaryColor: hex as CustomPrimaryColor }).primary,\n ),\n ),\n )\n .join('\\n\\n');\n};\n\nexport const getCustomPaletteCss = (name: string) => {\n if (!isRegisteredPalette(name)) return '';\n return (['light', 'dark'] as const)\n .map((appearance) => {\n const { neutral, palette } = createLobeTokenGroups({ appearance, palette: name });\n return rule(\n `[data-palette='${name}']${appearanceSelector(appearance)}`,\n toCssVariables({\n ...palette,\n colorErrorOutline: neutral.colorErrorOutline,\n colorWarningOutline: neutral.colorWarningOutline,\n }),\n );\n })\n .join('\\n\\n');\n};\n\nconst essentialCss = `@layer lobe-ui {\n .lobe-brand-loading path {\n fill: currentcolor;\n fill-opacity: 0;\n stroke: currentcolor;\n stroke-dasharray: 1000;\n stroke-dashoffset: 1000;\n stroke-width: 0.25em;\n\n animation:\n draw 2s cubic-bezier(0.4, 0, 0.2, 1) infinite,\n fill 2s cubic-bezier(0.4, 0, 0.2, 1) infinite;\n }\n}\n\n@layer lobe-popup {\n .lobe-context-trigger[data-popup-open],\n .lobe-dropdown-menu-trigger[data-popup-open] {\n background: ${v('colorFillTertiary')};\n }\n}\n\n@layer lobe-base {\n :where(body, body [data-theme]) {\n color: ${v('colorText')};\n font-family: ${v('fontFamily')};\n font-size: ${v('fontSize')};\n line-height: ${v('lineHeight')};\n }\n\n :where(body) :where(a) {\n cursor: pointer;\n\n color: ${v('colorLink')};\n text-decoration: none;\n\n background-color: transparent;\n outline: none;\n\n transition: color ${v('motionDurationSlow')};\n }\n\n :where(body) :where(a):hover {\n color: ${v('colorLinkHover')};\n }\n\n :where(body) :where(a):active {\n color: ${v('colorLinkActive')};\n }\n\n :where(body) :where(a):focus-visible {\n outline: ${v('lineWidthFocus')} solid ${v('colorPrimaryBorder')};\n outline-offset: 1px;\n }\n\n :where(body) :where(a)[disabled] {\n cursor: not-allowed;\n color: ${v('colorTextDisabled')};\n }\n\n :where(.lobe-flex) {\n --lobe-flex: 0 1 auto;\n --lobe-flex-direction: column;\n --lobe-flex-wrap: nowrap;\n --lobe-flex-justify: flex-start;\n --lobe-flex-align: stretch;\n --lobe-flex-width: auto;\n --lobe-flex-height: auto;\n --lobe-flex-padding: 0;\n --lobe-flex-padding-inline: var(--lobe-flex-padding);\n --lobe-flex-padding-block: var(--lobe-flex-padding);\n --lobe-flex-gap: 0;\n\n display: flex;\n flex: var(--lobe-flex);\n flex-flow: var(--lobe-flex-direction) var(--lobe-flex-wrap);\n gap: var(--lobe-flex-gap);\n align-items: var(--lobe-flex-align);\n justify-content: var(--lobe-flex-justify);\n\n width: var(--lobe-flex-width);\n height: var(--lobe-flex-height);\n padding: var(--lobe-flex-padding);\n padding-block: var(--lobe-flex-padding-block);\n padding-inline: var(--lobe-flex-padding-inline);\n }\n\n .lobe-flex-hidden {\n display: none;\n }\n}\n\n@keyframes draw {\n 0% {\n stroke-dashoffset: 1000;\n }\n\n 100% {\n stroke-dashoffset: 0;\n }\n}\n\n@keyframes fill {\n 30% {\n fill-opacity: 0.05;\n }\n\n 100% {\n fill-opacity: 1;\n }\n}`;\n\nconst globalCss = `@layer lobe-base {\n :root {\n --font-settings: 'cv01', 'tnum', 'kern';\n --font-variations: 'opsz' auto, tabular-nums;\n\n font-synthesis: style;\n text-autospace: normal;\n }\n\n html {\n overscroll-behavior: none;\n }\n\n body {\n overflow: hidden auto;\n\n min-height: 100vh;\n margin: 0;\n padding: 0;\n\n font-feature-settings: var(--font-settings);\n font-variation-settings: var(--font-variations);\n font-optical-sizing: auto;\n font-kerning: normal;\n font-variant-ligatures: common-ligatures contextual;\n font-variant-numeric: tabular-nums;\n font-size-adjust: from-font;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n text-wrap: pretty;\n text-size-adjust: 100%;\n text-rendering: optimizelegibility;\n overflow-wrap: anywhere;\n vertical-align: baseline;\n\n background-color: ${v('colorBgLayout')};\n\n font-synthesis: style;\n\n -webkit-overflow-scrolling: touch;\n -webkit-tap-highlight-color: transparent;\n }\n\n html,\n body {\n width: 100%;\n height: 100%;\n }\n\n [tabindex='-1']:focus {\n outline: none;\n }\n\n [hidden] {\n display: none !important;\n }\n\n h1,\n h2,\n h3,\n h4,\n h5,\n h6 {\n margin-block: 0 0.5em;\n font-weight: 500;\n }\n\n p,\n ol,\n ul,\n dl,\n pre {\n margin-block: 0 1em;\n }\n\n ol ol,\n ul ul,\n ol ul,\n ul ol {\n margin-block-end: 0;\n }\n\n dd {\n margin-block-end: 0.5em;\n margin-inline-start: 0;\n }\n\n blockquote,\n figure {\n margin-block: 0 1em;\n margin-inline: 0;\n }\n\n img {\n border-style: none;\n vertical-align: middle;\n }\n\n table {\n border-collapse: collapse;\n }\n\n input,\n button,\n select,\n optgroup,\n textarea {\n margin: 0;\n\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n color: inherit;\n }\n\n textarea {\n resize: vertical;\n overflow: auto;\n }\n\n fieldset {\n min-width: 0;\n margin: 0;\n padding: 0;\n border: 0;\n }\n\n code,\n kbd,\n samp,\n pre {\n font-family: ${v('fontFamilyCode')} !important;\n font-feature-settings:\n 'liga' 0,\n 'calt' 0;\n font-variant-ligatures: none;\n\n span {\n font-family: ${v('fontFamilyCode')} !important;\n }\n }\n\n ::selection {\n color: #000;\n background: ${v('yellow9')};\n\n -webkit-text-fill-color: unset !important;\n }\n\n * {\n scrollbar-color: ${v('colorFill')} transparent;\n scrollbar-width: thin;\n box-sizing: border-box;\n vertical-align: baseline;\n }\n}`;\n\nconst layerOrder = '@layer lobe-base, lobe-popup, lobe-ui;';\n\nlet cached: string | undefined;\n\nexport const getThemeCss = () =>\n (cached ??= [\n layerOrder,\n appearanceRule(\":root, [data-theme='light']\", 'light'),\n appearanceRule(\"[data-theme='dark']\", 'dark'),\n ...colorRules('light'),\n ...colorRules('dark'),\n essentialCss,\n ].join('\\n\\n'));\n\nexport const getGlobalCss = () => `${layerOrder}\\n\\n${globalCss}`;\n"],"mappings":";;;;;AAOA,MAAM,KAAK,QAAgB,OAAO,aAAa,GAAG,EAAE;AAEpD,MAAM,OAAuC;CAC3C,MAAM;CACN,OAAO;AACT;AAIA,MAAM,UAA0D;CAC9D,MAAM;EACJ,sBAAsB,EAAE,oBAAoB;EAC5C,6BAA6B,EAAE,WAAW;EAC1C,wBAAwB;EACxB,wBAAwB,EAAE,qBAAqB;CACjD;CACA,OAAO;EACL,sBAAsB,EAAE,WAAW;EACnC,6BAA6B,sBAAsB,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE;EACrF,wBAAwB;EACxB,wBAAwB;CAC1B;AACF;AAEA,MAAM,QAAQ,UAAkB,iBAC9B,GAAG,SAAS,MAAM,OAAO,QAAQ,YAAY,CAAC,CAC3C,KAAK,CAAC,MAAM,WAAW,KAAK,KAAK,IAAI,MAAM,EAAE,CAAC,CAC9C,KAAK,IAAI,EAAE;AAEhB,MAAM,kBAAkB,UAAkB,eAA+B;CACvE,MAAM,EAAE,SAAS,SAAS,SAAS,QAAQ,SAAS,sBAAsB,EAAE,WAAW,CAAC;CACxF,MAAM,QACJ,eAAe,UACX;EAAE,GAAG;EAAM,GAAG;EAAS,GAAG;EAAS,GAAG;CAAQ,IAC9C;EAAE,GAAG;EAAS,GAAG;EAAS,GAAG;CAAQ;CAE3C,OAAO,KAAK,UAAU;EACpB,GAAG,eAAe,KAAK;EACvB,eAAe,KAAK;EACpB,GAAG,QAAQ;EACX,gBAAgB;CAClB,CAAC;AACH;AAEA,MAAM,sBAAsB,eAC1B,eAAe,SAAS,wBAAwB;AAElD,MAAM,cAAc,eAA+B,CACjD,GAAI,OAAO,KAAK,aAAa,CAAC,CAAoC,KAAK,iBACrE,KACE,wBAAwB,aAAa,IAAI,mBAAmB,UAAU,KACtE,eAAe,sBAAsB;CAAE;CAAY;AAAa,CAAC,CAAC,CAAC,OAAO,CAC5E,CACF,GACA,GAAI,OAAO,KAAK,aAAa,CAAC,CAAoC,KAAK,iBACrE,KACE,wBAAwB,aAAa,IAAI,mBAAmB,UAAU,KACtE,eAAe,sBAAsB;CAAE;CAAY;AAAa,CAAC,CAAC,CAAC,OAAO,CAC5E,CACF,CACF;AAEA,MAAa,uBAAuB,UAA8B;CAChE,MAAM,MAAM,MAAM,YAAY;CAC9B,OAAQ,CAAC,SAAS,MAAM,CAAC,CACtB,KAAK,eACJ,KACE,wBAAwB,IAAI,IAAI,mBAAmB,UAAU,KAC7D,eACE,sBAAsB;EAAE;EAAY,cAAc;CAA0B,CAAC,CAAC,CAAC,OACjF,CACF,CACF,CAAC,CACA,KAAK,MAAM;AAChB;AAEA,MAAa,uBAAuB,SAAiB;CACnD,IAAI,CAAC,oBAAoB,IAAI,GAAG,OAAO;CACvC,OAAQ,CAAC,SAAS,MAAM,CAAC,CACtB,KAAK,eAAe;EACnB,MAAM,EAAE,SAAS,YAAY,sBAAsB;GAAE;GAAY,SAAS;EAAK,CAAC;EAChF,OAAO,KACL,kBAAkB,KAAK,IAAI,mBAAmB,UAAU,KACxD,eAAe;GACb,GAAG;GACH,mBAAmB,QAAQ;GAC3B,qBAAqB,QAAQ;EAC/B,CAAC,CACH;CACF,CAAC,CAAC,CACD,KAAK,MAAM;AAChB;AAEA,MAAM,eAAe;;;;;;;;;;;;;;;;;;kBAkBH,EAAE,mBAAmB,EAAE;;;;;;aAM5B,EAAE,WAAW,EAAE;mBACT,EAAE,YAAY,EAAE;iBAClB,EAAE,UAAU,EAAE;mBACZ,EAAE,YAAY,EAAE;;;;;;aAMtB,EAAE,WAAW,EAAE;;;;;;wBAMJ,EAAE,oBAAoB,EAAE;;;;aAInC,EAAE,gBAAgB,EAAE;;;;aAIpB,EAAE,iBAAiB,EAAE;;;;eAInB,EAAE,gBAAgB,EAAE,SAAS,EAAE,oBAAoB,EAAE;;;;;;aAMvD,EAAE,mBAAmB,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuDpC,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBAmCM,EAAE,eAAe,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBAgGxB,EAAE,gBAAgB,EAAE;;;;;;;qBAOlB,EAAE,gBAAgB,EAAE;;;;;;kBAMvB,EAAE,SAAS,EAAE;;;;;;uBAMR,EAAE,WAAW,EAAE;;;;;;AAOtC,MAAM,aAAa;AAEnB,IAAI;AAEJ,MAAa,oBACV,WAAW;CACV;CACA,eAAe,+BAA+B,OAAO;CACrD,eAAe,uBAAuB,MAAM;CAC5C,GAAG,WAAW,OAAO;CACrB,GAAG,WAAW,MAAM;CACpB;AACF,CAAC,CAAC,KAAK,MAAM;AAEf,MAAa,qBAAqB,GAAG,WAAW,MAAM"}
1
+ {"version":3,"file":"themeCss.mjs","names":[],"sources":["../../../src/styles/theme/themeCss.ts"],"sourcesContent":["import { toCssVariables, toCssVarName } from '../css';\nimport { neutralColors, primaryColors } from '../customTheme';\nimport { createLobeTokenGroups } from './createLobeToken';\nimport { isCustomNeutral } from './customNeutral';\nimport { isRegisteredPalette } from './customPalette';\nimport type { CustomPrimaryColor } from './customPrimary';\nimport type { LobeAppearance } from './scope';\n\nconst v = (key: string) => `var(${toCssVarName(key)})`;\n\nconst ring: Record<LobeAppearance, string> = {\n dark: '0 0 0 1px rgb(255 255 255 / 18%)',\n light: '0 0 0 1px rgb(0 0 0 / 5%)',\n};\n\n/* `initial` makes a light scope nested in a dark one fall back to each variant's own fill\n instead of inheriting the dark table surface. */\nconst surface: Record<LobeAppearance, Record<string, string>> = {\n dark: {\n '--lobe-fill-border': v('colorFillSecondary'),\n '--lobe-fill-border-strong': v('colorFill'),\n '--lobe-table-cell-bg': 'transparent',\n '--lobe-table-surface': v('colorFillQuaternary'),\n },\n light: {\n '--lobe-fill-border': v('colorFill'),\n '--lobe-fill-border-strong': `color-mix(in srgb, ${v('colorFill')}, ${v('colorText')} 15%)`,\n '--lobe-table-cell-bg': 'initial',\n '--lobe-table-surface': 'initial',\n },\n};\n\nconst rule = (selector: string, declarations: Record<string, number | string>) =>\n `${selector} {\\n${Object.entries(declarations)\n .map(([name, value]) => ` ${name}: ${value};`)\n .join('\\n')}\\n}`;\n\nconst appearanceRule = (selector: string, appearance: LobeAppearance) => {\n const { neutral, palette, primary, static: base } = createLobeTokenGroups({ appearance });\n const token =\n appearance === 'light'\n ? { ...base, ...palette, ...primary, ...neutral }\n : { ...palette, ...primary, ...neutral };\n\n return rule(selector, {\n ...toCssVariables(token),\n '--lobe-ring': ring[appearance],\n ...surface[appearance],\n 'color-scheme': appearance,\n });\n};\n\nconst appearanceSelector = (appearance: LobeAppearance) =>\n appearance === 'dark' ? \"[data-theme='dark']\" : \":not([data-theme='dark'])\";\n\nconst colorRules = (appearance: LobeAppearance) => [\n ...(Object.keys(primaryColors) as (keyof typeof primaryColors)[]).map((primaryColor) =>\n rule(\n `[data-primary-color='${primaryColor}']${appearanceSelector(appearance)}`,\n toCssVariables(createLobeTokenGroups({ appearance, primaryColor }).primary),\n ),\n ),\n ...(Object.keys(neutralColors) as (keyof typeof neutralColors)[]).map((neutralColor) =>\n rule(\n `[data-neutral-color='${neutralColor}']${appearanceSelector(appearance)}`,\n toCssVariables(createLobeTokenGroups({ appearance, neutralColor }).neutral),\n ),\n ),\n];\n\nexport const getCustomPrimaryCss = (color: CustomPrimaryColor) => {\n const hex = color.toLowerCase();\n return (['light', 'dark'] as const)\n .map((appearance) =>\n rule(\n `[data-primary-color='${hex}']${appearanceSelector(appearance)}`,\n toCssVariables(\n createLobeTokenGroups({ appearance, primaryColor: hex as CustomPrimaryColor }).primary,\n ),\n ),\n )\n .join('\\n\\n');\n};\n\n/** CSS for a custom neutral (6-digit hex or a `registerLobeNeutral` name); '' for presets. */\nexport const getCustomNeutralCss = (value: string) => {\n const key = value.startsWith('#') ? value.toLowerCase() : value;\n if (!isCustomNeutral(key)) return '';\n return (['light', 'dark'] as const)\n .map((appearance) =>\n rule(\n `[data-neutral-color='${key}']${appearanceSelector(appearance)}`,\n toCssVariables(createLobeTokenGroups({ appearance, neutralColor: key }).neutral),\n ),\n )\n .join('\\n\\n');\n};\n\nexport const getCustomPaletteCss = (name: string) => {\n if (!isRegisteredPalette(name)) return '';\n return (['light', 'dark'] as const)\n .map((appearance) => {\n const { neutral, palette } = createLobeTokenGroups({ appearance, palette: name });\n return rule(\n `[data-palette='${name}']${appearanceSelector(appearance)}`,\n toCssVariables({\n ...palette,\n colorErrorOutline: neutral.colorErrorOutline,\n colorWarningOutline: neutral.colorWarningOutline,\n }),\n );\n })\n .join('\\n\\n');\n};\n\n/**\n * The outline tokens depend on both the neutral and the status palette, so the standalone\n * neutral / palette stylesheets each compute them against the other's default. This rule\n * (higher specificity) recomputes them for the actual combination.\n */\nexport const getPaletteNeutralCss = (name: string, neutralColor: string) => {\n if (!isRegisteredPalette(name) || !neutralColor) return '';\n const key = neutralColor.startsWith('#') ? neutralColor.toLowerCase() : neutralColor;\n return (['light', 'dark'] as const)\n .map((appearance) => {\n const { neutral } = createLobeTokenGroups({ appearance, neutralColor: key, palette: name });\n return rule(\n `[data-palette='${name}'][data-neutral-color='${key}']${appearanceSelector(appearance)}`,\n toCssVariables({\n colorErrorOutline: neutral.colorErrorOutline,\n colorWarningOutline: neutral.colorWarningOutline,\n }),\n );\n })\n .join('\\n\\n');\n};\n\nconst essentialCss = `@layer lobe-ui {\n .lobe-brand-loading path {\n fill: currentcolor;\n fill-opacity: 0;\n stroke: currentcolor;\n stroke-dasharray: 1000;\n stroke-dashoffset: 1000;\n stroke-width: 0.25em;\n\n animation:\n draw 2s cubic-bezier(0.4, 0, 0.2, 1) infinite,\n fill 2s cubic-bezier(0.4, 0, 0.2, 1) infinite;\n }\n}\n\n@layer lobe-popup {\n .lobe-context-trigger[data-popup-open],\n .lobe-dropdown-menu-trigger[data-popup-open] {\n background: ${v('colorFillTertiary')};\n }\n}\n\n@layer lobe-base {\n :where(body, body [data-theme]) {\n color: ${v('colorText')};\n font-family: ${v('fontFamily')};\n font-size: ${v('fontSize')};\n line-height: ${v('lineHeight')};\n }\n\n :where(body) :where(a) {\n cursor: pointer;\n\n color: ${v('colorLink')};\n text-decoration: none;\n\n background-color: transparent;\n outline: none;\n\n transition: color ${v('motionDurationSlow')};\n }\n\n :where(body) :where(a):hover {\n color: ${v('colorLinkHover')};\n }\n\n :where(body) :where(a):active {\n color: ${v('colorLinkActive')};\n }\n\n :where(body) :where(a):focus-visible {\n outline: ${v('lineWidthFocus')} solid ${v('colorPrimaryBorder')};\n outline-offset: 1px;\n }\n\n :where(body) :where(a)[disabled] {\n cursor: not-allowed;\n color: ${v('colorTextDisabled')};\n }\n\n :where(.lobe-flex) {\n --lobe-flex: 0 1 auto;\n --lobe-flex-direction: column;\n --lobe-flex-wrap: nowrap;\n --lobe-flex-justify: flex-start;\n --lobe-flex-align: stretch;\n --lobe-flex-width: auto;\n --lobe-flex-height: auto;\n --lobe-flex-padding: 0;\n --lobe-flex-padding-inline: var(--lobe-flex-padding);\n --lobe-flex-padding-block: var(--lobe-flex-padding);\n --lobe-flex-gap: 0;\n\n display: flex;\n flex: var(--lobe-flex);\n flex-flow: var(--lobe-flex-direction) var(--lobe-flex-wrap);\n gap: var(--lobe-flex-gap);\n align-items: var(--lobe-flex-align);\n justify-content: var(--lobe-flex-justify);\n\n width: var(--lobe-flex-width);\n height: var(--lobe-flex-height);\n padding: var(--lobe-flex-padding);\n padding-block: var(--lobe-flex-padding-block);\n padding-inline: var(--lobe-flex-padding-inline);\n }\n\n .lobe-flex-hidden {\n display: none;\n }\n}\n\n@keyframes draw {\n 0% {\n stroke-dashoffset: 1000;\n }\n\n 100% {\n stroke-dashoffset: 0;\n }\n}\n\n@keyframes fill {\n 30% {\n fill-opacity: 0.05;\n }\n\n 100% {\n fill-opacity: 1;\n }\n}`;\n\nconst globalCss = `@layer lobe-base {\n :root {\n --font-settings: 'cv01', 'tnum', 'kern';\n --font-variations: 'opsz' auto, tabular-nums;\n\n font-synthesis: style;\n text-autospace: normal;\n }\n\n html {\n overscroll-behavior: none;\n }\n\n body {\n overflow: hidden auto;\n\n min-height: 100vh;\n margin: 0;\n padding: 0;\n\n font-feature-settings: var(--font-settings);\n font-variation-settings: var(--font-variations);\n font-optical-sizing: auto;\n font-kerning: normal;\n font-variant-ligatures: common-ligatures contextual;\n font-variant-numeric: tabular-nums;\n font-size-adjust: from-font;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n text-wrap: pretty;\n text-size-adjust: 100%;\n text-rendering: optimizelegibility;\n overflow-wrap: anywhere;\n vertical-align: baseline;\n\n background-color: ${v('colorBgLayout')};\n\n font-synthesis: style;\n\n -webkit-overflow-scrolling: touch;\n -webkit-tap-highlight-color: transparent;\n }\n\n html,\n body {\n width: 100%;\n height: 100%;\n }\n\n [tabindex='-1']:focus {\n outline: none;\n }\n\n [hidden] {\n display: none !important;\n }\n\n h1,\n h2,\n h3,\n h4,\n h5,\n h6 {\n margin-block: 0 0.5em;\n font-weight: 500;\n }\n\n p,\n ol,\n ul,\n dl,\n pre {\n margin-block: 0 1em;\n }\n\n ol ol,\n ul ul,\n ol ul,\n ul ol {\n margin-block-end: 0;\n }\n\n dd {\n margin-block-end: 0.5em;\n margin-inline-start: 0;\n }\n\n blockquote,\n figure {\n margin-block: 0 1em;\n margin-inline: 0;\n }\n\n img {\n border-style: none;\n vertical-align: middle;\n }\n\n table {\n border-collapse: collapse;\n }\n\n input,\n button,\n select,\n optgroup,\n textarea {\n margin: 0;\n\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n color: inherit;\n }\n\n textarea {\n resize: vertical;\n overflow: auto;\n }\n\n fieldset {\n min-width: 0;\n margin: 0;\n padding: 0;\n border: 0;\n }\n\n code,\n kbd,\n samp,\n pre {\n font-family: ${v('fontFamilyCode')} !important;\n font-feature-settings:\n 'liga' 0,\n 'calt' 0;\n font-variant-ligatures: none;\n\n span {\n font-family: ${v('fontFamilyCode')} !important;\n }\n }\n\n ::selection {\n color: #000;\n background: ${v('yellow9')};\n\n -webkit-text-fill-color: unset !important;\n }\n\n * {\n scrollbar-color: ${v('colorFill')} transparent;\n scrollbar-width: thin;\n box-sizing: border-box;\n vertical-align: baseline;\n }\n}`;\n\nconst layerOrder = '@layer lobe-base, lobe-popup, lobe-ui;';\n\nlet cached: string | undefined;\n\nexport const getThemeCss = () =>\n (cached ??= [\n layerOrder,\n appearanceRule(\":root, [data-theme='light']\", 'light'),\n appearanceRule(\"[data-theme='dark']\", 'dark'),\n ...colorRules('light'),\n ...colorRules('dark'),\n essentialCss,\n ].join('\\n\\n'));\n\nexport const getGlobalCss = () => `${layerOrder}\\n\\n${globalCss}`;\n"],"mappings":"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"}
@@ -1,8 +1,6 @@
1
- import gray from "../../../color/colors/gray.mjs";
2
- import { neutralColorScales } from "../../../color/neutrals/index.mjs";
3
1
  import { mixColors, parseRgb } from "../../../utils/colorMath.mjs";
2
+ import { resolveNeutral } from "../customNeutral.mjs";
4
3
  import { generateColorNeutralPalette } from "../generateColorPalette.mjs";
5
- import { neutralText } from "./neutralText.mjs";
6
4
  //#region src/styles/theme/token/neutral.ts
7
5
  const isChannel = (value) => value >= 0 && value <= 255;
8
6
  const getAlphaColor = (front, back) => {
@@ -21,8 +19,8 @@ const getAlphaColor = (front, back) => {
21
19
  return `rgb(${f.red},${f.green},${f.blue})`;
22
20
  };
23
21
  const createNeutralToken = (appearance, neutralColor, status) => {
24
- const scale = neutralColor && neutralColorScales[neutralColor] || gray;
25
- const [colorText, colorTextSecondary, colorTextTertiary, colorTextQuaternary] = neutralText[neutralColor && neutralColorScales[neutralColor] && neutralColor || "gray"][appearance];
22
+ const { scale, text } = resolveNeutral(neutralColor);
23
+ const [colorText, colorTextSecondary, colorTextTertiary, colorTextQuaternary] = text[appearance];
26
24
  const token = {
27
25
  ...generateColorNeutralPalette({
28
26
  appearance,
@@ -1 +1 @@
1
- {"version":3,"file":"neutral.mjs","names":[],"sources":["../../../../src/styles/theme/token/neutral.ts"],"sourcesContent":["import { gray } from '@/color/colors';\nimport { neutralColorScales } from '@/color/neutrals';\nimport { mixColors, parseRgb } from '@/utils/colorMath';\n\nimport type { NeutralColors } from '../../customTheme';\nimport { generateColorNeutralPalette } from '../generateColorPalette';\nimport { neutralText } from './neutralText';\n\nconst isChannel = (value: number) => value >= 0 && value <= 255;\n\n// port of antd's getAlphaColor: the most transparent rgba that renders `front` over `back`\nconst getAlphaColor = (front: string, back: string) => {\n const f = parseRgb(front);\n if (f.alpha !== undefined) return front;\n const b = parseRgb(back);\n\n for (let alpha = 0.01; alpha <= 1; alpha += 0.01) {\n const r = Math.round((f.red - b.red * (1 - alpha)) / alpha);\n const g = Math.round((f.green - b.green * (1 - alpha)) / alpha);\n const bl = Math.round((f.blue - b.blue * (1 - alpha)) / alpha);\n if (isChannel(r) && isChannel(g) && isChannel(bl)) {\n const a = Math.round(alpha * 100) / 100;\n return a === 1 ? `rgb(${r},${g},${bl})` : `rgba(${r},${g},${bl},${a})`;\n }\n }\n\n return `rgb(${f.red},${f.green},${f.blue})`;\n};\n\nexport const createNeutralToken = (\n appearance: 'light' | 'dark',\n neutralColor: NeutralColors | undefined,\n status: { colorErrorBg: string; colorWarningBg: string },\n) => {\n const scale = (neutralColor && neutralColorScales[neutralColor]) || gray;\n const [colorText, colorTextSecondary, colorTextTertiary, colorTextQuaternary] =\n neutralText[(neutralColor && neutralColorScales[neutralColor] && neutralColor) || 'gray'][\n appearance\n ];\n const token = {\n ...generateColorNeutralPalette({ appearance, scale }),\n colorText,\n colorTextQuaternary,\n colorTextSecondary,\n colorTextTertiary,\n };\n\n return {\n ...token,\n colorBgContainerDisabled: token.colorFillTertiary,\n colorBgContainerSecondary: mixColors(0.5, token.colorBgLayout, token.colorBgContainer),\n colorBgTextActive: token.colorFill,\n colorBgTextHover: token.colorFillSecondary,\n colorBorderBg: token.colorBgContainer,\n colorErrorOutline: getAlphaColor(status.colorErrorBg, token.colorBgContainer),\n colorFillAlter: token.colorFillQuaternary,\n colorFillContent: token.colorFillSecondary,\n colorFillContentHover: token.colorFill,\n colorIcon: token.colorTextTertiary,\n colorIconHover: token.colorText,\n colorSplit: getAlphaColor(token.colorBorderSecondary, token.colorBgContainer),\n colorTextDescription: token.colorTextTertiary,\n colorTextDisabled: token.colorTextQuaternary,\n colorTextHeading: token.colorText,\n colorTextLabel: token.colorTextSecondary,\n colorTextPlaceholder: token.colorTextQuaternary,\n colorWarningOutline: getAlphaColor(status.colorWarningBg, token.colorBgContainer),\n controlItemBgActiveDisabled: token.colorFill,\n controlItemBgHover: token.colorFillTertiary,\n controlTmpOutline: token.colorFillQuaternary,\n };\n};\n"],"mappings":";;;;;;AAQA,MAAM,aAAa,UAAkB,SAAS,KAAK,SAAS;AAG5D,MAAM,iBAAiB,OAAe,SAAiB;CACrD,MAAM,IAAI,SAAS,KAAK;CACxB,IAAI,EAAE,UAAU,KAAA,GAAW,OAAO;CAClC,MAAM,IAAI,SAAS,IAAI;CAEvB,KAAK,IAAI,QAAQ,KAAM,SAAS,GAAG,SAAS,KAAM;EAChD,MAAM,IAAI,KAAK,OAAO,EAAE,MAAM,EAAE,OAAO,IAAI,UAAU,KAAK;EAC1D,MAAM,IAAI,KAAK,OAAO,EAAE,QAAQ,EAAE,SAAS,IAAI,UAAU,KAAK;EAC9D,MAAM,KAAK,KAAK,OAAO,EAAE,OAAO,EAAE,QAAQ,IAAI,UAAU,KAAK;EAC7D,IAAI,UAAU,CAAC,KAAK,UAAU,CAAC,KAAK,UAAU,EAAE,GAAG;GACjD,MAAM,IAAI,KAAK,MAAM,QAAQ,GAAG,IAAI;GACpC,OAAO,MAAM,IAAI,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG,KAAK,QAAQ,EAAE,GAAG,EAAE,GAAG,GAAG,GAAG,EAAE;EACtE;CACF;CAEA,OAAO,OAAO,EAAE,IAAI,GAAG,EAAE,MAAM,GAAG,EAAE,KAAK;AAC3C;AAEA,MAAa,sBACX,YACA,cACA,WACG;CACH,MAAM,QAAS,gBAAgB,mBAAmB,iBAAkB;CACpE,MAAM,CAAC,WAAW,oBAAoB,mBAAmB,uBACvD,YAAa,gBAAgB,mBAAmB,iBAAiB,gBAAiB,OAAO,CACvF;CAEJ,MAAM,QAAQ;EACZ,GAAG,4BAA4B;GAAE;GAAY;EAAM,CAAC;EACpD;EACA;EACA;EACA;CACF;CAEA,OAAO;EACL,GAAG;EACH,0BAA0B,MAAM;EAChC,2BAA2B,UAAU,IAAK,MAAM,eAAe,MAAM,gBAAgB;EACrF,mBAAmB,MAAM;EACzB,kBAAkB,MAAM;EACxB,eAAe,MAAM;EACrB,mBAAmB,cAAc,OAAO,cAAc,MAAM,gBAAgB;EAC5E,gBAAgB,MAAM;EACtB,kBAAkB,MAAM;EACxB,uBAAuB,MAAM;EAC7B,WAAW,MAAM;EACjB,gBAAgB,MAAM;EACtB,YAAY,cAAc,MAAM,sBAAsB,MAAM,gBAAgB;EAC5E,sBAAsB,MAAM;EAC5B,mBAAmB,MAAM;EACzB,kBAAkB,MAAM;EACxB,gBAAgB,MAAM;EACtB,sBAAsB,MAAM;EAC5B,qBAAqB,cAAc,OAAO,gBAAgB,MAAM,gBAAgB;EAChF,6BAA6B,MAAM;EACnC,oBAAoB,MAAM;EAC1B,mBAAmB,MAAM;CAC3B;AACF"}
1
+ {"version":3,"file":"neutral.mjs","names":[],"sources":["../../../../src/styles/theme/token/neutral.ts"],"sourcesContent":["import { mixColors, parseRgb } from '@/utils/colorMath';\n\nimport { type LobeNeutralColor, resolveNeutral } from '../customNeutral';\nimport { generateColorNeutralPalette } from '../generateColorPalette';\n\nconst isChannel = (value: number) => value >= 0 && value <= 255;\n\n// port of antd's getAlphaColor: the most transparent rgba that renders `front` over `back`\nconst getAlphaColor = (front: string, back: string) => {\n const f = parseRgb(front);\n if (f.alpha !== undefined) return front;\n const b = parseRgb(back);\n\n for (let alpha = 0.01; alpha <= 1; alpha += 0.01) {\n const r = Math.round((f.red - b.red * (1 - alpha)) / alpha);\n const g = Math.round((f.green - b.green * (1 - alpha)) / alpha);\n const bl = Math.round((f.blue - b.blue * (1 - alpha)) / alpha);\n if (isChannel(r) && isChannel(g) && isChannel(bl)) {\n const a = Math.round(alpha * 100) / 100;\n return a === 1 ? `rgb(${r},${g},${bl})` : `rgba(${r},${g},${bl},${a})`;\n }\n }\n\n return `rgb(${f.red},${f.green},${f.blue})`;\n};\n\nexport const createNeutralToken = (\n appearance: 'light' | 'dark',\n neutralColor: LobeNeutralColor | undefined,\n status: { colorErrorBg: string; colorWarningBg: string },\n) => {\n const { scale, text } = resolveNeutral(neutralColor);\n const [colorText, colorTextSecondary, colorTextTertiary, colorTextQuaternary] = text[appearance];\n const token = {\n ...generateColorNeutralPalette({ appearance, scale }),\n colorText,\n colorTextQuaternary,\n colorTextSecondary,\n colorTextTertiary,\n };\n\n return {\n ...token,\n colorBgContainerDisabled: token.colorFillTertiary,\n colorBgContainerSecondary: mixColors(0.5, token.colorBgLayout, token.colorBgContainer),\n colorBgTextActive: token.colorFill,\n colorBgTextHover: token.colorFillSecondary,\n colorBorderBg: token.colorBgContainer,\n colorErrorOutline: getAlphaColor(status.colorErrorBg, token.colorBgContainer),\n colorFillAlter: token.colorFillQuaternary,\n colorFillContent: token.colorFillSecondary,\n colorFillContentHover: token.colorFill,\n colorIcon: token.colorTextTertiary,\n colorIconHover: token.colorText,\n colorSplit: getAlphaColor(token.colorBorderSecondary, token.colorBgContainer),\n colorTextDescription: token.colorTextTertiary,\n colorTextDisabled: token.colorTextQuaternary,\n colorTextHeading: token.colorText,\n colorTextLabel: token.colorTextSecondary,\n colorTextPlaceholder: token.colorTextQuaternary,\n colorWarningOutline: getAlphaColor(status.colorWarningBg, token.colorBgContainer),\n controlItemBgActiveDisabled: token.colorFill,\n controlItemBgHover: token.colorFillTertiary,\n controlTmpOutline: token.colorFillQuaternary,\n };\n};\n"],"mappings":";;;;AAKA,MAAM,aAAa,UAAkB,SAAS,KAAK,SAAS;AAG5D,MAAM,iBAAiB,OAAe,SAAiB;CACrD,MAAM,IAAI,SAAS,KAAK;CACxB,IAAI,EAAE,UAAU,KAAA,GAAW,OAAO;CAClC,MAAM,IAAI,SAAS,IAAI;CAEvB,KAAK,IAAI,QAAQ,KAAM,SAAS,GAAG,SAAS,KAAM;EAChD,MAAM,IAAI,KAAK,OAAO,EAAE,MAAM,EAAE,OAAO,IAAI,UAAU,KAAK;EAC1D,MAAM,IAAI,KAAK,OAAO,EAAE,QAAQ,EAAE,SAAS,IAAI,UAAU,KAAK;EAC9D,MAAM,KAAK,KAAK,OAAO,EAAE,OAAO,EAAE,QAAQ,IAAI,UAAU,KAAK;EAC7D,IAAI,UAAU,CAAC,KAAK,UAAU,CAAC,KAAK,UAAU,EAAE,GAAG;GACjD,MAAM,IAAI,KAAK,MAAM,QAAQ,GAAG,IAAI;GACpC,OAAO,MAAM,IAAI,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG,KAAK,QAAQ,EAAE,GAAG,EAAE,GAAG,GAAG,GAAG,EAAE;EACtE;CACF;CAEA,OAAO,OAAO,EAAE,IAAI,GAAG,EAAE,MAAM,GAAG,EAAE,KAAK;AAC3C;AAEA,MAAa,sBACX,YACA,cACA,WACG;CACH,MAAM,EAAE,OAAO,SAAS,eAAe,YAAY;CACnD,MAAM,CAAC,WAAW,oBAAoB,mBAAmB,uBAAuB,KAAK;CACrF,MAAM,QAAQ;EACZ,GAAG,4BAA4B;GAAE;GAAY;EAAM,CAAC;EACpD;EACA;EACA;EACA;CACF;CAEA,OAAO;EACL,GAAG;EACH,0BAA0B,MAAM;EAChC,2BAA2B,UAAU,IAAK,MAAM,eAAe,MAAM,gBAAgB;EACrF,mBAAmB,MAAM;EACzB,kBAAkB,MAAM;EACxB,eAAe,MAAM;EACrB,mBAAmB,cAAc,OAAO,cAAc,MAAM,gBAAgB;EAC5E,gBAAgB,MAAM;EACtB,kBAAkB,MAAM;EACxB,uBAAuB,MAAM;EAC7B,WAAW,MAAM;EACjB,gBAAgB,MAAM;EACtB,YAAY,cAAc,MAAM,sBAAsB,MAAM,gBAAgB;EAC5E,sBAAsB,MAAM;EAC5B,mBAAmB,MAAM;EACzB,kBAAkB,MAAM;EACxB,gBAAgB,MAAM;EACtB,sBAAsB,MAAM;EAC5B,qBAAqB,cAAc,OAAO,gBAAgB,MAAM,gBAAgB;EAChF,6BAA6B,MAAM;EACnC,oBAAoB,MAAM;EAC1B,mBAAmB,MAAM;CAC3B;AACF"}
@@ -0,0 +1,4 @@
1
+ //#region src/styles/theme/token/neutralText.d.ts
2
+ export type NeutralTextLadder = Record<'dark' | 'light', [string, string, string, string]>;
3
+ //#endregion
4
+ //# sourceMappingURL=neutralText.d.mts.map
@@ -0,0 +1,17 @@
1
+ import { isValidElement, version } from "react";
2
+ //#region src/utils/getElementRef.ts
3
+ const IS_REACT_19 = Number.parseInt(version, 10) >= 19;
4
+ /**
5
+ * The ref attached to a React element. React 19 moved it to `props.ref` and warns
6
+ * ("Accessing element.ref was removed in React 19") when `element.ref` is read; React 18
7
+ * keeps it on the element.
8
+ */
9
+ const getElementRef = (element) => {
10
+ if (!isValidElement(element)) return void 0;
11
+ if (IS_REACT_19) return element.props?.ref;
12
+ return element.ref;
13
+ };
14
+ //#endregion
15
+ export { getElementRef };
16
+
17
+ //# sourceMappingURL=getElementRef.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getElementRef.mjs","names":[],"sources":["../../src/utils/getElementRef.ts"],"sourcesContent":["import { isValidElement, type Ref, version } from 'react';\n\nconst IS_REACT_19 = Number.parseInt(version, 10) >= 19;\n\n/**\n * The ref attached to a React element. React 19 moved it to `props.ref` and warns\n * (\"Accessing element.ref was removed in React 19\") when `element.ref` is read; React 18\n * keeps it on the element.\n */\nexport const getElementRef = <T = unknown>(element: unknown): Ref<T> | undefined => {\n if (!isValidElement(element)) return undefined;\n if (IS_REACT_19) return (element.props as { ref?: Ref<T> } | null)?.ref;\n return (element as unknown as { ref?: Ref<T> }).ref;\n};\n"],"mappings":";;AAEA,MAAM,cAAc,OAAO,SAAS,SAAS,EAAE,KAAK;;;;;;AAOpD,MAAa,iBAA8B,YAAyC;CAClF,IAAI,CAAC,eAAe,OAAO,GAAG,OAAO,KAAA;CACrC,IAAI,aAAa,OAAQ,QAAQ,OAAmC;CACpE,OAAQ,QAAwC;AAClD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/ui",
3
- "version": "6.0.0",
3
+ "version": "6.0.1",
4
4
  "description": "Lobe UI is an open-source UI component library for building AIGC web apps",
5
5
  "keywords": [
6
6
  "lobehub",