@lmb-it/kitsconcerto-native 0.1.294 → 0.1.295

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.
@@ -22,7 +22,7 @@ const useFormManager = ({
22
22
  }
23
23
  }, [elements]);
24
24
  const defaultValues = useMemo(() => getDefaultValues(elements), [elements]);
25
- const schema = useMemo(() => schemaBuilder(elements), [elements]);
25
+ const schema = useMemo(() => schemaBuilder(elements), [elements, id]);
26
26
  const combinedChildIds = useMemo(() => getCombinedChildIds(elements), [elements]);
27
27
  const stripCombinedChildren = useCallback((data) => {
28
28
  if (combinedChildIds.size === 0) return data;
@@ -80,7 +80,7 @@ const useFormManager = ({
80
80
  [outputFormat, onSubmit, setIsSubmitting, formMethods, stripCombinedChildren]
81
81
  );
82
82
  const handleSubmitWrapper = useCallback(() => {
83
- formMethods.handleSubmit(onValidSubmit, (errors) => {
83
+ return formMethods.handleSubmit(onValidSubmit, (errors) => {
84
84
  setIsSubmitting(false);
85
85
  onInvalidSubmit?.(errors);
86
86
  })();
@@ -91,8 +91,9 @@ const useFormManager = ({
91
91
  return acc[key];
92
92
  }, obj);
93
93
  }
94
+ const { watch } = formMethods;
94
95
  useEffect(() => {
95
- const subscription = formMethods.watch((currentValues, { name, type }) => {
96
+ const subscription = watch((currentValues, { name, type }) => {
96
97
  onChange?.(stripCombinedChildren(currentValues));
97
98
  if (onChangeSingleValue && name && type === "change") {
98
99
  if (!combinedChildIds.has(name)) {
@@ -102,7 +103,7 @@ const useFormManager = ({
102
103
  }
103
104
  });
104
105
  return () => subscription.unsubscribe();
105
- }, [formMethods.watch, onChange, onChangeSingleValue, stripCombinedChildren, combinedChildIds]);
106
+ }, [watch, onChange, onChangeSingleValue, stripCombinedChildren, combinedChildIds]);
106
107
  return {
107
108
  formMethods,
108
109
  // The original object for FormProvider
@@ -1 +1 @@
1
- {"version":3,"file":"useFormManager.js","sources":["../../../../src/form-renderer/hooks/useFormManager.ts"],"sourcesContent":["// hooks/useFormManager.ts\n\nimport {type Dispatch, type SetStateAction, useCallback, useEffect, useMemo} from 'react';\nimport {type FieldValues, type SubmitHandler, useForm, type UseFormReturn, type DefaultValues} from 'react-hook-form';\nimport {yupResolver} from '@hookform/resolvers/yup';\nimport {checkNameDuplication, getCombinedChildIds, getDefaultValues, schemaBuilder} from '../utils';\nimport type {IFormComponent} from '@lmb-it/kitsconcerto-types';\n\n// --- Using Your Provided Type Definitions ---\n\n\n// --- CORRECTED RETURN TYPE ---\nexport interface UseFormManagerReturn<T extends FieldValues> {\n /**\n * The original, unmodified methods returned from react-hook-form's useForm hook.\n * This is what gets passed to the FormProvider.\n */\n formMethods: UseFormReturn<T>;\n /**\n * Our custom loading state for the submit button.\n */\n isSubmitting: boolean;\n /**\n * Our wrapped submit handler that manages the isSubmitting state.\n * This is what the <form> element's onSubmit will call.\n */\n handleSubmit: () => void;\n\n setIsSubmitting: Dispatch<SetStateAction<boolean>>\n}\n\n// A helper utility to check for valid URLs, as used in your original getDefaultValues.\nconst isValidURL = (urlString: string): boolean => {\n try {\n const url = new URL(urlString);\n return ['http:', 'https:'].includes(url.protocol);\n } catch (_) {\n return false;\n }\n};\n\nexport const useFormManager = <T extends FieldValues>({\n elements,\n onSubmit,\n onInvalidSubmit, id,\n onChange,\n onChangeSingleValue, // <-- Add this\n outputFormat = 'Json'\n }: IFormComponent<T>, isSubmitting: boolean, setIsSubmitting: Dispatch<SetStateAction<boolean>>): UseFormManagerReturn<T> => {\n\n\n // 1. Check for duplicate names on mount and when elements change.\n useEffect(() => {\n try {\n checkNameDuplication(elements);\n } catch (error) {\n console.error(\"Form Configuration Error:\", error);\n }\n }, [elements]);\n\n // 2. Generate the default values object, memoized for performance.\n const defaultValues = useMemo(() => getDefaultValues(elements), [elements]);\n\n const schema = useMemo(() => schemaBuilder(elements, id!), [elements]);\n\n // 2b. Collect Combined children IDs to exclude from output data.\n const combinedChildIds = useMemo(() => getCombinedChildIds(elements), [elements]);\n\n const stripCombinedChildren = useCallback((data: Record<string, any>) => {\n if (combinedChildIds.size === 0) return data;\n const filtered: Record<string, any> = {};\n for (const [key, value] of Object.entries(data)) {\n if (!combinedChildIds.has(key)) {\n filtered[key] = value;\n }\n }\n return filtered;\n }, [combinedChildIds]);\n\n // console.log(id, elements, schema)\n\n // console.log(schema.describe())\n // This is the original, unmodified object from react-hook-form\n const formMethods = useForm<T>({\n resolver: yupResolver(schema),\n defaultValues: defaultValues as DefaultValues<T>,\n shouldUnregister: false,\n mode: \"onSubmit\",\n reValidateMode: \"onChange\",\n // ... (defaultValues logic remains the same)\n });\n\n\n const onValidSubmit: SubmitHandler<any> = useCallback(\n (rawData) => {\n const data = stripCombinedChildren(rawData);\n if (outputFormat === \"FormData\") {\n const formData = new FormData();\n for (const [key, value] of Object.entries(data)) {\n if (Array.isArray(value)) {\n if (value.length === 1 && value[0] instanceof File) {\n formData.append(key, value[0]);\n } else {\n value.forEach((item) => {\n if (item instanceof File) {\n formData.append(`${key}[]`, item);\n } else if (\n typeof item === \"number\" ||\n typeof item === \"string\"\n ) {\n formData.append(`${key}[]`, item.toString());\n } else {\n formData.append(`${key}[]`, JSON.stringify(item));\n }\n });\n }\n } else {\n if (value instanceof File) {\n formData.append(key, value);\n } else if (\n typeof value === \"number\" ||\n typeof value === \"string\"\n ) {\n formData.append(key, value.toString());\n } else {\n formData.append(key, JSON.stringify(value));\n }\n }\n }\n\n onSubmit && onSubmit(formData, setIsSubmitting, formMethods);\n } else {\n onSubmit && onSubmit(data, setIsSubmitting, formMethods);\n }\n },\n [outputFormat, onSubmit, setIsSubmitting, formMethods, stripCombinedChildren],\n );\n\n\n // Our custom wrapper function\n const handleSubmitWrapper = useCallback(() => {\n // We call the ORIGINAL handleSubmit from formMethods here\n formMethods.handleSubmit(onValidSubmit, (errors) => {\n setIsSubmitting(false);\n onInvalidSubmit?.(errors);\n })();\n }, [formMethods, onValidSubmit, setIsSubmitting, onInvalidSubmit]);\n\n function getValueByPath(obj: any, path: string) {\n return path.split(\".\").reduce((acc, key) => {\n if (acc === undefined || acc === null) return undefined;\n return acc[key];\n }, obj);\n }\n\n // This useEffect hook handles both global and single-value change events.\n useEffect(() => {\n const subscription = formMethods.watch((currentValues, {name, type}) => {\n // 1. Trigger the global onChange callback with all values (excluding Combined children).\n onChange?.(stripCombinedChildren(currentValues as { [s: string]: any }));\n\n // 2. Trigger the single value change callback (skip Combined child fields).\n if (onChangeSingleValue && name && type === 'change') {\n if (!combinedChildIds.has(name)) {\n const value = getValueByPath(currentValues, name);\n onChangeSingleValue(name, value);\n }\n }\n });\n return () => subscription.unsubscribe();\n }, [formMethods.watch, onChange, onChangeSingleValue, stripCombinedChildren, combinedChildIds]);\n\n // Return the two concerns separately\n return {\n formMethods, // The original object for FormProvider\n isSubmitting,\n setIsSubmitting,\n handleSubmit: handleSubmitWrapper, // The wrapped function for our UI\n };\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useFormManager.js","sources":["../../../../src/form-renderer/hooks/useFormManager.ts"],"sourcesContent":["// hooks/useFormManager.ts\n\nimport {type Dispatch, type SetStateAction, useCallback, useEffect, useMemo} from 'react';\nimport {type FieldValues, type SubmitHandler, useForm, type UseFormReturn, type DefaultValues} from 'react-hook-form';\nimport {yupResolver} from '@hookform/resolvers/yup';\nimport {checkNameDuplication, getCombinedChildIds, getDefaultValues, schemaBuilder} from '../utils';\nimport type {IFormComponent} from '@lmb-it/kitsconcerto-types';\n\n// --- Using Your Provided Type Definitions ---\n\n\n// --- CORRECTED RETURN TYPE ---\nexport interface UseFormManagerReturn<T extends FieldValues> {\n /**\n * The original, unmodified methods returned from react-hook-form's useForm hook.\n * This is what gets passed to the FormProvider.\n */\n formMethods: UseFormReturn<T>;\n /**\n * Our custom loading state for the submit button.\n */\n isSubmitting: boolean;\n /**\n * Our wrapped submit handler that manages the isSubmitting state.\n * This is what the <form> element's onSubmit will call.\n */\n handleSubmit: () => Promise<void>;\n\n setIsSubmitting: Dispatch<SetStateAction<boolean>>\n}\n\nexport const useFormManager = <T extends FieldValues>({\n elements,\n onSubmit,\n onInvalidSubmit, id,\n onChange,\n onChangeSingleValue, // <-- Add this\n outputFormat = 'Json'\n }: IFormComponent<T>, isSubmitting: boolean, setIsSubmitting: Dispatch<SetStateAction<boolean>>): UseFormManagerReturn<T> => {\n\n\n // 1. Check for duplicate names on mount and when elements change.\n useEffect(() => {\n try {\n checkNameDuplication(elements);\n } catch (error) {\n console.error(\"Form Configuration Error:\", error);\n }\n }, [elements]);\n\n // 2. Generate the default values object, memoized for performance.\n const defaultValues = useMemo(() => getDefaultValues(elements), [elements]);\n\n const schema = useMemo(() => schemaBuilder(elements, id!), [elements, id]);\n\n // 2b. Collect Combined children IDs to exclude from output data.\n const combinedChildIds = useMemo(() => getCombinedChildIds(elements), [elements]);\n\n const stripCombinedChildren = useCallback((data: Record<string, any>) => {\n if (combinedChildIds.size === 0) return data;\n const filtered: Record<string, any> = {};\n for (const [key, value] of Object.entries(data)) {\n if (!combinedChildIds.has(key)) {\n filtered[key] = value;\n }\n }\n return filtered;\n }, [combinedChildIds]);\n\n // console.log(id, elements, schema)\n\n // console.log(schema.describe())\n // This is the original, unmodified object from react-hook-form\n const formMethods = useForm<T>({\n resolver: yupResolver(schema),\n defaultValues: defaultValues as DefaultValues<T>,\n shouldUnregister: false,\n mode: \"onSubmit\",\n reValidateMode: \"onChange\",\n // ... (defaultValues logic remains the same)\n });\n\n\n const onValidSubmit: SubmitHandler<any> = useCallback(\n (rawData) => {\n const data = stripCombinedChildren(rawData);\n if (outputFormat === \"FormData\") {\n const formData = new FormData();\n for (const [key, value] of Object.entries(data)) {\n if (Array.isArray(value)) {\n if (value.length === 1 && value[0] instanceof File) {\n formData.append(key, value[0]);\n } else {\n value.forEach((item) => {\n if (item instanceof File) {\n formData.append(`${key}[]`, item);\n } else if (\n typeof item === \"number\" ||\n typeof item === \"string\"\n ) {\n formData.append(`${key}[]`, item.toString());\n } else {\n formData.append(`${key}[]`, JSON.stringify(item));\n }\n });\n }\n } else {\n if (value instanceof File) {\n formData.append(key, value);\n } else if (\n typeof value === \"number\" ||\n typeof value === \"string\"\n ) {\n formData.append(key, value.toString());\n } else {\n formData.append(key, JSON.stringify(value));\n }\n }\n }\n\n onSubmit && onSubmit(formData, setIsSubmitting, formMethods);\n } else {\n onSubmit && onSubmit(data, setIsSubmitting, formMethods);\n }\n },\n [outputFormat, onSubmit, setIsSubmitting, formMethods, stripCombinedChildren],\n );\n\n\n // Our custom wrapper function\n const handleSubmitWrapper = useCallback(() => {\n // We call the ORIGINAL handleSubmit from formMethods here\n return formMethods.handleSubmit(onValidSubmit, (errors) => {\n setIsSubmitting(false);\n onInvalidSubmit?.(errors);\n })();\n }, [formMethods, onValidSubmit, setIsSubmitting, onInvalidSubmit]);\n\n function getValueByPath(obj: any, path: string) {\n return path.split(\".\").reduce((acc, key) => {\n if (acc === undefined || acc === null) return undefined;\n return acc[key];\n }, obj);\n }\n\n // This useEffect hook handles both global and single-value change events.\n const {watch} = formMethods;\n useEffect(() => {\n const subscription = watch((currentValues, {name, type}) => {\n // 1. Trigger the global onChange callback with all values (excluding Combined children).\n onChange?.(stripCombinedChildren(currentValues as { [s: string]: any }));\n\n // 2. Trigger the single value change callback (skip Combined child fields).\n if (onChangeSingleValue && name && type === 'change') {\n if (!combinedChildIds.has(name)) {\n const value = getValueByPath(currentValues, name);\n onChangeSingleValue(name, value);\n }\n }\n });\n return () => subscription.unsubscribe();\n }, [watch, onChange, onChangeSingleValue, stripCombinedChildren, combinedChildIds]);\n\n // Return the two concerns separately\n return {\n formMethods, // The original object for FormProvider\n isSubmitting,\n setIsSubmitting,\n handleSubmit: handleSubmitWrapper, // The wrapped function for our UI\n };\n};\n"],"names":[],"mappings":"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"}
@@ -7,6 +7,7 @@ const WHITE = "#ffffff";
7
7
  function useSeverityColors(severity) {
8
8
  const { theme, resolveToken, colorMode } = KitsThemeProvider_native.useKitsTheme();
9
9
  return React.useMemo(() => {
10
+ const slots = theme.severity?.[severity] ?? theme.severity?.secondary;
10
11
  const relativeLuminance = (css) => {
11
12
  const m = /^#?([0-9a-f]{6})$/i.exec(css.trim());
12
13
  if (!m) return null;
@@ -21,7 +22,7 @@ function useSeverityColors(severity) {
21
22
  return l === null ? null : 1.05 / (l + 0.05);
22
23
  };
23
24
  const resolveSolidText = (fill) => {
24
- if (slots.solidText) return resolveToken(slots.solidText);
25
+ if (slots?.solidText) return resolveToken(slots.solidText);
25
26
  const ratio = fill ? whiteOn(fill) : null;
26
27
  if (ratio !== null && ratio < 4.5) {
27
28
  const ink = resolveToken("text-inverse");
@@ -52,7 +53,6 @@ function useSeverityColors(severity) {
52
53
  border: fill
53
54
  };
54
55
  }
55
- const slots = theme.severity?.[severity] ?? theme.severity?.secondary;
56
56
  if (!slots) {
57
57
  const isDark = colorMode === "dark";
58
58
  const pick = (token, backstop) => {
@@ -121,9 +121,11 @@ function useSeverityColors(severity) {
121
121
  }
122
122
  function useAllSeverityColors() {
123
123
  const { theme, resolveToken, colorMode } = KitsThemeProvider_native.useKitsTheme();
124
+ const interactive = useSeverityColors("primary-interactive");
124
125
  return React.useMemo(() => {
125
126
  const severities = ["success", "info", "warning", "danger", "help", "secondary", "brand"];
126
127
  const result = {};
128
+ result["primary-interactive"] = interactive;
127
129
  const primary = resolveToken("primary");
128
130
  for (const key of severities) {
129
131
  const slots = theme.severity?.[key];
@@ -154,7 +156,7 @@ function useAllSeverityColors() {
154
156
  }
155
157
  }
156
158
  return result;
157
- }, [theme.severity, resolveToken, colorMode]);
159
+ }, [theme.severity, resolveToken, colorMode, interactive]);
158
160
  }
159
161
 
160
162
  exports.useAllSeverityColors = useAllSeverityColors;
@@ -1 +1 @@
1
- {"version":3,"file":"useSeverityColors.cjs","sources":["../../../src/hooks/useSeverityColors.ts"],"sourcesContent":["import {useMemo} from 'react';\nimport {useKitsTheme} from '../contexts/Theme';\nimport type {Severity, ISeverityColorSlots} from '@lmb-it/kitsconcerto-types';\n\nexport interface ResolvedSeverityColors {\n solid: string;\n solidText: string;\n bgTint: string;\n iconFg: string;\n iconBg: string;\n text: string;\n border: string;\n}\n\nconst WHITE = '#ffffff';\n\n/**\n * Resolves all severity color slots for a given severity using the current theme.\n * All slot values go through resolveToken() — so they support:\n * - Raw values: '#ff0000', 'black', 'rgb(..)'\n * - Semantic tokens: 'primary', 'danger'\n * - Dot-notation: 'red.700', 'blue.500'\n * - Prefixed tokens: '$spacing.4'\n *\n * solidText defaults to white if not explicitly set by the consumer.\n */\n/**\n * ⛔ SHADE-SWAPPING ONLY WORKS ON A SHADED TOKEN, AND FOUR OF THIS LIBRARY'S\n * SEVEN SEVERITIES DO NOT HAVE ONE.\n *\n * Dark mode derives its tint and its text from the severity's `solid` value by\n * replacing the shade — `amber.500` → `amber.900` for the background and\n * `amber.200` for the text. That is only meaningful when `solid` LOOKS like\n * `name.NNN`. In `defaultTheme.ts` it often does not:\n *\n * brand brand.500 shaded → swap works\n * success green.500 shaded → swap works\n * secondary gray.500 shaded → swap works\n * danger #E3595B RAW HEX → swap is a no-op\n * warning #F59E0B RAW HEX → swap is a no-op\n * info #4F5BD5 RAW HEX → swap is a no-op\n * help #4F5BD5 RAW HEX → swap is a no-op\n *\n * ⛔ AND A NO-OP HERE IS NOT \"NO CHANGE\" — IT IS A COLLISION. The regex matches\n * nothing, so BOTH the background and the text resolve to the SAME raw hex, and\n * the text becomes invisible on its own background. Measured on the device\n * (AlphaMatch, Me tab, warning banner, dark): the action row was 6,923 sampled\n * pixels of #F59E0B with no text ink in it at all — the label and its reason\n * had vanished while the banner still looked like a banner.\n *\n * ⚠️ IT HITS THE FOUR SEVERITIES PEOPLE SEE MOST — errors, warnings, info and\n * help — and this hook is SHARED, not native-only, so web surfaces have it too.\n *\n * 💡 THE FALLBACK IS THE DESIGNED LIGHT PAIR. When there is no shade to swap,\n * the light slots are used instead: they are a pair someone chose to be legible\n * together, which is the one thing that must be true here. A dark-mode tint\n * would look better and is a palette decision, not a correctness one.\n */\nexport function useSeverityColors(severity: Severity): ResolvedSeverityColors {\n const {theme, resolveToken, colorMode} = useKitsTheme();\n\n return useMemo(() => {\n /*\n * ⛔ THE HELPERS AND THE `primary-interactive` BRANCH SIT ABOVE THE\n * NO-SLOTS RETURN ON PURPOSE. They used to sit below it, which meant a\n * product shipping no `theme.severity` at all never reached them and got\n * a grey secondary button while explicitly asking for an interactive\n * one. The helpers are pure definitions, so hoisting them changes\n * nothing for any other severity.\n */\n /*\n * ⛔ WHITE IS NOT A SAFE DEFAULT ON A FILL THAT FOLLOWS THE THEME —\n * R49, 25 October 2026, and this is the same fault as the no-slots\n * fallback above, one branch along.\n *\n * `solidText` defaulted to white unconditionally while `solid` for\n * the brand severity follows the `primary` token, which moves with\n * the colour mode. In the AlphaMatch Portal `primary` is #20AAB0 in\n * light and **#26C6DA in dark**, so a filled brand button measured\n * **2.82:1** and then **2.06:1** — the worst reading in the product.\n * Found on `/verification`: \"Verify on phone\", white on bright teal.\n *\n * ⛔ IT PICKS BY LUMINANCE RATHER THAN BY MODE, deliberately. Keying\n * on `colorMode === 'dark'` would be wrong for any product whose\n * primary is DARK in dark mode, and it was a guess about palettes\n * this library does not own. Relative luminance asks the only\n * question that matters — is white readable on this exact colour —\n * and answers it for a palette nobody here has seen.\n *\n * ⚠️ A CONSUMER'S OWN `solidText` STILL WINS. This changes the\n * DEFAULT only; a product that has stated what the ink should be is\n * not second-guessed.\n */\n const relativeLuminance = (css: string): number | null => {\n const m = /^#?([0-9a-f]{6})$/i.exec(css.trim());\n if (!m) return null;\n const channel = (v: number) =>\n v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);\n const [r, g, b] = [0, 2, 4].map(i =>\n channel(parseInt(m[1].substr(i, 2), 16) / 255),\n );\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n };\n /** White on `fill`, as a WCAG contrast ratio. */\n const whiteOn = (fill: string): number | null => {\n const l = relativeLuminance(fill);\n return l === null ? null : 1.05 / (l + 0.05);\n };\n const resolveSolidText = (fill?: string) => {\n if (slots.solidText) return resolveToken(slots.solidText);\n // ⚠️ UNKNOWN SHAPES KEEP THE OLD BEHAVIOUR. A gradient, a CSS\n // variable or a named colour returns null here, and white stays —\n // this must not start guessing at values it cannot read.\n const ratio = fill ? whiteOn(fill) : null;\n if (ratio !== null && ratio < 4.5) {\n const ink = resolveToken('text-inverse');\n if (ink && ink !== 'text-inverse') return ink;\n const surface = resolveToken('surface');\n if (surface && surface !== 'surface') return surface;\n return resolveToken('gray.900');\n }\n return WHITE;\n };\n\n /*\n * ⛔ `primary-interactive` RESOLVES FROM TOKENS, NOT FROM `theme.severity`,\n * for the same reason `brand` does below: the colour belongs to the\n * consuming product's palette, and this library ships no default for it.\n *\n * ⛔ IT IS PLACED BEFORE THE SLOTS LOOKUP DELIBERATELY. A product that has\n * not defined `theme.severity['primary-interactive']` — which is every\n * product today — would otherwise fall into the no-slots fallback and get\n * a grey secondary button while asking for an interactive one.\n *\n * ⛔ AND THE INK IS ITS OWN TOKEN, WHICH IS THE WHOLE POINT. `solidText`\n * here is `on-primary-interactive`, not white and not a luminance guess.\n * The fill is designed to flip between modes (AlphaMatch: #157C82 light,\n * #4DD0E1 dark) and white on the dark value measures 1.84:1 — the exact\n * failure R49 fixed for `brand`. A pair that was chosen together cannot\n * be re-derived here as reliably as it can be stated.\n *\n * ⚠️ A PRODUCT WITHOUT THE TOKENS DEGRADES TO `primary` RATHER THAN TO A\n * TOKEN NAME. `resolveToken` returns its argument unchanged when it knows\n * nothing, so the guard below treats that as absent. The Portal is\n * therefore unaffected unless it both defines the tokens and asks for\n * this severity.\n */\n if (severity === 'primary-interactive') {\n const known = (token: string): string | null => {\n const value = resolveToken(token);\n return value && value !== token ? value : null;\n };\n const fill = known('primary-interactive') ?? resolveToken('primary');\n const ink = known('on-primary-interactive') ?? resolveSolidText(fill);\n return {\n solid: fill,\n solidText: ink,\n /* The soft slots have no token of their own; the brand's tint is\n the nearest thing a product has already chosen, and it is only\n used by outlined/soft treatments, never under `ink`. */\n bgTint: known('primary-subtle') ?? resolveToken('surface-muted'),\n iconFg: fill,\n iconBg: known('primary-subtle') ?? resolveToken('surface-muted'),\n text: fill,\n border: fill,\n };\n }\n\n const slots: ISeverityColorSlots = theme.severity?.[severity] ?? theme.severity?.secondary;\n if (!slots) {\n /*\n * ⛔ THE FALLBACK MUST FOLLOW THE COLOUR MODE, AND `gray.500` DID\n * NOT — R49, 25 October 2026. This branch is not an edge case: a\n * product that ships `severity: {}` lands here for every severity\n * except `brand`, which has its own dynamic path above. The\n * AlphaMatch Client Portal does exactly that, so EVERY outlined\n * secondary control in it was inked `#6B7280`.\n *\n * A scale step is a fixed point on a ramp; it is the same colour\n * in both palettes, while everything around it moves. Measured in\n * the Portal:\n *\n * outlined label, light page #F5F7F6 **4.49:1** (already failing)\n * outlined label, dark page #111827 **3.67:1**\n * outlined label, dark card #1F2937 **3.04:1**\n *\n * ⚠️ THIS IS THE SECOND TIME THE SAME MISTAKE HAS BEEN FOUND HERE.\n * `Tabs` defaulted `inactiveBackground` to `gray.100` while\n * labelling it with a token that flips, and four chips measured\n * 1.34:1 on a dark page; that was fixed at source in 0.1.290. The\n * rule it produced — a default that pairs a scale value with a\n * semantic one will be wrong in one mode — was not carried across\n * to this function.\n *\n * ⛔ AND `solid` DOES TWO JOBS, WHICH IS WHY IT NEEDS A PAIR.\n * `Button` uses it as the FILL when solid and as the INK when\n * outlined; `Chip` does the same. A value dark enough for white\n * text is unreadable as ink on a dark page, and vice versa. So\n * the ink follows the mode and `solidText` is the surface behind\n * it, which moves the opposite way:\n *\n * filled light #FFFFFF on #546067 6.47:1\n * filled dark #1F2937 on #D1D5DB 9.96:1\n * outlined light #546067 on #F5F7F6 6.01:1\n * outlined dark #D1D5DB on #111827 12.04:1\n *\n * ⚠️ AND IT DEGRADES RATHER THAN BREAKING. This library ships no\n * default semantic tokens — consumers supply them — so a product\n * without `text-secondary` or `surface` would get the token name\n * back instead of a colour. `pick()` keeps a mode-aware scale\n * step behind each one: still better than today, because the\n * backstop flips even though a scale value normally cannot.\n */\n const isDark = colorMode === 'dark';\n const pick = (token: string, backstop: string) => {\n const value = resolveToken(token);\n return !value || value === token ? resolveToken(backstop) : value;\n };\n const ink = pick('text-secondary', isDark ? 'gray.300' : 'gray.600');\n const ground = pick('surface', isDark ? 'gray.800' : 'gray.50');\n const tint = pick('surface-muted', isDark ? 'gray.700' : 'gray.100');\n return {\n solid: ink,\n solidText: ground,\n bgTint: tint,\n iconFg: ink,\n iconBg: tint,\n text: ink,\n border: ink,\n };\n }\n\n\n // Brand severity: solid/border/iconFg dynamically follow the primary token\n if (severity === 'brand') {\n const primary = resolveToken('primary');\n if (colorMode === 'dark') {\n // Same shade-guard as below: a `solid` with no shade cannot be\n // swapped, and collapsing it would put the text on its own colour.\n const swapShade = (token: string, target: string, fallbackSlot: string) =>\n /\\.\\d+$/.test(token)\n ? resolveToken(token.replace(/\\.\\d+$/, `.${target}`))\n : resolveToken(fallbackSlot);\n return {\n solid: primary,\n solidText: resolveSolidText(primary),\n bgTint: swapShade(slots.solid, '900', slots.bgTint),\n iconFg: primary,\n iconBg: swapShade(slots.solid, '800', slots.iconBg),\n text: swapShade(slots.solid, '200', slots.text),\n border: primary,\n };\n }\n return {\n solid: primary,\n solidText: resolveSolidText(primary),\n bgTint: resolveToken(slots.bgTint),\n iconFg: primary,\n iconBg: resolveToken(slots.iconBg),\n text: resolveToken(slots.text),\n border: primary,\n };\n }\n\n if (colorMode === 'dark') {\n // `fallbackSlot` is used when `token` carries no shade to swap —\n // see the note above this function.\n const swapShade = (token: string, target: string, fallbackSlot: string) =>\n /\\.\\d+$/.test(token)\n ? resolveToken(token.replace(/\\.\\d+$/, `.${target}`))\n : resolveToken(fallbackSlot);\n return {\n solid: resolveToken(slots.solid),\n solidText: resolveSolidText(resolveToken(slots.solid)),\n bgTint: swapShade(slots.solid, '900', slots.bgTint),\n iconFg: swapShade(slots.solid, '300', slots.iconFg),\n iconBg: swapShade(slots.solid, '800', slots.iconBg),\n text: swapShade(slots.solid, '200', slots.text),\n border: resolveToken(slots.border),\n };\n }\n\n return {\n solid: resolveToken(slots.solid),\n solidText: resolveSolidText(resolveToken(slots.solid)),\n bgTint: resolveToken(slots.bgTint),\n iconFg: resolveToken(slots.iconFg),\n iconBg: resolveToken(slots.iconBg),\n text: resolveToken(slots.text),\n border: resolveToken(slots.border),\n };\n }, [theme.severity, severity, resolveToken, colorMode]);\n}\n\n/**\n * Returns the full resolved map for all severities.\n */\nexport function useAllSeverityColors(): Record<Severity, ResolvedSeverityColors> {\n const {theme, resolveToken, colorMode} = useKitsTheme();\n\n return useMemo(() => {\n const severities: Severity[] = ['success', 'info', 'warning', 'danger', 'help', 'secondary', 'brand'];\n const result = {} as Record<Severity, ResolvedSeverityColors>;\n const primary = resolveToken('primary');\n\n for (const key of severities) {\n const slots = theme.severity?.[key];\n if (!slots) continue;\n\n const isBrand = key === 'brand';\n const solidText = slots.solidText ? resolveToken(slots.solidText) : WHITE;\n\n if (colorMode === 'dark') {\n // Same shade-guard as useSeverityColors — four of the seven\n // default severities carry a raw hex, which cannot be swapped.\n const swapShade = (token: string, target: string, fallbackSlot: string) =>\n /\\.\\d+$/.test(token)\n ? resolveToken(token.replace(/\\.\\d+$/, `.${target}`))\n : resolveToken(fallbackSlot);\n result[key] = {\n solid: isBrand ? primary : resolveToken(slots.solid),\n solidText,\n bgTint: swapShade(slots.solid, '900', slots.bgTint),\n iconFg: isBrand ? primary : swapShade(slots.solid, '300', slots.iconFg),\n iconBg: swapShade(slots.solid, '800', slots.iconBg),\n text: swapShade(slots.solid, '200', slots.text),\n border: isBrand ? primary : resolveToken(slots.border),\n };\n } else {\n result[key] = {\n solid: isBrand ? primary : resolveToken(slots.solid),\n solidText,\n bgTint: resolveToken(slots.bgTint),\n iconFg: isBrand ? primary : resolveToken(slots.iconFg),\n iconBg: resolveToken(slots.iconBg),\n text: resolveToken(slots.text),\n border: isBrand ? primary : resolveToken(slots.border),\n };\n }\n }\n\n return result;\n }, [theme.severity, resolveToken, colorMode]);\n}\n"],"names":["useKitsTheme","useMemo"],"mappings":"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"}
1
+ {"version":3,"file":"useSeverityColors.cjs","sources":["../../../src/hooks/useSeverityColors.ts"],"sourcesContent":["import {useMemo} from 'react';\nimport {useKitsTheme} from '../contexts/Theme';\nimport type {Severity, ISeverityColorSlots} from '@lmb-it/kitsconcerto-types';\n\nexport interface ResolvedSeverityColors {\n solid: string;\n solidText: string;\n bgTint: string;\n iconFg: string;\n iconBg: string;\n text: string;\n border: string;\n}\n\nconst WHITE = '#ffffff';\n\n/**\n * Resolves all severity color slots for a given severity using the current theme.\n * All slot values go through resolveToken() — so they support:\n * - Raw values: '#ff0000', 'black', 'rgb(..)'\n * - Semantic tokens: 'primary', 'danger'\n * - Dot-notation: 'red.700', 'blue.500'\n * - Prefixed tokens: '$spacing.4'\n *\n * solidText defaults to white if not explicitly set by the consumer.\n */\n/**\n * ⛔ SHADE-SWAPPING ONLY WORKS ON A SHADED TOKEN, AND FOUR OF THIS LIBRARY'S\n * SEVEN SEVERITIES DO NOT HAVE ONE.\n *\n * Dark mode derives its tint and its text from the severity's `solid` value by\n * replacing the shade — `amber.500` → `amber.900` for the background and\n * `amber.200` for the text. That is only meaningful when `solid` LOOKS like\n * `name.NNN`. In `defaultTheme.ts` it often does not:\n *\n * brand brand.500 shaded → swap works\n * success green.500 shaded → swap works\n * secondary gray.500 shaded → swap works\n * danger #E3595B RAW HEX → swap is a no-op\n * warning #F59E0B RAW HEX → swap is a no-op\n * info #4F5BD5 RAW HEX → swap is a no-op\n * help #4F5BD5 RAW HEX → swap is a no-op\n *\n * ⛔ AND A NO-OP HERE IS NOT \"NO CHANGE\" — IT IS A COLLISION. The regex matches\n * nothing, so BOTH the background and the text resolve to the SAME raw hex, and\n * the text becomes invisible on its own background. Measured on the device\n * (AlphaMatch, Me tab, warning banner, dark): the action row was 6,923 sampled\n * pixels of #F59E0B with no text ink in it at all — the label and its reason\n * had vanished while the banner still looked like a banner.\n *\n * ⚠️ IT HITS THE FOUR SEVERITIES PEOPLE SEE MOST — errors, warnings, info and\n * help — and this hook is SHARED, not native-only, so web surfaces have it too.\n *\n * 💡 THE FALLBACK IS THE DESIGNED LIGHT PAIR. When there is no shade to swap,\n * the light slots are used instead: they are a pair someone chose to be legible\n * together, which is the one thing that must be true here. A dark-mode tint\n * would look better and is a palette decision, not a correctness one.\n */\nexport function useSeverityColors(severity: Severity): ResolvedSeverityColors {\n const {theme, resolveToken, colorMode} = useKitsTheme();\n\n return useMemo(() => {\n const slots: ISeverityColorSlots | undefined = theme.severity?.[severity] ?? theme.severity?.secondary;\n /*\n * ⛔ THE HELPERS AND THE `primary-interactive` BRANCH SIT ABOVE THE\n * NO-SLOTS RETURN ON PURPOSE. They used to sit below it, which meant a\n * product shipping no `theme.severity` at all never reached them and got\n * a grey secondary button while explicitly asking for an interactive\n * one. The helpers are pure definitions, so hoisting them changes\n * nothing for any other severity.\n */\n /*\n * ⛔ WHITE IS NOT A SAFE DEFAULT ON A FILL THAT FOLLOWS THE THEME —\n * R49, 25 October 2026, and this is the same fault as the no-slots\n * fallback above, one branch along.\n *\n * `solidText` defaulted to white unconditionally while `solid` for\n * the brand severity follows the `primary` token, which moves with\n * the colour mode. In the AlphaMatch Portal `primary` is #20AAB0 in\n * light and **#26C6DA in dark**, so a filled brand button measured\n * **2.82:1** and then **2.06:1** — the worst reading in the product.\n * Found on `/verification`: \"Verify on phone\", white on bright teal.\n *\n * ⛔ IT PICKS BY LUMINANCE RATHER THAN BY MODE, deliberately. Keying\n * on `colorMode === 'dark'` would be wrong for any product whose\n * primary is DARK in dark mode, and it was a guess about palettes\n * this library does not own. Relative luminance asks the only\n * question that matters — is white readable on this exact colour —\n * and answers it for a palette nobody here has seen.\n *\n * ⚠️ A CONSUMER'S OWN `solidText` STILL WINS. This changes the\n * DEFAULT only; a product that has stated what the ink should be is\n * not second-guessed.\n */\n const relativeLuminance = (css: string): number | null => {\n const m = /^#?([0-9a-f]{6})$/i.exec(css.trim());\n if (!m) return null;\n const channel = (v: number) =>\n v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);\n const [r, g, b] = [0, 2, 4].map(i =>\n channel(parseInt(m[1].substr(i, 2), 16) / 255),\n );\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n };\n /** White on `fill`, as a WCAG contrast ratio. */\n const whiteOn = (fill: string): number | null => {\n const l = relativeLuminance(fill);\n return l === null ? null : 1.05 / (l + 0.05);\n };\n const resolveSolidText = (fill?: string) => {\n if (slots?.solidText) return resolveToken(slots.solidText);\n // ⚠️ UNKNOWN SHAPES KEEP THE OLD BEHAVIOUR. A gradient, a CSS\n // variable or a named colour returns null here, and white stays —\n // this must not start guessing at values it cannot read.\n const ratio = fill ? whiteOn(fill) : null;\n if (ratio !== null && ratio < 4.5) {\n const ink = resolveToken('text-inverse');\n if (ink && ink !== 'text-inverse') return ink;\n const surface = resolveToken('surface');\n if (surface && surface !== 'surface') return surface;\n return resolveToken('gray.900');\n }\n return WHITE;\n };\n\n /*\n * ⛔ `primary-interactive` RESOLVES FROM TOKENS, NOT FROM `theme.severity`,\n * for the same reason `brand` does below: the colour belongs to the\n * consuming product's palette, and this library ships no default for it.\n *\n * ⛔ IT IS PLACED BEFORE THE SLOTS LOOKUP DELIBERATELY. A product that has\n * not defined `theme.severity['primary-interactive']` — which is every\n * product today — would otherwise fall into the no-slots fallback and get\n * a grey secondary button while asking for an interactive one.\n *\n * ⛔ AND THE INK IS ITS OWN TOKEN, WHICH IS THE WHOLE POINT. `solidText`\n * here is `on-primary-interactive`, not white and not a luminance guess.\n * The fill is designed to flip between modes (AlphaMatch: #157C82 light,\n * #4DD0E1 dark) and white on the dark value measures 1.84:1 — the exact\n * failure R49 fixed for `brand`. A pair that was chosen together cannot\n * be re-derived here as reliably as it can be stated.\n *\n * ⚠️ A PRODUCT WITHOUT THE TOKENS DEGRADES TO `primary` RATHER THAN TO A\n * TOKEN NAME. `resolveToken` returns its argument unchanged when it knows\n * nothing, so the guard below treats that as absent. The Portal is\n * therefore unaffected unless it both defines the tokens and asks for\n * this severity.\n */\n if (severity === 'primary-interactive') {\n const known = (token: string): string | null => {\n const value = resolveToken(token);\n return value && value !== token ? value : null;\n };\n const fill = known('primary-interactive') ?? resolveToken('primary');\n const ink = known('on-primary-interactive') ?? resolveSolidText(fill);\n return {\n solid: fill,\n solidText: ink,\n /* The soft slots have no token of their own; the brand's tint is\n the nearest thing a product has already chosen, and it is only\n used by outlined/soft treatments, never under `ink`. */\n bgTint: known('primary-subtle') ?? resolveToken('surface-muted'),\n iconFg: fill,\n iconBg: known('primary-subtle') ?? resolveToken('surface-muted'),\n text: fill,\n border: fill,\n };\n }\n\n if (!slots) {\n /*\n * ⛔ THE FALLBACK MUST FOLLOW THE COLOUR MODE, AND `gray.500` DID\n * NOT — R49, 25 October 2026. This branch is not an edge case: a\n * product that ships `severity: {}` lands here for every severity\n * except `brand`, which has its own dynamic path above. The\n * AlphaMatch Client Portal does exactly that, so EVERY outlined\n * secondary control in it was inked `#6B7280`.\n *\n * A scale step is a fixed point on a ramp; it is the same colour\n * in both palettes, while everything around it moves. Measured in\n * the Portal:\n *\n * outlined label, light page #F5F7F6 **4.49:1** (already failing)\n * outlined label, dark page #111827 **3.67:1**\n * outlined label, dark card #1F2937 **3.04:1**\n *\n * ⚠️ THIS IS THE SECOND TIME THE SAME MISTAKE HAS BEEN FOUND HERE.\n * `Tabs` defaulted `inactiveBackground` to `gray.100` while\n * labelling it with a token that flips, and four chips measured\n * 1.34:1 on a dark page; that was fixed at source in 0.1.290. The\n * rule it produced — a default that pairs a scale value with a\n * semantic one will be wrong in one mode — was not carried across\n * to this function.\n *\n * ⛔ AND `solid` DOES TWO JOBS, WHICH IS WHY IT NEEDS A PAIR.\n * `Button` uses it as the FILL when solid and as the INK when\n * outlined; `Chip` does the same. A value dark enough for white\n * text is unreadable as ink on a dark page, and vice versa. So\n * the ink follows the mode and `solidText` is the surface behind\n * it, which moves the opposite way:\n *\n * filled light #FFFFFF on #546067 6.47:1\n * filled dark #1F2937 on #D1D5DB 9.96:1\n * outlined light #546067 on #F5F7F6 6.01:1\n * outlined dark #D1D5DB on #111827 12.04:1\n *\n * ⚠️ AND IT DEGRADES RATHER THAN BREAKING. This library ships no\n * default semantic tokens — consumers supply them — so a product\n * without `text-secondary` or `surface` would get the token name\n * back instead of a colour. `pick()` keeps a mode-aware scale\n * step behind each one: still better than today, because the\n * backstop flips even though a scale value normally cannot.\n */\n const isDark = colorMode === 'dark';\n const pick = (token: string, backstop: string) => {\n const value = resolveToken(token);\n return !value || value === token ? resolveToken(backstop) : value;\n };\n const ink = pick('text-secondary', isDark ? 'gray.300' : 'gray.600');\n const ground = pick('surface', isDark ? 'gray.800' : 'gray.50');\n const tint = pick('surface-muted', isDark ? 'gray.700' : 'gray.100');\n return {\n solid: ink,\n solidText: ground,\n bgTint: tint,\n iconFg: ink,\n iconBg: tint,\n text: ink,\n border: ink,\n };\n }\n\n\n // Brand severity: solid/border/iconFg dynamically follow the primary token\n if (severity === 'brand') {\n const primary = resolveToken('primary');\n if (colorMode === 'dark') {\n // Same shade-guard as below: a `solid` with no shade cannot be\n // swapped, and collapsing it would put the text on its own colour.\n const swapShade = (token: string, target: string, fallbackSlot: string) =>\n /\\.\\d+$/.test(token)\n ? resolveToken(token.replace(/\\.\\d+$/, `.${target}`))\n : resolveToken(fallbackSlot);\n return {\n solid: primary,\n solidText: resolveSolidText(primary),\n bgTint: swapShade(slots.solid, '900', slots.bgTint),\n iconFg: primary,\n iconBg: swapShade(slots.solid, '800', slots.iconBg),\n text: swapShade(slots.solid, '200', slots.text),\n border: primary,\n };\n }\n return {\n solid: primary,\n solidText: resolveSolidText(primary),\n bgTint: resolveToken(slots.bgTint),\n iconFg: primary,\n iconBg: resolveToken(slots.iconBg),\n text: resolveToken(slots.text),\n border: primary,\n };\n }\n\n if (colorMode === 'dark') {\n // `fallbackSlot` is used when `token` carries no shade to swap —\n // see the note above this function.\n const swapShade = (token: string, target: string, fallbackSlot: string) =>\n /\\.\\d+$/.test(token)\n ? resolveToken(token.replace(/\\.\\d+$/, `.${target}`))\n : resolveToken(fallbackSlot);\n return {\n solid: resolveToken(slots.solid),\n solidText: resolveSolidText(resolveToken(slots.solid)),\n bgTint: swapShade(slots.solid, '900', slots.bgTint),\n iconFg: swapShade(slots.solid, '300', slots.iconFg),\n iconBg: swapShade(slots.solid, '800', slots.iconBg),\n text: swapShade(slots.solid, '200', slots.text),\n border: resolveToken(slots.border),\n };\n }\n\n return {\n solid: resolveToken(slots.solid),\n solidText: resolveSolidText(resolveToken(slots.solid)),\n bgTint: resolveToken(slots.bgTint),\n iconFg: resolveToken(slots.iconFg),\n iconBg: resolveToken(slots.iconBg),\n text: resolveToken(slots.text),\n border: resolveToken(slots.border),\n };\n }, [theme.severity, severity, resolveToken, colorMode]);\n}\n\n/**\n * Returns the full resolved map for all severities.\n */\nexport function useAllSeverityColors(): Record<Severity, ResolvedSeverityColors> {\n const {theme, resolveToken, colorMode} = useKitsTheme();\n const interactive = useSeverityColors('primary-interactive');\n\n return useMemo(() => {\n const severities: Severity[] = ['success', 'info', 'warning', 'danger', 'help', 'secondary', 'brand'];\n const result = {} as Record<Severity, ResolvedSeverityColors>;\n result['primary-interactive'] = interactive;\n const primary = resolveToken('primary');\n\n for (const key of severities) {\n const slots = theme.severity?.[key];\n if (!slots) continue;\n\n const isBrand = key === 'brand';\n const solidText = slots.solidText ? resolveToken(slots.solidText) : WHITE;\n\n if (colorMode === 'dark') {\n // Same shade-guard as useSeverityColors — four of the seven\n // default severities carry a raw hex, which cannot be swapped.\n const swapShade = (token: string, target: string, fallbackSlot: string) =>\n /\\.\\d+$/.test(token)\n ? resolveToken(token.replace(/\\.\\d+$/, `.${target}`))\n : resolveToken(fallbackSlot);\n result[key] = {\n solid: isBrand ? primary : resolveToken(slots.solid),\n solidText,\n bgTint: swapShade(slots.solid, '900', slots.bgTint),\n iconFg: isBrand ? primary : swapShade(slots.solid, '300', slots.iconFg),\n iconBg: swapShade(slots.solid, '800', slots.iconBg),\n text: swapShade(slots.solid, '200', slots.text),\n border: isBrand ? primary : resolveToken(slots.border),\n };\n } else {\n result[key] = {\n solid: isBrand ? primary : resolveToken(slots.solid),\n solidText,\n bgTint: resolveToken(slots.bgTint),\n iconFg: isBrand ? primary : resolveToken(slots.iconFg),\n iconBg: resolveToken(slots.iconBg),\n text: resolveToken(slots.text),\n border: isBrand ? primary : resolveToken(slots.border),\n };\n }\n }\n\n return result;\n }, [theme.severity, resolveToken, colorMode, interactive]);\n}\n"],"names":["useKitsTheme","useMemo"],"mappings":"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"}
@@ -5,6 +5,7 @@ const WHITE = "#ffffff";
5
5
  function useSeverityColors(severity) {
6
6
  const { theme, resolveToken, colorMode } = useKitsTheme();
7
7
  return useMemo(() => {
8
+ const slots = theme.severity?.[severity] ?? theme.severity?.secondary;
8
9
  const relativeLuminance = (css) => {
9
10
  const m = /^#?([0-9a-f]{6})$/i.exec(css.trim());
10
11
  if (!m) return null;
@@ -19,7 +20,7 @@ function useSeverityColors(severity) {
19
20
  return l === null ? null : 1.05 / (l + 0.05);
20
21
  };
21
22
  const resolveSolidText = (fill) => {
22
- if (slots.solidText) return resolveToken(slots.solidText);
23
+ if (slots?.solidText) return resolveToken(slots.solidText);
23
24
  const ratio = fill ? whiteOn(fill) : null;
24
25
  if (ratio !== null && ratio < 4.5) {
25
26
  const ink = resolveToken("text-inverse");
@@ -50,7 +51,6 @@ function useSeverityColors(severity) {
50
51
  border: fill
51
52
  };
52
53
  }
53
- const slots = theme.severity?.[severity] ?? theme.severity?.secondary;
54
54
  if (!slots) {
55
55
  const isDark = colorMode === "dark";
56
56
  const pick = (token, backstop) => {
@@ -119,9 +119,11 @@ function useSeverityColors(severity) {
119
119
  }
120
120
  function useAllSeverityColors() {
121
121
  const { theme, resolveToken, colorMode } = useKitsTheme();
122
+ const interactive = useSeverityColors("primary-interactive");
122
123
  return useMemo(() => {
123
124
  const severities = ["success", "info", "warning", "danger", "help", "secondary", "brand"];
124
125
  const result = {};
126
+ result["primary-interactive"] = interactive;
125
127
  const primary = resolveToken("primary");
126
128
  for (const key of severities) {
127
129
  const slots = theme.severity?.[key];
@@ -152,7 +154,7 @@ function useAllSeverityColors() {
152
154
  }
153
155
  }
154
156
  return result;
155
- }, [theme.severity, resolveToken, colorMode]);
157
+ }, [theme.severity, resolveToken, colorMode, interactive]);
156
158
  }
157
159
 
158
160
  export { useAllSeverityColors, useSeverityColors };
@@ -1 +1 @@
1
- {"version":3,"file":"useSeverityColors.js","sources":["../../../src/hooks/useSeverityColors.ts"],"sourcesContent":["import {useMemo} from 'react';\nimport {useKitsTheme} from '../contexts/Theme';\nimport type {Severity, ISeverityColorSlots} from '@lmb-it/kitsconcerto-types';\n\nexport interface ResolvedSeverityColors {\n solid: string;\n solidText: string;\n bgTint: string;\n iconFg: string;\n iconBg: string;\n text: string;\n border: string;\n}\n\nconst WHITE = '#ffffff';\n\n/**\n * Resolves all severity color slots for a given severity using the current theme.\n * All slot values go through resolveToken() — so they support:\n * - Raw values: '#ff0000', 'black', 'rgb(..)'\n * - Semantic tokens: 'primary', 'danger'\n * - Dot-notation: 'red.700', 'blue.500'\n * - Prefixed tokens: '$spacing.4'\n *\n * solidText defaults to white if not explicitly set by the consumer.\n */\n/**\n * ⛔ SHADE-SWAPPING ONLY WORKS ON A SHADED TOKEN, AND FOUR OF THIS LIBRARY'S\n * SEVEN SEVERITIES DO NOT HAVE ONE.\n *\n * Dark mode derives its tint and its text from the severity's `solid` value by\n * replacing the shade — `amber.500` → `amber.900` for the background and\n * `amber.200` for the text. That is only meaningful when `solid` LOOKS like\n * `name.NNN`. In `defaultTheme.ts` it often does not:\n *\n * brand brand.500 shaded → swap works\n * success green.500 shaded → swap works\n * secondary gray.500 shaded → swap works\n * danger #E3595B RAW HEX → swap is a no-op\n * warning #F59E0B RAW HEX → swap is a no-op\n * info #4F5BD5 RAW HEX → swap is a no-op\n * help #4F5BD5 RAW HEX → swap is a no-op\n *\n * ⛔ AND A NO-OP HERE IS NOT \"NO CHANGE\" — IT IS A COLLISION. The regex matches\n * nothing, so BOTH the background and the text resolve to the SAME raw hex, and\n * the text becomes invisible on its own background. Measured on the device\n * (AlphaMatch, Me tab, warning banner, dark): the action row was 6,923 sampled\n * pixels of #F59E0B with no text ink in it at all — the label and its reason\n * had vanished while the banner still looked like a banner.\n *\n * ⚠️ IT HITS THE FOUR SEVERITIES PEOPLE SEE MOST — errors, warnings, info and\n * help — and this hook is SHARED, not native-only, so web surfaces have it too.\n *\n * 💡 THE FALLBACK IS THE DESIGNED LIGHT PAIR. When there is no shade to swap,\n * the light slots are used instead: they are a pair someone chose to be legible\n * together, which is the one thing that must be true here. A dark-mode tint\n * would look better and is a palette decision, not a correctness one.\n */\nexport function useSeverityColors(severity: Severity): ResolvedSeverityColors {\n const {theme, resolveToken, colorMode} = useKitsTheme();\n\n return useMemo(() => {\n /*\n * ⛔ THE HELPERS AND THE `primary-interactive` BRANCH SIT ABOVE THE\n * NO-SLOTS RETURN ON PURPOSE. They used to sit below it, which meant a\n * product shipping no `theme.severity` at all never reached them and got\n * a grey secondary button while explicitly asking for an interactive\n * one. The helpers are pure definitions, so hoisting them changes\n * nothing for any other severity.\n */\n /*\n * ⛔ WHITE IS NOT A SAFE DEFAULT ON A FILL THAT FOLLOWS THE THEME —\n * R49, 25 October 2026, and this is the same fault as the no-slots\n * fallback above, one branch along.\n *\n * `solidText` defaulted to white unconditionally while `solid` for\n * the brand severity follows the `primary` token, which moves with\n * the colour mode. In the AlphaMatch Portal `primary` is #20AAB0 in\n * light and **#26C6DA in dark**, so a filled brand button measured\n * **2.82:1** and then **2.06:1** — the worst reading in the product.\n * Found on `/verification`: \"Verify on phone\", white on bright teal.\n *\n * ⛔ IT PICKS BY LUMINANCE RATHER THAN BY MODE, deliberately. Keying\n * on `colorMode === 'dark'` would be wrong for any product whose\n * primary is DARK in dark mode, and it was a guess about palettes\n * this library does not own. Relative luminance asks the only\n * question that matters — is white readable on this exact colour —\n * and answers it for a palette nobody here has seen.\n *\n * ⚠️ A CONSUMER'S OWN `solidText` STILL WINS. This changes the\n * DEFAULT only; a product that has stated what the ink should be is\n * not second-guessed.\n */\n const relativeLuminance = (css: string): number | null => {\n const m = /^#?([0-9a-f]{6})$/i.exec(css.trim());\n if (!m) return null;\n const channel = (v: number) =>\n v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);\n const [r, g, b] = [0, 2, 4].map(i =>\n channel(parseInt(m[1].substr(i, 2), 16) / 255),\n );\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n };\n /** White on `fill`, as a WCAG contrast ratio. */\n const whiteOn = (fill: string): number | null => {\n const l = relativeLuminance(fill);\n return l === null ? null : 1.05 / (l + 0.05);\n };\n const resolveSolidText = (fill?: string) => {\n if (slots.solidText) return resolveToken(slots.solidText);\n // ⚠️ UNKNOWN SHAPES KEEP THE OLD BEHAVIOUR. A gradient, a CSS\n // variable or a named colour returns null here, and white stays —\n // this must not start guessing at values it cannot read.\n const ratio = fill ? whiteOn(fill) : null;\n if (ratio !== null && ratio < 4.5) {\n const ink = resolveToken('text-inverse');\n if (ink && ink !== 'text-inverse') return ink;\n const surface = resolveToken('surface');\n if (surface && surface !== 'surface') return surface;\n return resolveToken('gray.900');\n }\n return WHITE;\n };\n\n /*\n * ⛔ `primary-interactive` RESOLVES FROM TOKENS, NOT FROM `theme.severity`,\n * for the same reason `brand` does below: the colour belongs to the\n * consuming product's palette, and this library ships no default for it.\n *\n * ⛔ IT IS PLACED BEFORE THE SLOTS LOOKUP DELIBERATELY. A product that has\n * not defined `theme.severity['primary-interactive']` — which is every\n * product today — would otherwise fall into the no-slots fallback and get\n * a grey secondary button while asking for an interactive one.\n *\n * ⛔ AND THE INK IS ITS OWN TOKEN, WHICH IS THE WHOLE POINT. `solidText`\n * here is `on-primary-interactive`, not white and not a luminance guess.\n * The fill is designed to flip between modes (AlphaMatch: #157C82 light,\n * #4DD0E1 dark) and white on the dark value measures 1.84:1 — the exact\n * failure R49 fixed for `brand`. A pair that was chosen together cannot\n * be re-derived here as reliably as it can be stated.\n *\n * ⚠️ A PRODUCT WITHOUT THE TOKENS DEGRADES TO `primary` RATHER THAN TO A\n * TOKEN NAME. `resolveToken` returns its argument unchanged when it knows\n * nothing, so the guard below treats that as absent. The Portal is\n * therefore unaffected unless it both defines the tokens and asks for\n * this severity.\n */\n if (severity === 'primary-interactive') {\n const known = (token: string): string | null => {\n const value = resolveToken(token);\n return value && value !== token ? value : null;\n };\n const fill = known('primary-interactive') ?? resolveToken('primary');\n const ink = known('on-primary-interactive') ?? resolveSolidText(fill);\n return {\n solid: fill,\n solidText: ink,\n /* The soft slots have no token of their own; the brand's tint is\n the nearest thing a product has already chosen, and it is only\n used by outlined/soft treatments, never under `ink`. */\n bgTint: known('primary-subtle') ?? resolveToken('surface-muted'),\n iconFg: fill,\n iconBg: known('primary-subtle') ?? resolveToken('surface-muted'),\n text: fill,\n border: fill,\n };\n }\n\n const slots: ISeverityColorSlots = theme.severity?.[severity] ?? theme.severity?.secondary;\n if (!slots) {\n /*\n * ⛔ THE FALLBACK MUST FOLLOW THE COLOUR MODE, AND `gray.500` DID\n * NOT — R49, 25 October 2026. This branch is not an edge case: a\n * product that ships `severity: {}` lands here for every severity\n * except `brand`, which has its own dynamic path above. The\n * AlphaMatch Client Portal does exactly that, so EVERY outlined\n * secondary control in it was inked `#6B7280`.\n *\n * A scale step is a fixed point on a ramp; it is the same colour\n * in both palettes, while everything around it moves. Measured in\n * the Portal:\n *\n * outlined label, light page #F5F7F6 **4.49:1** (already failing)\n * outlined label, dark page #111827 **3.67:1**\n * outlined label, dark card #1F2937 **3.04:1**\n *\n * ⚠️ THIS IS THE SECOND TIME THE SAME MISTAKE HAS BEEN FOUND HERE.\n * `Tabs` defaulted `inactiveBackground` to `gray.100` while\n * labelling it with a token that flips, and four chips measured\n * 1.34:1 on a dark page; that was fixed at source in 0.1.290. The\n * rule it produced — a default that pairs a scale value with a\n * semantic one will be wrong in one mode — was not carried across\n * to this function.\n *\n * ⛔ AND `solid` DOES TWO JOBS, WHICH IS WHY IT NEEDS A PAIR.\n * `Button` uses it as the FILL when solid and as the INK when\n * outlined; `Chip` does the same. A value dark enough for white\n * text is unreadable as ink on a dark page, and vice versa. So\n * the ink follows the mode and `solidText` is the surface behind\n * it, which moves the opposite way:\n *\n * filled light #FFFFFF on #546067 6.47:1\n * filled dark #1F2937 on #D1D5DB 9.96:1\n * outlined light #546067 on #F5F7F6 6.01:1\n * outlined dark #D1D5DB on #111827 12.04:1\n *\n * ⚠️ AND IT DEGRADES RATHER THAN BREAKING. This library ships no\n * default semantic tokens — consumers supply them — so a product\n * without `text-secondary` or `surface` would get the token name\n * back instead of a colour. `pick()` keeps a mode-aware scale\n * step behind each one: still better than today, because the\n * backstop flips even though a scale value normally cannot.\n */\n const isDark = colorMode === 'dark';\n const pick = (token: string, backstop: string) => {\n const value = resolveToken(token);\n return !value || value === token ? resolveToken(backstop) : value;\n };\n const ink = pick('text-secondary', isDark ? 'gray.300' : 'gray.600');\n const ground = pick('surface', isDark ? 'gray.800' : 'gray.50');\n const tint = pick('surface-muted', isDark ? 'gray.700' : 'gray.100');\n return {\n solid: ink,\n solidText: ground,\n bgTint: tint,\n iconFg: ink,\n iconBg: tint,\n text: ink,\n border: ink,\n };\n }\n\n\n // Brand severity: solid/border/iconFg dynamically follow the primary token\n if (severity === 'brand') {\n const primary = resolveToken('primary');\n if (colorMode === 'dark') {\n // Same shade-guard as below: a `solid` with no shade cannot be\n // swapped, and collapsing it would put the text on its own colour.\n const swapShade = (token: string, target: string, fallbackSlot: string) =>\n /\\.\\d+$/.test(token)\n ? resolveToken(token.replace(/\\.\\d+$/, `.${target}`))\n : resolveToken(fallbackSlot);\n return {\n solid: primary,\n solidText: resolveSolidText(primary),\n bgTint: swapShade(slots.solid, '900', slots.bgTint),\n iconFg: primary,\n iconBg: swapShade(slots.solid, '800', slots.iconBg),\n text: swapShade(slots.solid, '200', slots.text),\n border: primary,\n };\n }\n return {\n solid: primary,\n solidText: resolveSolidText(primary),\n bgTint: resolveToken(slots.bgTint),\n iconFg: primary,\n iconBg: resolveToken(slots.iconBg),\n text: resolveToken(slots.text),\n border: primary,\n };\n }\n\n if (colorMode === 'dark') {\n // `fallbackSlot` is used when `token` carries no shade to swap —\n // see the note above this function.\n const swapShade = (token: string, target: string, fallbackSlot: string) =>\n /\\.\\d+$/.test(token)\n ? resolveToken(token.replace(/\\.\\d+$/, `.${target}`))\n : resolveToken(fallbackSlot);\n return {\n solid: resolveToken(slots.solid),\n solidText: resolveSolidText(resolveToken(slots.solid)),\n bgTint: swapShade(slots.solid, '900', slots.bgTint),\n iconFg: swapShade(slots.solid, '300', slots.iconFg),\n iconBg: swapShade(slots.solid, '800', slots.iconBg),\n text: swapShade(slots.solid, '200', slots.text),\n border: resolveToken(slots.border),\n };\n }\n\n return {\n solid: resolveToken(slots.solid),\n solidText: resolveSolidText(resolveToken(slots.solid)),\n bgTint: resolveToken(slots.bgTint),\n iconFg: resolveToken(slots.iconFg),\n iconBg: resolveToken(slots.iconBg),\n text: resolveToken(slots.text),\n border: resolveToken(slots.border),\n };\n }, [theme.severity, severity, resolveToken, colorMode]);\n}\n\n/**\n * Returns the full resolved map for all severities.\n */\nexport function useAllSeverityColors(): Record<Severity, ResolvedSeverityColors> {\n const {theme, resolveToken, colorMode} = useKitsTheme();\n\n return useMemo(() => {\n const severities: Severity[] = ['success', 'info', 'warning', 'danger', 'help', 'secondary', 'brand'];\n const result = {} as Record<Severity, ResolvedSeverityColors>;\n const primary = resolveToken('primary');\n\n for (const key of severities) {\n const slots = theme.severity?.[key];\n if (!slots) continue;\n\n const isBrand = key === 'brand';\n const solidText = slots.solidText ? resolveToken(slots.solidText) : WHITE;\n\n if (colorMode === 'dark') {\n // Same shade-guard as useSeverityColors — four of the seven\n // default severities carry a raw hex, which cannot be swapped.\n const swapShade = (token: string, target: string, fallbackSlot: string) =>\n /\\.\\d+$/.test(token)\n ? resolveToken(token.replace(/\\.\\d+$/, `.${target}`))\n : resolveToken(fallbackSlot);\n result[key] = {\n solid: isBrand ? primary : resolveToken(slots.solid),\n solidText,\n bgTint: swapShade(slots.solid, '900', slots.bgTint),\n iconFg: isBrand ? primary : swapShade(slots.solid, '300', slots.iconFg),\n iconBg: swapShade(slots.solid, '800', slots.iconBg),\n text: swapShade(slots.solid, '200', slots.text),\n border: isBrand ? primary : resolveToken(slots.border),\n };\n } else {\n result[key] = {\n solid: isBrand ? primary : resolveToken(slots.solid),\n solidText,\n bgTint: resolveToken(slots.bgTint),\n iconFg: isBrand ? primary : resolveToken(slots.iconFg),\n iconBg: resolveToken(slots.iconBg),\n text: resolveToken(slots.text),\n border: isBrand ? primary : resolveToken(slots.border),\n };\n }\n }\n\n return result;\n }, [theme.severity, resolveToken, colorMode]);\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useSeverityColors.js","sources":["../../../src/hooks/useSeverityColors.ts"],"sourcesContent":["import {useMemo} from 'react';\nimport {useKitsTheme} from '../contexts/Theme';\nimport type {Severity, ISeverityColorSlots} from '@lmb-it/kitsconcerto-types';\n\nexport interface ResolvedSeverityColors {\n solid: string;\n solidText: string;\n bgTint: string;\n iconFg: string;\n iconBg: string;\n text: string;\n border: string;\n}\n\nconst WHITE = '#ffffff';\n\n/**\n * Resolves all severity color slots for a given severity using the current theme.\n * All slot values go through resolveToken() — so they support:\n * - Raw values: '#ff0000', 'black', 'rgb(..)'\n * - Semantic tokens: 'primary', 'danger'\n * - Dot-notation: 'red.700', 'blue.500'\n * - Prefixed tokens: '$spacing.4'\n *\n * solidText defaults to white if not explicitly set by the consumer.\n */\n/**\n * ⛔ SHADE-SWAPPING ONLY WORKS ON A SHADED TOKEN, AND FOUR OF THIS LIBRARY'S\n * SEVEN SEVERITIES DO NOT HAVE ONE.\n *\n * Dark mode derives its tint and its text from the severity's `solid` value by\n * replacing the shade — `amber.500` → `amber.900` for the background and\n * `amber.200` for the text. That is only meaningful when `solid` LOOKS like\n * `name.NNN`. In `defaultTheme.ts` it often does not:\n *\n * brand brand.500 shaded → swap works\n * success green.500 shaded → swap works\n * secondary gray.500 shaded → swap works\n * danger #E3595B RAW HEX → swap is a no-op\n * warning #F59E0B RAW HEX → swap is a no-op\n * info #4F5BD5 RAW HEX → swap is a no-op\n * help #4F5BD5 RAW HEX → swap is a no-op\n *\n * ⛔ AND A NO-OP HERE IS NOT \"NO CHANGE\" — IT IS A COLLISION. The regex matches\n * nothing, so BOTH the background and the text resolve to the SAME raw hex, and\n * the text becomes invisible on its own background. Measured on the device\n * (AlphaMatch, Me tab, warning banner, dark): the action row was 6,923 sampled\n * pixels of #F59E0B with no text ink in it at all — the label and its reason\n * had vanished while the banner still looked like a banner.\n *\n * ⚠️ IT HITS THE FOUR SEVERITIES PEOPLE SEE MOST — errors, warnings, info and\n * help — and this hook is SHARED, not native-only, so web surfaces have it too.\n *\n * 💡 THE FALLBACK IS THE DESIGNED LIGHT PAIR. When there is no shade to swap,\n * the light slots are used instead: they are a pair someone chose to be legible\n * together, which is the one thing that must be true here. A dark-mode tint\n * would look better and is a palette decision, not a correctness one.\n */\nexport function useSeverityColors(severity: Severity): ResolvedSeverityColors {\n const {theme, resolveToken, colorMode} = useKitsTheme();\n\n return useMemo(() => {\n const slots: ISeverityColorSlots | undefined = theme.severity?.[severity] ?? theme.severity?.secondary;\n /*\n * ⛔ THE HELPERS AND THE `primary-interactive` BRANCH SIT ABOVE THE\n * NO-SLOTS RETURN ON PURPOSE. They used to sit below it, which meant a\n * product shipping no `theme.severity` at all never reached them and got\n * a grey secondary button while explicitly asking for an interactive\n * one. The helpers are pure definitions, so hoisting them changes\n * nothing for any other severity.\n */\n /*\n * ⛔ WHITE IS NOT A SAFE DEFAULT ON A FILL THAT FOLLOWS THE THEME —\n * R49, 25 October 2026, and this is the same fault as the no-slots\n * fallback above, one branch along.\n *\n * `solidText` defaulted to white unconditionally while `solid` for\n * the brand severity follows the `primary` token, which moves with\n * the colour mode. In the AlphaMatch Portal `primary` is #20AAB0 in\n * light and **#26C6DA in dark**, so a filled brand button measured\n * **2.82:1** and then **2.06:1** — the worst reading in the product.\n * Found on `/verification`: \"Verify on phone\", white on bright teal.\n *\n * ⛔ IT PICKS BY LUMINANCE RATHER THAN BY MODE, deliberately. Keying\n * on `colorMode === 'dark'` would be wrong for any product whose\n * primary is DARK in dark mode, and it was a guess about palettes\n * this library does not own. Relative luminance asks the only\n * question that matters — is white readable on this exact colour —\n * and answers it for a palette nobody here has seen.\n *\n * ⚠️ A CONSUMER'S OWN `solidText` STILL WINS. This changes the\n * DEFAULT only; a product that has stated what the ink should be is\n * not second-guessed.\n */\n const relativeLuminance = (css: string): number | null => {\n const m = /^#?([0-9a-f]{6})$/i.exec(css.trim());\n if (!m) return null;\n const channel = (v: number) =>\n v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);\n const [r, g, b] = [0, 2, 4].map(i =>\n channel(parseInt(m[1].substr(i, 2), 16) / 255),\n );\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n };\n /** White on `fill`, as a WCAG contrast ratio. */\n const whiteOn = (fill: string): number | null => {\n const l = relativeLuminance(fill);\n return l === null ? null : 1.05 / (l + 0.05);\n };\n const resolveSolidText = (fill?: string) => {\n if (slots?.solidText) return resolveToken(slots.solidText);\n // ⚠️ UNKNOWN SHAPES KEEP THE OLD BEHAVIOUR. A gradient, a CSS\n // variable or a named colour returns null here, and white stays —\n // this must not start guessing at values it cannot read.\n const ratio = fill ? whiteOn(fill) : null;\n if (ratio !== null && ratio < 4.5) {\n const ink = resolveToken('text-inverse');\n if (ink && ink !== 'text-inverse') return ink;\n const surface = resolveToken('surface');\n if (surface && surface !== 'surface') return surface;\n return resolveToken('gray.900');\n }\n return WHITE;\n };\n\n /*\n * ⛔ `primary-interactive` RESOLVES FROM TOKENS, NOT FROM `theme.severity`,\n * for the same reason `brand` does below: the colour belongs to the\n * consuming product's palette, and this library ships no default for it.\n *\n * ⛔ IT IS PLACED BEFORE THE SLOTS LOOKUP DELIBERATELY. A product that has\n * not defined `theme.severity['primary-interactive']` — which is every\n * product today — would otherwise fall into the no-slots fallback and get\n * a grey secondary button while asking for an interactive one.\n *\n * ⛔ AND THE INK IS ITS OWN TOKEN, WHICH IS THE WHOLE POINT. `solidText`\n * here is `on-primary-interactive`, not white and not a luminance guess.\n * The fill is designed to flip between modes (AlphaMatch: #157C82 light,\n * #4DD0E1 dark) and white on the dark value measures 1.84:1 — the exact\n * failure R49 fixed for `brand`. A pair that was chosen together cannot\n * be re-derived here as reliably as it can be stated.\n *\n * ⚠️ A PRODUCT WITHOUT THE TOKENS DEGRADES TO `primary` RATHER THAN TO A\n * TOKEN NAME. `resolveToken` returns its argument unchanged when it knows\n * nothing, so the guard below treats that as absent. The Portal is\n * therefore unaffected unless it both defines the tokens and asks for\n * this severity.\n */\n if (severity === 'primary-interactive') {\n const known = (token: string): string | null => {\n const value = resolveToken(token);\n return value && value !== token ? value : null;\n };\n const fill = known('primary-interactive') ?? resolveToken('primary');\n const ink = known('on-primary-interactive') ?? resolveSolidText(fill);\n return {\n solid: fill,\n solidText: ink,\n /* The soft slots have no token of their own; the brand's tint is\n the nearest thing a product has already chosen, and it is only\n used by outlined/soft treatments, never under `ink`. */\n bgTint: known('primary-subtle') ?? resolveToken('surface-muted'),\n iconFg: fill,\n iconBg: known('primary-subtle') ?? resolveToken('surface-muted'),\n text: fill,\n border: fill,\n };\n }\n\n if (!slots) {\n /*\n * ⛔ THE FALLBACK MUST FOLLOW THE COLOUR MODE, AND `gray.500` DID\n * NOT — R49, 25 October 2026. This branch is not an edge case: a\n * product that ships `severity: {}` lands here for every severity\n * except `brand`, which has its own dynamic path above. The\n * AlphaMatch Client Portal does exactly that, so EVERY outlined\n * secondary control in it was inked `#6B7280`.\n *\n * A scale step is a fixed point on a ramp; it is the same colour\n * in both palettes, while everything around it moves. Measured in\n * the Portal:\n *\n * outlined label, light page #F5F7F6 **4.49:1** (already failing)\n * outlined label, dark page #111827 **3.67:1**\n * outlined label, dark card #1F2937 **3.04:1**\n *\n * ⚠️ THIS IS THE SECOND TIME THE SAME MISTAKE HAS BEEN FOUND HERE.\n * `Tabs` defaulted `inactiveBackground` to `gray.100` while\n * labelling it with a token that flips, and four chips measured\n * 1.34:1 on a dark page; that was fixed at source in 0.1.290. The\n * rule it produced — a default that pairs a scale value with a\n * semantic one will be wrong in one mode — was not carried across\n * to this function.\n *\n * ⛔ AND `solid` DOES TWO JOBS, WHICH IS WHY IT NEEDS A PAIR.\n * `Button` uses it as the FILL when solid and as the INK when\n * outlined; `Chip` does the same. A value dark enough for white\n * text is unreadable as ink on a dark page, and vice versa. So\n * the ink follows the mode and `solidText` is the surface behind\n * it, which moves the opposite way:\n *\n * filled light #FFFFFF on #546067 6.47:1\n * filled dark #1F2937 on #D1D5DB 9.96:1\n * outlined light #546067 on #F5F7F6 6.01:1\n * outlined dark #D1D5DB on #111827 12.04:1\n *\n * ⚠️ AND IT DEGRADES RATHER THAN BREAKING. This library ships no\n * default semantic tokens — consumers supply them — so a product\n * without `text-secondary` or `surface` would get the token name\n * back instead of a colour. `pick()` keeps a mode-aware scale\n * step behind each one: still better than today, because the\n * backstop flips even though a scale value normally cannot.\n */\n const isDark = colorMode === 'dark';\n const pick = (token: string, backstop: string) => {\n const value = resolveToken(token);\n return !value || value === token ? resolveToken(backstop) : value;\n };\n const ink = pick('text-secondary', isDark ? 'gray.300' : 'gray.600');\n const ground = pick('surface', isDark ? 'gray.800' : 'gray.50');\n const tint = pick('surface-muted', isDark ? 'gray.700' : 'gray.100');\n return {\n solid: ink,\n solidText: ground,\n bgTint: tint,\n iconFg: ink,\n iconBg: tint,\n text: ink,\n border: ink,\n };\n }\n\n\n // Brand severity: solid/border/iconFg dynamically follow the primary token\n if (severity === 'brand') {\n const primary = resolveToken('primary');\n if (colorMode === 'dark') {\n // Same shade-guard as below: a `solid` with no shade cannot be\n // swapped, and collapsing it would put the text on its own colour.\n const swapShade = (token: string, target: string, fallbackSlot: string) =>\n /\\.\\d+$/.test(token)\n ? resolveToken(token.replace(/\\.\\d+$/, `.${target}`))\n : resolveToken(fallbackSlot);\n return {\n solid: primary,\n solidText: resolveSolidText(primary),\n bgTint: swapShade(slots.solid, '900', slots.bgTint),\n iconFg: primary,\n iconBg: swapShade(slots.solid, '800', slots.iconBg),\n text: swapShade(slots.solid, '200', slots.text),\n border: primary,\n };\n }\n return {\n solid: primary,\n solidText: resolveSolidText(primary),\n bgTint: resolveToken(slots.bgTint),\n iconFg: primary,\n iconBg: resolveToken(slots.iconBg),\n text: resolveToken(slots.text),\n border: primary,\n };\n }\n\n if (colorMode === 'dark') {\n // `fallbackSlot` is used when `token` carries no shade to swap —\n // see the note above this function.\n const swapShade = (token: string, target: string, fallbackSlot: string) =>\n /\\.\\d+$/.test(token)\n ? resolveToken(token.replace(/\\.\\d+$/, `.${target}`))\n : resolveToken(fallbackSlot);\n return {\n solid: resolveToken(slots.solid),\n solidText: resolveSolidText(resolveToken(slots.solid)),\n bgTint: swapShade(slots.solid, '900', slots.bgTint),\n iconFg: swapShade(slots.solid, '300', slots.iconFg),\n iconBg: swapShade(slots.solid, '800', slots.iconBg),\n text: swapShade(slots.solid, '200', slots.text),\n border: resolveToken(slots.border),\n };\n }\n\n return {\n solid: resolveToken(slots.solid),\n solidText: resolveSolidText(resolveToken(slots.solid)),\n bgTint: resolveToken(slots.bgTint),\n iconFg: resolveToken(slots.iconFg),\n iconBg: resolveToken(slots.iconBg),\n text: resolveToken(slots.text),\n border: resolveToken(slots.border),\n };\n }, [theme.severity, severity, resolveToken, colorMode]);\n}\n\n/**\n * Returns the full resolved map for all severities.\n */\nexport function useAllSeverityColors(): Record<Severity, ResolvedSeverityColors> {\n const {theme, resolveToken, colorMode} = useKitsTheme();\n const interactive = useSeverityColors('primary-interactive');\n\n return useMemo(() => {\n const severities: Severity[] = ['success', 'info', 'warning', 'danger', 'help', 'secondary', 'brand'];\n const result = {} as Record<Severity, ResolvedSeverityColors>;\n result['primary-interactive'] = interactive;\n const primary = resolveToken('primary');\n\n for (const key of severities) {\n const slots = theme.severity?.[key];\n if (!slots) continue;\n\n const isBrand = key === 'brand';\n const solidText = slots.solidText ? resolveToken(slots.solidText) : WHITE;\n\n if (colorMode === 'dark') {\n // Same shade-guard as useSeverityColors — four of the seven\n // default severities carry a raw hex, which cannot be swapped.\n const swapShade = (token: string, target: string, fallbackSlot: string) =>\n /\\.\\d+$/.test(token)\n ? resolveToken(token.replace(/\\.\\d+$/, `.${target}`))\n : resolveToken(fallbackSlot);\n result[key] = {\n solid: isBrand ? primary : resolveToken(slots.solid),\n solidText,\n bgTint: swapShade(slots.solid, '900', slots.bgTint),\n iconFg: isBrand ? primary : swapShade(slots.solid, '300', slots.iconFg),\n iconBg: swapShade(slots.solid, '800', slots.iconBg),\n text: swapShade(slots.solid, '200', slots.text),\n border: isBrand ? primary : resolveToken(slots.border),\n };\n } else {\n result[key] = {\n solid: isBrand ? primary : resolveToken(slots.solid),\n solidText,\n bgTint: resolveToken(slots.bgTint),\n iconFg: isBrand ? primary : resolveToken(slots.iconFg),\n iconBg: resolveToken(slots.iconBg),\n text: resolveToken(slots.text),\n border: isBrand ? primary : resolveToken(slots.border),\n };\n }\n }\n\n return result;\n }, [theme.severity, resolveToken, colorMode, interactive]);\n}\n"],"names":[],"mappings":"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"}
package/index.d.ts CHANGED
@@ -2310,7 +2310,7 @@ export type IUseFormReturn<T extends FieldValues = FieldValues> = UseFormReturn<
2310
2310
  id: string;
2311
2311
  setValues(patch: DeepPartial$1<T>, opts?: SetManyOpts): void;
2312
2312
  mergeReset(patch: DeepPartial$1<T>): void;
2313
- onFormSubmit: () => void;
2313
+ onFormSubmit: () => Promise<void>;
2314
2314
  setIsSubmitting: (isSubmitting: boolean) => void;
2315
2315
  };
2316
2316
  export type IFormComponent<T extends FieldValues> = UseFormManagerEvents<T> & (IFormContextPropsFormData<T> | IFormContextPropsJSON<T>) & {
@@ -9673,7 +9673,7 @@ export interface UseFormManagerReturn<T extends FieldValues> {
9673
9673
  * Our wrapped submit handler that manages the isSubmitting state.
9674
9674
  * This is what the <form> element's onSubmit will call.
9675
9675
  */
9676
- handleSubmit: () => void;
9676
+ handleSubmit: () => Promise<void>;
9677
9677
  setIsSubmitting: Dispatch<SetStateAction<boolean>>;
9678
9678
  }
9679
9679
  declare const useFormManager: <T extends FieldValues>({ elements, onSubmit, onInvalidSubmit, id, onChange, onChangeSingleValue, outputFormat }: IFormComponent<T>, isSubmitting: boolean, setIsSubmitting: Dispatch<SetStateAction<boolean>>) => UseFormManagerReturn<T>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lmb-it/kitsconcerto-native",
3
- "version": "0.1.294",
3
+ "version": "0.1.295",
4
4
  "description": "KitsConcerto — Native (React Native) UI components",
5
5
  "main": "index.cjs",
6
6
  "module": "index.js",