@lmb-it/kitsconcerto-native 0.1.250 → 0.1.252
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/Button/Button.cjs +9 -4
- package/components/Button/Button.cjs.map +1 -1
- package/components/Button/Button.js +9 -4
- package/components/Button/Button.js.map +1 -1
- package/form-renderer/components/layout/FieldWrapper.cjs +1 -1
- package/form-renderer/components/layout/FieldWrapper.cjs.map +1 -1
- package/form-renderer/components/layout/FieldWrapper.js +1 -1
- package/form-renderer/components/layout/FieldWrapper.js.map +1 -1
- package/index.d.ts +26 -1
- package/package.json +13 -2
- package/packages/types/src/Theme/defaultTheme.cjs +3 -0
- package/packages/types/src/Theme/defaultTheme.cjs.map +1 -1
- package/packages/types/src/Theme/defaultTheme.js +3 -0
- package/packages/types/src/Theme/defaultTheme.js.map +1 -1
- package/packages/types/src/Theme/shadows.cjs +12 -2
- package/packages/types/src/Theme/shadows.cjs.map +1 -1
- package/packages/types/src/Theme/shadows.js +12 -2
- package/packages/types/src/Theme/shadows.js.map +1 -1
- package/primitives/Text/index.cjs +5 -6
- package/primitives/Text/index.cjs.map +1 -1
- package/primitives/Text/index.js +5 -6
- package/primitives/Text/index.js.map +1 -1
|
@@ -91,6 +91,7 @@ require('../../contexts/DialogContext.cjs');
|
|
|
91
91
|
var useComponentDefaults = require('../../hooks/useComponentDefaults.cjs');
|
|
92
92
|
var useSeverityColors = require('../../hooks/useSeverityColors.cjs');
|
|
93
93
|
var useThemedFont = require('../../hooks/useThemedFont.cjs');
|
|
94
|
+
var KitsThemeProvider_native = require('../../contexts/Theme/KitsThemeProvider.cjs');
|
|
94
95
|
|
|
95
96
|
const SIZE_MAP = {
|
|
96
97
|
sm: { height: 32, fontSize: 12, px: 10, py: 6, gap: 6, iconSize: 14 },
|
|
@@ -143,9 +144,11 @@ function Button(rawProps) {
|
|
|
143
144
|
const resolvedLabel = typeof children === "string" ? children : loading && isLoadingText ? isLoadingText : label ? t(label) : "";
|
|
144
145
|
const hasLabel = !!resolvedLabel;
|
|
145
146
|
const isIconOnly = !hasLabel && !!icon;
|
|
146
|
-
const
|
|
147
|
-
const
|
|
148
|
-
const
|
|
147
|
+
const { resolveToken } = KitsThemeProvider_native.useKitsTheme();
|
|
148
|
+
const disabledText = resolveToken("text-subtle");
|
|
149
|
+
const textColor = isDisabled ? disabledText : outlined ? sevColors.solid : sevColors.solidText;
|
|
150
|
+
const bgColor = outlined ? "transparent" : isDisabled ? resolveToken("surface-muted") : sevColors.solid;
|
|
151
|
+
const borderColor = isDisabled ? resolveToken("border") : sevColors.border || sevColors.solid;
|
|
149
152
|
const computedCss = React.useMemo(() => ({
|
|
150
153
|
backgroundColor: bgColor,
|
|
151
154
|
borderColor,
|
|
@@ -160,7 +163,9 @@ function Button(rawProps) {
|
|
|
160
163
|
alignItems: "center",
|
|
161
164
|
justifyContent: "center",
|
|
162
165
|
gap: sizeConfig.gap,
|
|
163
|
-
|
|
166
|
+
// ⚠️ NO OPACITY BRANCH. The colours above carry the state now; fading
|
|
167
|
+
// them as well would take the new muted label back below legibility.
|
|
168
|
+
opacity: 100,
|
|
164
169
|
alignSelf: "flex-start",
|
|
165
170
|
overflow: "hidden",
|
|
166
171
|
...raised ? {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.cjs","sources":["../../../../src/components/Button/Button.native.tsx"],"sourcesContent":["import React, {useMemo, useCallback, type ElementType, type ReactElement} from 'react';\nimport {\n ActivityIndicator,\n Text as RNText,\n type TextStyle,\n type GestureResponderEvent,\n} from 'react-native';\nimport type {IButtonProps, IconName, IStyleClasses} from '@lmb-it/kitsconcerto-types';\nimport {ResponsiveElement, useSeparator, style as resolveStyle} from '@lmb-it/kitsconcertomobile';\nimport {IconMap} from '../../assets/Icons';\nimport {useLanguage, useComponentDefaults, useThemedFont} from '../../hooks';\nimport {useSeverityColors} from '../../hooks/useSeverityColors';\n\n/* ─── Size configuration (matches web) ────────────────────────── */\nconst SIZE_MAP = {\n sm: {height: 32, fontSize: 12, px: 10, py: 6, gap: 6, iconSize: 14},\n md: {height: 40, fontSize: 14, px: 16, py: 10, gap: 8, iconSize: 16},\n lg: {height: 48, fontSize: 16, px: 24, py: 12, gap: 10, iconSize: 18},\n} as const;\n\n/* ─── Icon resolver ──────────────────────────────────────────── */\nfunction resolveIcon(\n icon: unknown,\n color: string,\n iconSize: number,\n): React.ReactNode {\n if (!icon) return null;\n if (React.isValidElement(icon)) {\n return React.cloneElement(icon as ReactElement<any>, {color, size: iconSize});\n }\n if (typeof icon === 'function') {\n const Comp = icon as ElementType;\n return <Comp size={iconSize} color={color} />;\n }\n if (typeof icon !== 'string') return null;\n const piMatches = icon.match(/pi-([a-z-]+)/g);\n const iconName = piMatches\n ? (piMatches[piMatches.length - 1]!.replace('pi-', '') as IconName)\n : (icon as IconName);\n const Component = IconMap[iconName];\n if (Component) return <Component size={iconSize} color={color} />;\n return null;\n}\n\n/* ─── Button Component ───────────────────────────────────────── */\nexport function Button(rawProps: IButtonProps<IconName | string | ElementType | ReactElement>) {\n const {mergedProps: props, themeStyle, elementStyles} = useComponentDefaults('Button', rawProps);\n const {\n children,\n label,\n isLoadingText,\n severity = 'brand',\n size = 'md',\n loading,\n iconPos = 'left',\n icon,\n disabled,\n outlined,\n raised,\n rounded,\n testID,\n style: styleProp,\n onPress,\n onClick,\n ...rest\n } = props;\n\n const {t} = useLanguage();\n const labelFont = useThemedFont('body');\n const {cssProps, nativeProps} = useSeparator<Record<any, any>>(rest);\n const sevColors = useSeverityColors((severity as any) ?? 'brand');\n\n const sizeConfig = SIZE_MAP[size as keyof typeof SIZE_MAP] ?? SIZE_MAP.md;\n const isDisabled = disabled || loading;\n\n /* ── Resolve label text ── */\n const resolvedLabel = typeof children === 'string'\n ? children\n : loading && isLoadingText\n ? isLoadingText\n : label ? t(label) : '';\n\n const hasLabel = !!resolvedLabel;\n const isIconOnly = !hasLabel && !!icon;\n\n /* ── Severity colors ── */\n const textColor = outlined ? sevColors.solid : sevColors.solidText;\n const bgColor = outlined ? 'transparent' : sevColors.solid;\n const borderColor = sevColors.border || sevColors.solid;\n\n /* ── Build CSS props for ResponsiveElement ── */\n const computedCss:IStyleClasses = useMemo(() => ({\n backgroundColor: bgColor,\n borderColor,\n borderWidth: 1,\n height: sizeConfig.height,\n paddingLeft: hasLabel ? sizeConfig.px : sizeConfig.py,\n paddingRight: hasLabel ? sizeConfig.px : sizeConfig.py,\n paddingTop: sizeConfig.py,\n paddingBottom: sizeConfig.py,\n borderRadius: rounded ? 9999 : 6,\n flexDirection: iconPos === 'top' || iconPos === 'bottom'\n ? (iconPos === 'bottom' ? 'column-reverse' : 'column')\n : (iconPos === 'right' ? 'row-reverse' : 'row'),\n alignItems: 'center',\n justifyContent: 'center',\n gap: sizeConfig.gap,\n opacity: isDisabled ? 50 : 100,\n alignSelf: 'flex-start',\n overflow: 'hidden',\n ...(raised ? {\n shadowColor: '#000',\n shadowOffset: {width: 0, height: 3},\n shadowOpacity: 0.16,\n shadowRadius: 6,\n elevation: 4,\n } : {}),\n // Theme overrides the defaults above: shared `root` first, then the\n // `text` slot (label buttons) or the `iconOnly` slot. Icon-only buttons\n // are forced square so a `root`/`text` height can never stretch them —\n // the `iconOnly` slot can still override the square if needed.\n ...themeStyle,\n ...(elementStyles.root || {}),\n ...(isIconOnly\n ? {\n width: sizeConfig.height,\n height: sizeConfig.height,\n minWidth: sizeConfig.height,\n ...(elementStyles.iconOnly || {}),\n }\n : (elementStyles.text || {})),\n ...cssProps,\n ...styleProp,\n }), [themeStyle, elementStyles, cssProps, bgColor, borderColor, sizeConfig,\n hasLabel, icon, iconPos, rounded, raised, isDisabled, styleProp, isIconOnly]);\n\n /* ── Icon element ── */\n const iconElement = loading\n ? <ActivityIndicator size=\"small\" color={textColor} />\n : resolveIcon(icon, textColor, sizeConfig.iconSize);\n\n /* ── Text style ── */\n const labelStyle: TextStyle = useMemo(() => ({\n color: textColor,\n fontSize: sizeConfig.fontSize,\n fontWeight: '600',\n lineHeight: sizeConfig.fontSize * 1.2,\n ...(labelFont ? {fontFamily: labelFont} : {}),\n // Theme/per-instance label slot wins — e.g. theme.components.Button.label.style\n // = { lineHeight: 22 } to stop Arabic (NotoKufiArabic) glyphs clipping.\n ...((elementStyles.label as TextStyle) || {}),\n }), [textColor, sizeConfig, labelFont, elementStyles.label]);\n\n /* ── Press handler ── */\n const handlePress = useCallback((e: GestureResponderEvent) => {\n if (isDisabled) return;\n if (onPress) onPress(e as any);\n if (onClick) onClick(e as any);\n }, [isDisabled, onPress, onClick]);\n\n return (\n <ResponsiveElement\n as=\"Pressable\"\n cssProps={computedCss}\n nativeProps={{\n ...nativeProps,\n onPress: handlePress,\n disabled: isDisabled,\n testID,\n accessibilityRole: 'button',\n accessibilityState: {disabled: isDisabled, busy: loading},\n accessibilityLabel: resolvedLabel || undefined,\n }}\n >\n {iconElement}\n {!!hasLabel && (\n <RNText style={labelStyle} numberOfLines={1}>\n {resolvedLabel}\n </RNText>\n )}\n {typeof children !== 'string' ? children : undefined}\n </ResponsiveElement>\n );\n}\n\nexport default Button;\n"],"names":["jsx","IconMap","useComponentDefaults","useLanguage","useThemedFont","useSeparator","useSeverityColors","useMemo","ActivityIndicator","useCallback","jsxs","ResponsiveElement","RNText"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Button.cjs","sources":["../../../../src/components/Button/Button.native.tsx"],"sourcesContent":["import React, {useMemo, useCallback, type ElementType, type ReactElement} from 'react';\nimport {\n ActivityIndicator,\n Text as RNText,\n type TextStyle,\n type GestureResponderEvent,\n} from 'react-native';\nimport type {IButtonProps, IconName, IStyleClasses} from '@lmb-it/kitsconcerto-types';\nimport {ResponsiveElement, useSeparator, style as resolveStyle} from '@lmb-it/kitsconcertomobile';\nimport {IconMap} from '../../assets/Icons';\nimport {useLanguage, useComponentDefaults, useThemedFont} from '../../hooks';\nimport {useSeverityColors} from '../../hooks/useSeverityColors';\nimport {useKitsTheme} from '../../contexts/Theme';\n\n/* ─── Size configuration (matches web) ────────────────────────── */\nconst SIZE_MAP = {\n sm: {height: 32, fontSize: 12, px: 10, py: 6, gap: 6, iconSize: 14},\n md: {height: 40, fontSize: 14, px: 16, py: 10, gap: 8, iconSize: 16},\n lg: {height: 48, fontSize: 16, px: 24, py: 12, gap: 10, iconSize: 18},\n} as const;\n\n/* ─── Icon resolver ──────────────────────────────────────────── */\nfunction resolveIcon(\n icon: unknown,\n color: string,\n iconSize: number,\n): React.ReactNode {\n if (!icon) return null;\n if (React.isValidElement(icon)) {\n return React.cloneElement(icon as ReactElement<any>, {color, size: iconSize});\n }\n if (typeof icon === 'function') {\n const Comp = icon as ElementType;\n return <Comp size={iconSize} color={color} />;\n }\n if (typeof icon !== 'string') return null;\n const piMatches = icon.match(/pi-([a-z-]+)/g);\n const iconName = piMatches\n ? (piMatches[piMatches.length - 1]!.replace('pi-', '') as IconName)\n : (icon as IconName);\n const Component = IconMap[iconName];\n if (Component) return <Component size={iconSize} color={color} />;\n return null;\n}\n\n/* ─── Button Component ───────────────────────────────────────── */\nexport function Button(rawProps: IButtonProps<IconName | string | ElementType | ReactElement>) {\n const {mergedProps: props, themeStyle, elementStyles} = useComponentDefaults('Button', rawProps);\n const {\n children,\n label,\n isLoadingText,\n severity = 'brand',\n size = 'md',\n loading,\n iconPos = 'left',\n icon,\n disabled,\n outlined,\n raised,\n rounded,\n testID,\n style: styleProp,\n onPress,\n onClick,\n ...rest\n } = props;\n\n const {t} = useLanguage();\n const labelFont = useThemedFont('body');\n const {cssProps, nativeProps} = useSeparator<Record<any, any>>(rest);\n const sevColors = useSeverityColors((severity as any) ?? 'brand');\n\n const sizeConfig = SIZE_MAP[size as keyof typeof SIZE_MAP] ?? SIZE_MAP.md;\n const isDisabled = disabled || loading;\n\n /* ── Resolve label text ── */\n const resolvedLabel = typeof children === 'string'\n ? children\n : loading && isLoadingText\n ? isLoadingText\n : label ? t(label) : '';\n\n const hasLabel = !!resolvedLabel;\n const isIconOnly = !hasLabel && !!icon;\n\n /* ── Severity colors ── */\n /*\n * ⛔ A DISABLED BUTTON STOPS BEING THE BRAND, IT DOES NOT FADE — and this is\n * the phone's half of the same change made on web.\n *\n * The only disabled treatment was `opacity: isDisabled ? 50 : 100`, so the\n * button kept its brand fill, its white label and its brand border at half\n * strength. Same colour family, same filled shape, same affordance: it reads\n * as a slightly lighter LIVE button, and white on half-opacity brand is near\n * 1.7:1, so the fade hurts the label more than it announces the state.\n *\n * ⛔ THE COMP ALREADY ANSWERED IT: the locked bid rows draw a disabled\n * control as `#EEF0F2` ground with `#9CA3AF` text at FULL opacity.\n * `surface-muted` and `text-subtle` are the theme's own nearest tokens.\n *\n * ⚠️ OUTLINED KEEPS ITS TRANSPARENT GROUND. Filling it grey would make a\n * quiet control louder disabled than enabled.\n */\n const {resolveToken} = useKitsTheme();\n const disabledText = resolveToken('text-subtle');\n const textColor = isDisabled\n ? disabledText\n : outlined ? sevColors.solid : sevColors.solidText;\n const bgColor = outlined\n ? 'transparent'\n : isDisabled ? resolveToken('surface-muted') : sevColors.solid;\n const borderColor = isDisabled\n ? resolveToken('border')\n : sevColors.border || sevColors.solid;\n\n /* ── Build CSS props for ResponsiveElement ── */\n const computedCss:IStyleClasses = useMemo(() => ({\n backgroundColor: bgColor,\n borderColor,\n borderWidth: 1,\n height: sizeConfig.height,\n paddingLeft: hasLabel ? sizeConfig.px : sizeConfig.py,\n paddingRight: hasLabel ? sizeConfig.px : sizeConfig.py,\n paddingTop: sizeConfig.py,\n paddingBottom: sizeConfig.py,\n borderRadius: rounded ? 9999 : 6,\n flexDirection: iconPos === 'top' || iconPos === 'bottom'\n ? (iconPos === 'bottom' ? 'column-reverse' : 'column')\n : (iconPos === 'right' ? 'row-reverse' : 'row'),\n alignItems: 'center',\n justifyContent: 'center',\n gap: sizeConfig.gap,\n // ⚠️ NO OPACITY BRANCH. The colours above carry the state now; fading\n // them as well would take the new muted label back below legibility.\n opacity: 100,\n alignSelf: 'flex-start',\n overflow: 'hidden',\n ...(raised ? {\n shadowColor: '#000',\n shadowOffset: {width: 0, height: 3},\n shadowOpacity: 0.16,\n shadowRadius: 6,\n elevation: 4,\n } : {}),\n // Theme overrides the defaults above: shared `root` first, then the\n // `text` slot (label buttons) or the `iconOnly` slot. Icon-only buttons\n // are forced square so a `root`/`text` height can never stretch them —\n // the `iconOnly` slot can still override the square if needed.\n ...themeStyle,\n ...(elementStyles.root || {}),\n ...(isIconOnly\n ? {\n width: sizeConfig.height,\n height: sizeConfig.height,\n minWidth: sizeConfig.height,\n ...(elementStyles.iconOnly || {}),\n }\n : (elementStyles.text || {})),\n ...cssProps,\n ...styleProp,\n }), [themeStyle, elementStyles, cssProps, bgColor, borderColor, sizeConfig,\n hasLabel, icon, iconPos, rounded, raised, isDisabled, styleProp, isIconOnly]);\n\n /* ── Icon element ── */\n const iconElement = loading\n ? <ActivityIndicator size=\"small\" color={textColor} />\n : resolveIcon(icon, textColor, sizeConfig.iconSize);\n\n /* ── Text style ── */\n const labelStyle: TextStyle = useMemo(() => ({\n color: textColor,\n fontSize: sizeConfig.fontSize,\n fontWeight: '600',\n lineHeight: sizeConfig.fontSize * 1.2,\n ...(labelFont ? {fontFamily: labelFont} : {}),\n // Theme/per-instance label slot wins — e.g. theme.components.Button.label.style\n // = { lineHeight: 22 } to stop Arabic (NotoKufiArabic) glyphs clipping.\n ...((elementStyles.label as TextStyle) || {}),\n }), [textColor, sizeConfig, labelFont, elementStyles.label]);\n\n /* ── Press handler ── */\n const handlePress = useCallback((e: GestureResponderEvent) => {\n if (isDisabled) return;\n if (onPress) onPress(e as any);\n if (onClick) onClick(e as any);\n }, [isDisabled, onPress, onClick]);\n\n return (\n <ResponsiveElement\n as=\"Pressable\"\n cssProps={computedCss}\n nativeProps={{\n ...nativeProps,\n onPress: handlePress,\n disabled: isDisabled,\n testID,\n accessibilityRole: 'button',\n accessibilityState: {disabled: isDisabled, busy: loading},\n accessibilityLabel: resolvedLabel || undefined,\n }}\n >\n {iconElement}\n {!!hasLabel && (\n <RNText style={labelStyle} numberOfLines={1}>\n {resolvedLabel}\n </RNText>\n )}\n {typeof children !== 'string' ? children : undefined}\n </ResponsiveElement>\n );\n}\n\nexport default Button;\n"],"names":["jsx","IconMap","useComponentDefaults","useLanguage","useThemedFont","useSeparator","useSeverityColors","useKitsTheme","useMemo","ActivityIndicator","useCallback","jsxs","ResponsiveElement","RNText"],"mappings":"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"}
|
|
@@ -87,6 +87,7 @@ import '../../contexts/DialogContext.js';
|
|
|
87
87
|
import useComponentDefaults from '../../hooks/useComponentDefaults.js';
|
|
88
88
|
import { useSeverityColors } from '../../hooks/useSeverityColors.js';
|
|
89
89
|
import useThemedFont from '../../hooks/useThemedFont.js';
|
|
90
|
+
import { useKitsTheme } from '../../contexts/Theme/KitsThemeProvider.js';
|
|
90
91
|
|
|
91
92
|
const SIZE_MAP = {
|
|
92
93
|
sm: { height: 32, fontSize: 12, px: 10, py: 6, gap: 6, iconSize: 14 },
|
|
@@ -139,9 +140,11 @@ function Button(rawProps) {
|
|
|
139
140
|
const resolvedLabel = typeof children === "string" ? children : loading && isLoadingText ? isLoadingText : label ? t(label) : "";
|
|
140
141
|
const hasLabel = !!resolvedLabel;
|
|
141
142
|
const isIconOnly = !hasLabel && !!icon;
|
|
142
|
-
const
|
|
143
|
-
const
|
|
144
|
-
const
|
|
143
|
+
const { resolveToken } = useKitsTheme();
|
|
144
|
+
const disabledText = resolveToken("text-subtle");
|
|
145
|
+
const textColor = isDisabled ? disabledText : outlined ? sevColors.solid : sevColors.solidText;
|
|
146
|
+
const bgColor = outlined ? "transparent" : isDisabled ? resolveToken("surface-muted") : sevColors.solid;
|
|
147
|
+
const borderColor = isDisabled ? resolveToken("border") : sevColors.border || sevColors.solid;
|
|
145
148
|
const computedCss = useMemo(() => ({
|
|
146
149
|
backgroundColor: bgColor,
|
|
147
150
|
borderColor,
|
|
@@ -156,7 +159,9 @@ function Button(rawProps) {
|
|
|
156
159
|
alignItems: "center",
|
|
157
160
|
justifyContent: "center",
|
|
158
161
|
gap: sizeConfig.gap,
|
|
159
|
-
|
|
162
|
+
// ⚠️ NO OPACITY BRANCH. The colours above carry the state now; fading
|
|
163
|
+
// them as well would take the new muted label back below legibility.
|
|
164
|
+
opacity: 100,
|
|
160
165
|
alignSelf: "flex-start",
|
|
161
166
|
overflow: "hidden",
|
|
162
167
|
...raised ? {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.js","sources":["../../../../src/components/Button/Button.native.tsx"],"sourcesContent":["import React, {useMemo, useCallback, type ElementType, type ReactElement} from 'react';\nimport {\n ActivityIndicator,\n Text as RNText,\n type TextStyle,\n type GestureResponderEvent,\n} from 'react-native';\nimport type {IButtonProps, IconName, IStyleClasses} from '@lmb-it/kitsconcerto-types';\nimport {ResponsiveElement, useSeparator, style as resolveStyle} from '@lmb-it/kitsconcertomobile';\nimport {IconMap} from '../../assets/Icons';\nimport {useLanguage, useComponentDefaults, useThemedFont} from '../../hooks';\nimport {useSeverityColors} from '../../hooks/useSeverityColors';\n\n/* ─── Size configuration (matches web) ────────────────────────── */\nconst SIZE_MAP = {\n sm: {height: 32, fontSize: 12, px: 10, py: 6, gap: 6, iconSize: 14},\n md: {height: 40, fontSize: 14, px: 16, py: 10, gap: 8, iconSize: 16},\n lg: {height: 48, fontSize: 16, px: 24, py: 12, gap: 10, iconSize: 18},\n} as const;\n\n/* ─── Icon resolver ──────────────────────────────────────────── */\nfunction resolveIcon(\n icon: unknown,\n color: string,\n iconSize: number,\n): React.ReactNode {\n if (!icon) return null;\n if (React.isValidElement(icon)) {\n return React.cloneElement(icon as ReactElement<any>, {color, size: iconSize});\n }\n if (typeof icon === 'function') {\n const Comp = icon as ElementType;\n return <Comp size={iconSize} color={color} />;\n }\n if (typeof icon !== 'string') return null;\n const piMatches = icon.match(/pi-([a-z-]+)/g);\n const iconName = piMatches\n ? (piMatches[piMatches.length - 1]!.replace('pi-', '') as IconName)\n : (icon as IconName);\n const Component = IconMap[iconName];\n if (Component) return <Component size={iconSize} color={color} />;\n return null;\n}\n\n/* ─── Button Component ───────────────────────────────────────── */\nexport function Button(rawProps: IButtonProps<IconName | string | ElementType | ReactElement>) {\n const {mergedProps: props, themeStyle, elementStyles} = useComponentDefaults('Button', rawProps);\n const {\n children,\n label,\n isLoadingText,\n severity = 'brand',\n size = 'md',\n loading,\n iconPos = 'left',\n icon,\n disabled,\n outlined,\n raised,\n rounded,\n testID,\n style: styleProp,\n onPress,\n onClick,\n ...rest\n } = props;\n\n const {t} = useLanguage();\n const labelFont = useThemedFont('body');\n const {cssProps, nativeProps} = useSeparator<Record<any, any>>(rest);\n const sevColors = useSeverityColors((severity as any) ?? 'brand');\n\n const sizeConfig = SIZE_MAP[size as keyof typeof SIZE_MAP] ?? SIZE_MAP.md;\n const isDisabled = disabled || loading;\n\n /* ── Resolve label text ── */\n const resolvedLabel = typeof children === 'string'\n ? children\n : loading && isLoadingText\n ? isLoadingText\n : label ? t(label) : '';\n\n const hasLabel = !!resolvedLabel;\n const isIconOnly = !hasLabel && !!icon;\n\n /* ── Severity colors ── */\n const textColor = outlined ? sevColors.solid : sevColors.solidText;\n const bgColor = outlined ? 'transparent' : sevColors.solid;\n const borderColor = sevColors.border || sevColors.solid;\n\n /* ── Build CSS props for ResponsiveElement ── */\n const computedCss:IStyleClasses = useMemo(() => ({\n backgroundColor: bgColor,\n borderColor,\n borderWidth: 1,\n height: sizeConfig.height,\n paddingLeft: hasLabel ? sizeConfig.px : sizeConfig.py,\n paddingRight: hasLabel ? sizeConfig.px : sizeConfig.py,\n paddingTop: sizeConfig.py,\n paddingBottom: sizeConfig.py,\n borderRadius: rounded ? 9999 : 6,\n flexDirection: iconPos === 'top' || iconPos === 'bottom'\n ? (iconPos === 'bottom' ? 'column-reverse' : 'column')\n : (iconPos === 'right' ? 'row-reverse' : 'row'),\n alignItems: 'center',\n justifyContent: 'center',\n gap: sizeConfig.gap,\n opacity: isDisabled ? 50 : 100,\n alignSelf: 'flex-start',\n overflow: 'hidden',\n ...(raised ? {\n shadowColor: '#000',\n shadowOffset: {width: 0, height: 3},\n shadowOpacity: 0.16,\n shadowRadius: 6,\n elevation: 4,\n } : {}),\n // Theme overrides the defaults above: shared `root` first, then the\n // `text` slot (label buttons) or the `iconOnly` slot. Icon-only buttons\n // are forced square so a `root`/`text` height can never stretch them —\n // the `iconOnly` slot can still override the square if needed.\n ...themeStyle,\n ...(elementStyles.root || {}),\n ...(isIconOnly\n ? {\n width: sizeConfig.height,\n height: sizeConfig.height,\n minWidth: sizeConfig.height,\n ...(elementStyles.iconOnly || {}),\n }\n : (elementStyles.text || {})),\n ...cssProps,\n ...styleProp,\n }), [themeStyle, elementStyles, cssProps, bgColor, borderColor, sizeConfig,\n hasLabel, icon, iconPos, rounded, raised, isDisabled, styleProp, isIconOnly]);\n\n /* ── Icon element ── */\n const iconElement = loading\n ? <ActivityIndicator size=\"small\" color={textColor} />\n : resolveIcon(icon, textColor, sizeConfig.iconSize);\n\n /* ── Text style ── */\n const labelStyle: TextStyle = useMemo(() => ({\n color: textColor,\n fontSize: sizeConfig.fontSize,\n fontWeight: '600',\n lineHeight: sizeConfig.fontSize * 1.2,\n ...(labelFont ? {fontFamily: labelFont} : {}),\n // Theme/per-instance label slot wins — e.g. theme.components.Button.label.style\n // = { lineHeight: 22 } to stop Arabic (NotoKufiArabic) glyphs clipping.\n ...((elementStyles.label as TextStyle) || {}),\n }), [textColor, sizeConfig, labelFont, elementStyles.label]);\n\n /* ── Press handler ── */\n const handlePress = useCallback((e: GestureResponderEvent) => {\n if (isDisabled) return;\n if (onPress) onPress(e as any);\n if (onClick) onClick(e as any);\n }, [isDisabled, onPress, onClick]);\n\n return (\n <ResponsiveElement\n as=\"Pressable\"\n cssProps={computedCss}\n nativeProps={{\n ...nativeProps,\n onPress: handlePress,\n disabled: isDisabled,\n testID,\n accessibilityRole: 'button',\n accessibilityState: {disabled: isDisabled, busy: loading},\n accessibilityLabel: resolvedLabel || undefined,\n }}\n >\n {iconElement}\n {!!hasLabel && (\n <RNText style={labelStyle} numberOfLines={1}>\n {resolvedLabel}\n </RNText>\n )}\n {typeof children !== 'string' ? children : undefined}\n </ResponsiveElement>\n );\n}\n\nexport default Button;\n"],"names":["RNText"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Button.js","sources":["../../../../src/components/Button/Button.native.tsx"],"sourcesContent":["import React, {useMemo, useCallback, type ElementType, type ReactElement} from 'react';\nimport {\n ActivityIndicator,\n Text as RNText,\n type TextStyle,\n type GestureResponderEvent,\n} from 'react-native';\nimport type {IButtonProps, IconName, IStyleClasses} from '@lmb-it/kitsconcerto-types';\nimport {ResponsiveElement, useSeparator, style as resolveStyle} from '@lmb-it/kitsconcertomobile';\nimport {IconMap} from '../../assets/Icons';\nimport {useLanguage, useComponentDefaults, useThemedFont} from '../../hooks';\nimport {useSeverityColors} from '../../hooks/useSeverityColors';\nimport {useKitsTheme} from '../../contexts/Theme';\n\n/* ─── Size configuration (matches web) ────────────────────────── */\nconst SIZE_MAP = {\n sm: {height: 32, fontSize: 12, px: 10, py: 6, gap: 6, iconSize: 14},\n md: {height: 40, fontSize: 14, px: 16, py: 10, gap: 8, iconSize: 16},\n lg: {height: 48, fontSize: 16, px: 24, py: 12, gap: 10, iconSize: 18},\n} as const;\n\n/* ─── Icon resolver ──────────────────────────────────────────── */\nfunction resolveIcon(\n icon: unknown,\n color: string,\n iconSize: number,\n): React.ReactNode {\n if (!icon) return null;\n if (React.isValidElement(icon)) {\n return React.cloneElement(icon as ReactElement<any>, {color, size: iconSize});\n }\n if (typeof icon === 'function') {\n const Comp = icon as ElementType;\n return <Comp size={iconSize} color={color} />;\n }\n if (typeof icon !== 'string') return null;\n const piMatches = icon.match(/pi-([a-z-]+)/g);\n const iconName = piMatches\n ? (piMatches[piMatches.length - 1]!.replace('pi-', '') as IconName)\n : (icon as IconName);\n const Component = IconMap[iconName];\n if (Component) return <Component size={iconSize} color={color} />;\n return null;\n}\n\n/* ─── Button Component ───────────────────────────────────────── */\nexport function Button(rawProps: IButtonProps<IconName | string | ElementType | ReactElement>) {\n const {mergedProps: props, themeStyle, elementStyles} = useComponentDefaults('Button', rawProps);\n const {\n children,\n label,\n isLoadingText,\n severity = 'brand',\n size = 'md',\n loading,\n iconPos = 'left',\n icon,\n disabled,\n outlined,\n raised,\n rounded,\n testID,\n style: styleProp,\n onPress,\n onClick,\n ...rest\n } = props;\n\n const {t} = useLanguage();\n const labelFont = useThemedFont('body');\n const {cssProps, nativeProps} = useSeparator<Record<any, any>>(rest);\n const sevColors = useSeverityColors((severity as any) ?? 'brand');\n\n const sizeConfig = SIZE_MAP[size as keyof typeof SIZE_MAP] ?? SIZE_MAP.md;\n const isDisabled = disabled || loading;\n\n /* ── Resolve label text ── */\n const resolvedLabel = typeof children === 'string'\n ? children\n : loading && isLoadingText\n ? isLoadingText\n : label ? t(label) : '';\n\n const hasLabel = !!resolvedLabel;\n const isIconOnly = !hasLabel && !!icon;\n\n /* ── Severity colors ── */\n /*\n * ⛔ A DISABLED BUTTON STOPS BEING THE BRAND, IT DOES NOT FADE — and this is\n * the phone's half of the same change made on web.\n *\n * The only disabled treatment was `opacity: isDisabled ? 50 : 100`, so the\n * button kept its brand fill, its white label and its brand border at half\n * strength. Same colour family, same filled shape, same affordance: it reads\n * as a slightly lighter LIVE button, and white on half-opacity brand is near\n * 1.7:1, so the fade hurts the label more than it announces the state.\n *\n * ⛔ THE COMP ALREADY ANSWERED IT: the locked bid rows draw a disabled\n * control as `#EEF0F2` ground with `#9CA3AF` text at FULL opacity.\n * `surface-muted` and `text-subtle` are the theme's own nearest tokens.\n *\n * ⚠️ OUTLINED KEEPS ITS TRANSPARENT GROUND. Filling it grey would make a\n * quiet control louder disabled than enabled.\n */\n const {resolveToken} = useKitsTheme();\n const disabledText = resolveToken('text-subtle');\n const textColor = isDisabled\n ? disabledText\n : outlined ? sevColors.solid : sevColors.solidText;\n const bgColor = outlined\n ? 'transparent'\n : isDisabled ? resolveToken('surface-muted') : sevColors.solid;\n const borderColor = isDisabled\n ? resolveToken('border')\n : sevColors.border || sevColors.solid;\n\n /* ── Build CSS props for ResponsiveElement ── */\n const computedCss:IStyleClasses = useMemo(() => ({\n backgroundColor: bgColor,\n borderColor,\n borderWidth: 1,\n height: sizeConfig.height,\n paddingLeft: hasLabel ? sizeConfig.px : sizeConfig.py,\n paddingRight: hasLabel ? sizeConfig.px : sizeConfig.py,\n paddingTop: sizeConfig.py,\n paddingBottom: sizeConfig.py,\n borderRadius: rounded ? 9999 : 6,\n flexDirection: iconPos === 'top' || iconPos === 'bottom'\n ? (iconPos === 'bottom' ? 'column-reverse' : 'column')\n : (iconPos === 'right' ? 'row-reverse' : 'row'),\n alignItems: 'center',\n justifyContent: 'center',\n gap: sizeConfig.gap,\n // ⚠️ NO OPACITY BRANCH. The colours above carry the state now; fading\n // them as well would take the new muted label back below legibility.\n opacity: 100,\n alignSelf: 'flex-start',\n overflow: 'hidden',\n ...(raised ? {\n shadowColor: '#000',\n shadowOffset: {width: 0, height: 3},\n shadowOpacity: 0.16,\n shadowRadius: 6,\n elevation: 4,\n } : {}),\n // Theme overrides the defaults above: shared `root` first, then the\n // `text` slot (label buttons) or the `iconOnly` slot. Icon-only buttons\n // are forced square so a `root`/`text` height can never stretch them —\n // the `iconOnly` slot can still override the square if needed.\n ...themeStyle,\n ...(elementStyles.root || {}),\n ...(isIconOnly\n ? {\n width: sizeConfig.height,\n height: sizeConfig.height,\n minWidth: sizeConfig.height,\n ...(elementStyles.iconOnly || {}),\n }\n : (elementStyles.text || {})),\n ...cssProps,\n ...styleProp,\n }), [themeStyle, elementStyles, cssProps, bgColor, borderColor, sizeConfig,\n hasLabel, icon, iconPos, rounded, raised, isDisabled, styleProp, isIconOnly]);\n\n /* ── Icon element ── */\n const iconElement = loading\n ? <ActivityIndicator size=\"small\" color={textColor} />\n : resolveIcon(icon, textColor, sizeConfig.iconSize);\n\n /* ── Text style ── */\n const labelStyle: TextStyle = useMemo(() => ({\n color: textColor,\n fontSize: sizeConfig.fontSize,\n fontWeight: '600',\n lineHeight: sizeConfig.fontSize * 1.2,\n ...(labelFont ? {fontFamily: labelFont} : {}),\n // Theme/per-instance label slot wins — e.g. theme.components.Button.label.style\n // = { lineHeight: 22 } to stop Arabic (NotoKufiArabic) glyphs clipping.\n ...((elementStyles.label as TextStyle) || {}),\n }), [textColor, sizeConfig, labelFont, elementStyles.label]);\n\n /* ── Press handler ── */\n const handlePress = useCallback((e: GestureResponderEvent) => {\n if (isDisabled) return;\n if (onPress) onPress(e as any);\n if (onClick) onClick(e as any);\n }, [isDisabled, onPress, onClick]);\n\n return (\n <ResponsiveElement\n as=\"Pressable\"\n cssProps={computedCss}\n nativeProps={{\n ...nativeProps,\n onPress: handlePress,\n disabled: isDisabled,\n testID,\n accessibilityRole: 'button',\n accessibilityState: {disabled: isDisabled, busy: loading},\n accessibilityLabel: resolvedLabel || undefined,\n }}\n >\n {iconElement}\n {!!hasLabel && (\n <RNText style={labelStyle} numberOfLines={1}>\n {resolvedLabel}\n </RNText>\n )}\n {typeof children !== 'string' ? children : undefined}\n </ResponsiveElement>\n );\n}\n\nexport default Button;\n"],"names":["RNText"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FieldWrapper.cjs","sources":["../../../../../src/form-renderer/components/layout/FieldWrapper.native.tsx"],"sourcesContent":["// components/layout/FieldWrapper.native.tsx\n\nimport React, {useCallback, useEffect, useMemo, useRef} from \"react\";\nimport type {FieldValues} from \"react-hook-form\";\nimport {useWatch} from \"react-hook-form\";\nimport {useFieldLogic} from \"../../hooks\";\nimport type {FieldWrapperProps, GroupFieldConfigs} from \"@lmb-it/kitsconcerto-types\";\nimport {Flex} from \"../../../_compat\";\nimport {createStyleContext, resolveResponsiveValue} from \"@lmb-it/kitsconcertomobile\";\n\n/**\n * Native layout contract:\n * - Container MUST be flex-row + flex-wrap\n * - Grid semantics are approximated via percentages\n *\n * Supported mappings:\n * - columns -> virtual column count\n * - colSpan -> width %\n * - colOffset -> marginLeft %\n * - columnGap -> item horizontal padding\n * - rowGap -> item vertical padding\n * - rowSpan -> minHeight approximation\n *\n * Ignored (grid-only semantics):\n * - templateAreas\n * - area\n * - gridTemplateRows\n * - gridTemplateColumns\n * - rowStart / rowEnd\n * - colStart / colEnd\n */\n\nconst DEFAULT_COLUMNS = 12;\nconst DEFAULT_ROW_HEIGHT = 56;\n\nexport const FieldWrapper = <T extends FieldValues>({\n element,\n control,\n getValues,\n setFocusedField,\n focusedField,\n children,\n grid,\n onGridChange,\n groupField,\n }: FieldWrapperProps<T>) => {\n /* ---------------- helpers (no hooks) ---------------- */\n\n const currentLayout = useRef<any>(null);\n const resolveDepPath = (dep: string, groupField?: GroupFieldConfigs<T>['groupField']): string => {\n if (!groupField || groupField.index === undefined || groupField.index < 0) return dep;\n\n const parts = dep.split(\".\");\n const {name, index} = groupField;\n\n // Must be \"<groupRef>.current.<field...>\"\n if (parts.length < 2 || parts[1] !== \"current\") return dep;\n\n // Group-relative deps are authored against the group's own id\n // (e.g. \"addresses.current.x\"). When the group is nested inside an\n // Object the live `name` is the fully-qualified dotted path\n // (e.g. \"addressGroup.addresses\"), so match the dep's first segment\n // against EITHER the full name or its trailing segment, then rebuild\n // against the fully-qualified name so useWatch hits the real path.\n const nameLast = name.includes(\".\") ? name.slice(name.lastIndexOf(\".\") + 1) : name;\n if (parts[0] !== name && parts[0] !== nameLast) return dep;\n\n return [name, String(index), ...parts.slice(2)].join(\".\");\n };\n\n const flattenPaths = (obj: any, prefix = ''): string[] =>\n Object.keys(obj ?? {}).flatMap((key) => {\n const path = prefix ? `${prefix}.${key}` : key;\n return typeof obj[key] === 'object' && obj[key] !== null\n ? [path, ...flattenPaths(obj[key], path)]\n : [path];\n });\n\n // 1. Determine which fields to watch for changes.\n const dependencies = useMemo(() => {\n const deps = new Set<string>([...(element.deps ?? []), ...(element.logic?.deps ?? [])]);\n\n if (deps.has('*')) {\n return flattenPaths(getValues());\n }\n\n return Array.from(deps).map((d) => resolveDepPath(d, groupField));\n }, [element, groupField]);\n\n\n // 2. Efficiently watch only the necessary fields.\n const watchedValues = useWatch({\n name: dependencies as any,\n compute:(fieldValue)=>{\n return fieldValue\n },\n exact:true,\n disabled:dependencies.length === 0\n });\n\n useEffect(() => {\n // console.log('watchedValues', watchedValues)\n }, [watchedValues]);\n // This single hook provides all the necessary logic and state for the field.\n const fieldLogic = useFieldLogic({element, control, getValues, isFocused: focusedField === element.id, groupField, watchedValues});\n\n const {\n field,\n isShown,\n layout,\n style:elementStyle,\n animation\n } = fieldLogic;\n const isFocused = focusedField === field.name;\n\n const onFocus = useCallback(() => setFocusedField(field.name), [setFocusedField, field.name]);\n const onBlur: React.FocusEventHandler<any> = useCallback((event) => {\n const cl = event.target['className'];\n // @ts-ignore\n if(!cl || cl.includes(`${element.id}_option_item`) || cl.includes(`p-multiselect-filter`)){\n return;\n }\n setFocusedField('');\n field.onBlur(); // Call the original onBlur from react-hook-form\n }, [setFocusedField, field.onBlur]);\n\n // Grid layout effect — MUST be before any conditional return (Rules of Hooks)\n useEffect(() => {\n if(layout.colSpan && currentLayout.current != JSON.stringify(layout)){\n const ctx = createStyleContext();\n\n const colSpan = resolveResponsiveValue<string | number>(layout.colSpan, ctx);\n\n onGridChange?.(colSpan?.toString() ?? '12')\n\n currentLayout.current = JSON.stringify(layout)\n }\n\n }, [layout]);\n\n // If the logic dictates this field should not be shown, render nothing.\n if (!isShown || !children) {\n return null;\n }\n\n\n\n // @ts-ignore\n return <Flex {...elementStyle?.container} {...animation}\n zIndex={isFocused ? 1000 :
|
|
1
|
+
{"version":3,"file":"FieldWrapper.cjs","sources":["../../../../../src/form-renderer/components/layout/FieldWrapper.native.tsx"],"sourcesContent":["// components/layout/FieldWrapper.native.tsx\n\nimport React, {useCallback, useEffect, useMemo, useRef} from \"react\";\nimport type {FieldValues} from \"react-hook-form\";\nimport {useWatch} from \"react-hook-form\";\nimport {useFieldLogic} from \"../../hooks\";\nimport type {FieldWrapperProps, GroupFieldConfigs} from \"@lmb-it/kitsconcerto-types\";\nimport {Flex} from \"../../../_compat\";\nimport {createStyleContext, resolveResponsiveValue} from \"@lmb-it/kitsconcertomobile\";\n\n/**\n * Native layout contract:\n * - Container MUST be flex-row + flex-wrap\n * - Grid semantics are approximated via percentages\n *\n * Supported mappings:\n * - columns -> virtual column count\n * - colSpan -> width %\n * - colOffset -> marginLeft %\n * - columnGap -> item horizontal padding\n * - rowGap -> item vertical padding\n * - rowSpan -> minHeight approximation\n *\n * Ignored (grid-only semantics):\n * - templateAreas\n * - area\n * - gridTemplateRows\n * - gridTemplateColumns\n * - rowStart / rowEnd\n * - colStart / colEnd\n */\n\nconst DEFAULT_COLUMNS = 12;\nconst DEFAULT_ROW_HEIGHT = 56;\n\nexport const FieldWrapper = <T extends FieldValues>({\n element,\n control,\n getValues,\n setFocusedField,\n focusedField,\n children,\n grid,\n onGridChange,\n groupField,\n }: FieldWrapperProps<T>) => {\n /* ---------------- helpers (no hooks) ---------------- */\n\n const currentLayout = useRef<any>(null);\n const resolveDepPath = (dep: string, groupField?: GroupFieldConfigs<T>['groupField']): string => {\n if (!groupField || groupField.index === undefined || groupField.index < 0) return dep;\n\n const parts = dep.split(\".\");\n const {name, index} = groupField;\n\n // Must be \"<groupRef>.current.<field...>\"\n if (parts.length < 2 || parts[1] !== \"current\") return dep;\n\n // Group-relative deps are authored against the group's own id\n // (e.g. \"addresses.current.x\"). When the group is nested inside an\n // Object the live `name` is the fully-qualified dotted path\n // (e.g. \"addressGroup.addresses\"), so match the dep's first segment\n // against EITHER the full name or its trailing segment, then rebuild\n // against the fully-qualified name so useWatch hits the real path.\n const nameLast = name.includes(\".\") ? name.slice(name.lastIndexOf(\".\") + 1) : name;\n if (parts[0] !== name && parts[0] !== nameLast) return dep;\n\n return [name, String(index), ...parts.slice(2)].join(\".\");\n };\n\n const flattenPaths = (obj: any, prefix = ''): string[] =>\n Object.keys(obj ?? {}).flatMap((key) => {\n const path = prefix ? `${prefix}.${key}` : key;\n return typeof obj[key] === 'object' && obj[key] !== null\n ? [path, ...flattenPaths(obj[key], path)]\n : [path];\n });\n\n // 1. Determine which fields to watch for changes.\n const dependencies = useMemo(() => {\n const deps = new Set<string>([...(element.deps ?? []), ...(element.logic?.deps ?? [])]);\n\n if (deps.has('*')) {\n return flattenPaths(getValues());\n }\n\n return Array.from(deps).map((d) => resolveDepPath(d, groupField));\n }, [element, groupField]);\n\n\n // 2. Efficiently watch only the necessary fields.\n const watchedValues = useWatch({\n name: dependencies as any,\n compute:(fieldValue)=>{\n return fieldValue\n },\n exact:true,\n disabled:dependencies.length === 0\n });\n\n useEffect(() => {\n // console.log('watchedValues', watchedValues)\n }, [watchedValues]);\n // This single hook provides all the necessary logic and state for the field.\n const fieldLogic = useFieldLogic({element, control, getValues, isFocused: focusedField === element.id, groupField, watchedValues});\n\n const {\n field,\n isShown,\n layout,\n style:elementStyle,\n animation\n } = fieldLogic;\n const isFocused = focusedField === field.name;\n\n const onFocus = useCallback(() => setFocusedField(field.name), [setFocusedField, field.name]);\n const onBlur: React.FocusEventHandler<any> = useCallback((event) => {\n const cl = event.target['className'];\n // @ts-ignore\n if(!cl || cl.includes(`${element.id}_option_item`) || cl.includes(`p-multiselect-filter`)){\n return;\n }\n setFocusedField('');\n field.onBlur(); // Call the original onBlur from react-hook-form\n }, [setFocusedField, field.onBlur]);\n\n // Grid layout effect — MUST be before any conditional return (Rules of Hooks)\n useEffect(() => {\n if(layout.colSpan && currentLayout.current != JSON.stringify(layout)){\n const ctx = createStyleContext();\n\n const colSpan = resolveResponsiveValue<string | number>(layout.colSpan, ctx);\n\n onGridChange?.(colSpan?.toString() ?? '12')\n\n currentLayout.current = JSON.stringify(layout)\n }\n\n }, [layout]);\n\n // If the logic dictates this field should not be shown, render nothing.\n if (!isShown || !children) {\n return null;\n }\n\n\n\n // @ts-ignore\n return <Flex {...elementStyle?.container} {...animation}\n zIndex={isFocused ? 1000 : undefined}\n w={'100%'} onTouchStart={onFocus}>\n {children(fieldLogic)}\n </Flex>\n};\n"],"names":["useRef","groupField","useMemo","useWatch","useEffect","useFieldLogic","useCallback","createStyleContext","resolveResponsiveValue","jsx","Flex"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCO,MAAM,eAAe,CAAwB;AAAA,EACI,OAAA;AAAA,EACA,OAAA;AAAA,EACA,SAAA;AAAA,EACA,eAAA;AAAA,EACA,YAAA;AAAA,EACA,QAAA;AAAA,EACA,IAAA;AAAA,EACA,YAAA;AAAA,EACA;AACJ,CAAA,KAA4B;AAG5E,EAAA,MAAM,aAAA,GAAgBA,aAAY,IAAI,CAAA;AACtC,EAAA,MAAM,cAAA,GAAiB,CAAC,GAAA,EAAaC,WAAAA,KAA4D;AAC7F,IAAA,IAAI,CAACA,eAAcA,WAAAA,CAAW,KAAA,KAAU,UAAaA,WAAAA,CAAW,KAAA,GAAQ,GAAG,OAAO,GAAA;AAElF,IAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,KAAA,CAAM,GAAG,CAAA;AAC3B,IAAA,MAAM,EAAC,IAAA,EAAM,KAAA,EAAK,GAAIA,WAAAA;AAGtB,IAAA,IAAI,MAAM,MAAA,GAAS,CAAA,IAAK,MAAM,CAAC,CAAA,KAAM,WAAW,OAAO,GAAA;AAQvD,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,QAAA,CAAS,GAAG,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,WAAA,CAAY,GAAG,CAAA,GAAI,CAAC,CAAA,GAAI,IAAA;AAC9E,IAAA,IAAI,KAAA,CAAM,CAAC,CAAA,KAAM,IAAA,IAAQ,MAAM,CAAC,CAAA,KAAM,UAAU,OAAO,GAAA;AAEvD,IAAA,OAAO,CAAC,IAAA,EAAM,MAAA,CAAO,KAAK,CAAA,EAAG,GAAG,KAAA,CAAM,KAAA,CAAM,CAAC,CAAC,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA;AAAA,EAC5D,CAAA;AAEA,EAAA,MAAM,YAAA,GAAe,CAAC,GAAA,EAAU,MAAA,GAAS,EAAA,KACrC,MAAA,CAAO,IAAA,CAAK,GAAA,IAAO,EAAE,CAAA,CAAE,OAAA,CAAQ,CAAC,GAAA,KAAQ;AACpC,IAAA,MAAM,OAAO,MAAA,GAAS,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,GAAG,CAAA,CAAA,GAAK,GAAA;AAC3C,IAAA,OAAO,OAAO,IAAI,GAAG,CAAA,KAAM,YAAY,GAAA,CAAI,GAAG,MAAM,IAAA,GAC9C,CAAC,MAAM,GAAG,YAAA,CAAa,IAAI,GAAG,CAAA,EAAG,IAAI,CAAC,CAAA,GACtC,CAAC,IAAI,CAAA;AAAA,EACf,CAAC,CAAA;AAGL,EAAA,MAAM,YAAA,GAAeC,cAAQ,MAAM;AAC/B,IAAA,MAAM,IAAA,mBAAO,IAAI,GAAA,CAAY,CAAC,GAAI,OAAA,CAAQ,IAAA,IAAQ,EAAC,EAAI,GAAI,OAAA,CAAQ,KAAA,EAAO,IAAA,IAAQ,EAAG,CAAC,CAAA;AAEtF,IAAA,IAAI,IAAA,CAAK,GAAA,CAAI,GAAG,CAAA,EAAG;AACf,MAAA,OAAO,YAAA,CAAa,WAAW,CAAA;AAAA,IACnC;AAEA,IAAA,OAAO,KAAA,CAAM,IAAA,CAAK,IAAI,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,KAAM,cAAA,CAAe,CAAA,EAAG,UAAU,CAAC,CAAA;AAAA,EACpE,CAAA,EAAG,CAAC,OAAA,EAAS,UAAU,CAAC,CAAA;AAIxB,EAAA,MAAM,gBAAgBC,sBAAA,CAAS;AAAA,IAC3B,IAAA,EAAM,YAAA;AAAA,IACN,OAAA,EAAQ,CAAC,UAAA,KAAa;AAClB,MAAA,OAAO,UAAA;AAAA,IACX,CAAA;AAAA,IACA,KAAA,EAAM,IAAA;AAAA,IACN,QAAA,EAAS,aAAa,MAAA,KAAW;AAAA,GACpC,CAAA;AAED,EAAAC,eAAA,CAAU,MAAM;AAAA,EAEhB,CAAA,EAAG,CAAC,aAAa,CAAC,CAAA;AAElB,EAAA,MAAM,UAAA,GAAaC,2BAAA,CAAc,EAAC,OAAA,EAAS,OAAA,EAAS,SAAA,EAAW,SAAA,EAAW,YAAA,KAAiB,OAAA,CAAQ,EAAA,EAAI,UAAA,EAAY,aAAA,EAAc,CAAA;AAEjI,EAAA,MAAM;AAAA,IACF,KAAA;AAAA,IACA,OAAA;AAAA,IACA,MAAA;AAAA,IACA,KAAA,EAAM,YAAA;AAAA,IACN;AAAA,GACJ,GAAI,UAAA;AACJ,EAAA,MAAM,SAAA,GAAY,iBAAiB,KAAA,CAAM,IAAA;AAEzC,EAAA,MAAM,OAAA,GAAUC,iBAAA,CAAY,MAAM,eAAA,CAAgB,KAAA,CAAM,IAAI,CAAA,EAAG,CAAC,eAAA,EAAiB,KAAA,CAAM,IAAI,CAAC,CAAA;AAC5F,EAA6CA,iBAAA,CAAY,CAAC,KAAA,KAAU;AAChE,IAAA,MAAM,EAAA,GAAK,KAAA,CAAM,MAAA,CAAO,WAAW,CAAA;AAEnC,IAAA,IAAG,CAAC,EAAA,IAAM,EAAA,CAAG,QAAA,CAAS,CAAA,EAAG,OAAA,CAAQ,EAAE,CAAA,YAAA,CAAc,CAAA,IAAK,EAAA,CAAG,QAAA,CAAS,CAAA,oBAAA,CAAsB,CAAA,EAAE;AACtF,MAAA;AAAA,IACJ;AACA,IAAA,eAAA,CAAgB,EAAE,CAAA;AAClB,IAAA,KAAA,CAAM,MAAA,EAAO;AAAA,EACjB,CAAA,EAAG,CAAC,eAAA,EAAiB,KAAA,CAAM,MAAM,CAAC;AAGlC,EAAAF,eAAA,CAAU,MAAM;AACZ,IAAA,IAAG,OAAO,OAAA,IAAW,aAAA,CAAc,WAAW,IAAA,CAAK,SAAA,CAAU,MAAM,CAAA,EAAE;AACjE,MAAA,MAAM,MAAMG,wBAAA,EAAmB;AAE/B,MAAA,MAAM,OAAA,GAAUC,4BAAA,CAAwC,MAAA,CAAO,OAAA,EAAS,GAAG,CAAA;AAE3E,MAAA,YAAA,GAAe,OAAA,EAAS,QAAA,EAAS,IAAK,IAAI,CAAA;AAE1C,MAAA,aAAA,CAAc,OAAA,GAAU,IAAA,CAAK,SAAA,CAAU,MAAM,CAAA;AAAA,IACjD;AAAA,EAEJ,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAGX,EAAA,IAAI,CAAC,OAAA,IAAW,CAAC,QAAA,EAAU;AACvB,IAAA,OAAO,IAAA;AAAA,EACX;AAKA,EAAA,uBAAOC,cAAA;AAAA,IAACC,aAAA;AAAA,IAAA;AAAA,MAAM,GAAG,YAAA,EAAc,SAAA;AAAA,MAAY,GAAG,SAAA;AAAA,MACjC,MAAA,EAAQ,YAAY,GAAA,GAAO,MAAA;AAAA,MAC3B,CAAA,EAAG,MAAA;AAAA,MAAQ,YAAA,EAAc,OAAA;AAAA,MACjC,mBAAS,UAAU;AAAA;AAAA,GACxB;AACJ;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FieldWrapper.js","sources":["../../../../../src/form-renderer/components/layout/FieldWrapper.native.tsx"],"sourcesContent":["// components/layout/FieldWrapper.native.tsx\n\nimport React, {useCallback, useEffect, useMemo, useRef} from \"react\";\nimport type {FieldValues} from \"react-hook-form\";\nimport {useWatch} from \"react-hook-form\";\nimport {useFieldLogic} from \"../../hooks\";\nimport type {FieldWrapperProps, GroupFieldConfigs} from \"@lmb-it/kitsconcerto-types\";\nimport {Flex} from \"../../../_compat\";\nimport {createStyleContext, resolveResponsiveValue} from \"@lmb-it/kitsconcertomobile\";\n\n/**\n * Native layout contract:\n * - Container MUST be flex-row + flex-wrap\n * - Grid semantics are approximated via percentages\n *\n * Supported mappings:\n * - columns -> virtual column count\n * - colSpan -> width %\n * - colOffset -> marginLeft %\n * - columnGap -> item horizontal padding\n * - rowGap -> item vertical padding\n * - rowSpan -> minHeight approximation\n *\n * Ignored (grid-only semantics):\n * - templateAreas\n * - area\n * - gridTemplateRows\n * - gridTemplateColumns\n * - rowStart / rowEnd\n * - colStart / colEnd\n */\n\nconst DEFAULT_COLUMNS = 12;\nconst DEFAULT_ROW_HEIGHT = 56;\n\nexport const FieldWrapper = <T extends FieldValues>({\n element,\n control,\n getValues,\n setFocusedField,\n focusedField,\n children,\n grid,\n onGridChange,\n groupField,\n }: FieldWrapperProps<T>) => {\n /* ---------------- helpers (no hooks) ---------------- */\n\n const currentLayout = useRef<any>(null);\n const resolveDepPath = (dep: string, groupField?: GroupFieldConfigs<T>['groupField']): string => {\n if (!groupField || groupField.index === undefined || groupField.index < 0) return dep;\n\n const parts = dep.split(\".\");\n const {name, index} = groupField;\n\n // Must be \"<groupRef>.current.<field...>\"\n if (parts.length < 2 || parts[1] !== \"current\") return dep;\n\n // Group-relative deps are authored against the group's own id\n // (e.g. \"addresses.current.x\"). When the group is nested inside an\n // Object the live `name` is the fully-qualified dotted path\n // (e.g. \"addressGroup.addresses\"), so match the dep's first segment\n // against EITHER the full name or its trailing segment, then rebuild\n // against the fully-qualified name so useWatch hits the real path.\n const nameLast = name.includes(\".\") ? name.slice(name.lastIndexOf(\".\") + 1) : name;\n if (parts[0] !== name && parts[0] !== nameLast) return dep;\n\n return [name, String(index), ...parts.slice(2)].join(\".\");\n };\n\n const flattenPaths = (obj: any, prefix = ''): string[] =>\n Object.keys(obj ?? {}).flatMap((key) => {\n const path = prefix ? `${prefix}.${key}` : key;\n return typeof obj[key] === 'object' && obj[key] !== null\n ? [path, ...flattenPaths(obj[key], path)]\n : [path];\n });\n\n // 1. Determine which fields to watch for changes.\n const dependencies = useMemo(() => {\n const deps = new Set<string>([...(element.deps ?? []), ...(element.logic?.deps ?? [])]);\n\n if (deps.has('*')) {\n return flattenPaths(getValues());\n }\n\n return Array.from(deps).map((d) => resolveDepPath(d, groupField));\n }, [element, groupField]);\n\n\n // 2. Efficiently watch only the necessary fields.\n const watchedValues = useWatch({\n name: dependencies as any,\n compute:(fieldValue)=>{\n return fieldValue\n },\n exact:true,\n disabled:dependencies.length === 0\n });\n\n useEffect(() => {\n // console.log('watchedValues', watchedValues)\n }, [watchedValues]);\n // This single hook provides all the necessary logic and state for the field.\n const fieldLogic = useFieldLogic({element, control, getValues, isFocused: focusedField === element.id, groupField, watchedValues});\n\n const {\n field,\n isShown,\n layout,\n style:elementStyle,\n animation\n } = fieldLogic;\n const isFocused = focusedField === field.name;\n\n const onFocus = useCallback(() => setFocusedField(field.name), [setFocusedField, field.name]);\n const onBlur: React.FocusEventHandler<any> = useCallback((event) => {\n const cl = event.target['className'];\n // @ts-ignore\n if(!cl || cl.includes(`${element.id}_option_item`) || cl.includes(`p-multiselect-filter`)){\n return;\n }\n setFocusedField('');\n field.onBlur(); // Call the original onBlur from react-hook-form\n }, [setFocusedField, field.onBlur]);\n\n // Grid layout effect — MUST be before any conditional return (Rules of Hooks)\n useEffect(() => {\n if(layout.colSpan && currentLayout.current != JSON.stringify(layout)){\n const ctx = createStyleContext();\n\n const colSpan = resolveResponsiveValue<string | number>(layout.colSpan, ctx);\n\n onGridChange?.(colSpan?.toString() ?? '12')\n\n currentLayout.current = JSON.stringify(layout)\n }\n\n }, [layout]);\n\n // If the logic dictates this field should not be shown, render nothing.\n if (!isShown || !children) {\n return null;\n }\n\n\n\n // @ts-ignore\n return <Flex {...elementStyle?.container} {...animation}\n zIndex={isFocused ? 1000 :
|
|
1
|
+
{"version":3,"file":"FieldWrapper.js","sources":["../../../../../src/form-renderer/components/layout/FieldWrapper.native.tsx"],"sourcesContent":["// components/layout/FieldWrapper.native.tsx\n\nimport React, {useCallback, useEffect, useMemo, useRef} from \"react\";\nimport type {FieldValues} from \"react-hook-form\";\nimport {useWatch} from \"react-hook-form\";\nimport {useFieldLogic} from \"../../hooks\";\nimport type {FieldWrapperProps, GroupFieldConfigs} from \"@lmb-it/kitsconcerto-types\";\nimport {Flex} from \"../../../_compat\";\nimport {createStyleContext, resolveResponsiveValue} from \"@lmb-it/kitsconcertomobile\";\n\n/**\n * Native layout contract:\n * - Container MUST be flex-row + flex-wrap\n * - Grid semantics are approximated via percentages\n *\n * Supported mappings:\n * - columns -> virtual column count\n * - colSpan -> width %\n * - colOffset -> marginLeft %\n * - columnGap -> item horizontal padding\n * - rowGap -> item vertical padding\n * - rowSpan -> minHeight approximation\n *\n * Ignored (grid-only semantics):\n * - templateAreas\n * - area\n * - gridTemplateRows\n * - gridTemplateColumns\n * - rowStart / rowEnd\n * - colStart / colEnd\n */\n\nconst DEFAULT_COLUMNS = 12;\nconst DEFAULT_ROW_HEIGHT = 56;\n\nexport const FieldWrapper = <T extends FieldValues>({\n element,\n control,\n getValues,\n setFocusedField,\n focusedField,\n children,\n grid,\n onGridChange,\n groupField,\n }: FieldWrapperProps<T>) => {\n /* ---------------- helpers (no hooks) ---------------- */\n\n const currentLayout = useRef<any>(null);\n const resolveDepPath = (dep: string, groupField?: GroupFieldConfigs<T>['groupField']): string => {\n if (!groupField || groupField.index === undefined || groupField.index < 0) return dep;\n\n const parts = dep.split(\".\");\n const {name, index} = groupField;\n\n // Must be \"<groupRef>.current.<field...>\"\n if (parts.length < 2 || parts[1] !== \"current\") return dep;\n\n // Group-relative deps are authored against the group's own id\n // (e.g. \"addresses.current.x\"). When the group is nested inside an\n // Object the live `name` is the fully-qualified dotted path\n // (e.g. \"addressGroup.addresses\"), so match the dep's first segment\n // against EITHER the full name or its trailing segment, then rebuild\n // against the fully-qualified name so useWatch hits the real path.\n const nameLast = name.includes(\".\") ? name.slice(name.lastIndexOf(\".\") + 1) : name;\n if (parts[0] !== name && parts[0] !== nameLast) return dep;\n\n return [name, String(index), ...parts.slice(2)].join(\".\");\n };\n\n const flattenPaths = (obj: any, prefix = ''): string[] =>\n Object.keys(obj ?? {}).flatMap((key) => {\n const path = prefix ? `${prefix}.${key}` : key;\n return typeof obj[key] === 'object' && obj[key] !== null\n ? [path, ...flattenPaths(obj[key], path)]\n : [path];\n });\n\n // 1. Determine which fields to watch for changes.\n const dependencies = useMemo(() => {\n const deps = new Set<string>([...(element.deps ?? []), ...(element.logic?.deps ?? [])]);\n\n if (deps.has('*')) {\n return flattenPaths(getValues());\n }\n\n return Array.from(deps).map((d) => resolveDepPath(d, groupField));\n }, [element, groupField]);\n\n\n // 2. Efficiently watch only the necessary fields.\n const watchedValues = useWatch({\n name: dependencies as any,\n compute:(fieldValue)=>{\n return fieldValue\n },\n exact:true,\n disabled:dependencies.length === 0\n });\n\n useEffect(() => {\n // console.log('watchedValues', watchedValues)\n }, [watchedValues]);\n // This single hook provides all the necessary logic and state for the field.\n const fieldLogic = useFieldLogic({element, control, getValues, isFocused: focusedField === element.id, groupField, watchedValues});\n\n const {\n field,\n isShown,\n layout,\n style:elementStyle,\n animation\n } = fieldLogic;\n const isFocused = focusedField === field.name;\n\n const onFocus = useCallback(() => setFocusedField(field.name), [setFocusedField, field.name]);\n const onBlur: React.FocusEventHandler<any> = useCallback((event) => {\n const cl = event.target['className'];\n // @ts-ignore\n if(!cl || cl.includes(`${element.id}_option_item`) || cl.includes(`p-multiselect-filter`)){\n return;\n }\n setFocusedField('');\n field.onBlur(); // Call the original onBlur from react-hook-form\n }, [setFocusedField, field.onBlur]);\n\n // Grid layout effect — MUST be before any conditional return (Rules of Hooks)\n useEffect(() => {\n if(layout.colSpan && currentLayout.current != JSON.stringify(layout)){\n const ctx = createStyleContext();\n\n const colSpan = resolveResponsiveValue<string | number>(layout.colSpan, ctx);\n\n onGridChange?.(colSpan?.toString() ?? '12')\n\n currentLayout.current = JSON.stringify(layout)\n }\n\n }, [layout]);\n\n // If the logic dictates this field should not be shown, render nothing.\n if (!isShown || !children) {\n return null;\n }\n\n\n\n // @ts-ignore\n return <Flex {...elementStyle?.container} {...animation}\n zIndex={isFocused ? 1000 : undefined}\n w={'100%'} onTouchStart={onFocus}>\n {children(fieldLogic)}\n </Flex>\n};\n"],"names":["groupField"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCO,MAAM,eAAe,CAAwB;AAAA,EACI,OAAA;AAAA,EACA,OAAA;AAAA,EACA,SAAA;AAAA,EACA,eAAA;AAAA,EACA,YAAA;AAAA,EACA,QAAA;AAAA,EACA,IAAA;AAAA,EACA,YAAA;AAAA,EACA;AACJ,CAAA,KAA4B;AAG5E,EAAA,MAAM,aAAA,GAAgB,OAAY,IAAI,CAAA;AACtC,EAAA,MAAM,cAAA,GAAiB,CAAC,GAAA,EAAaA,WAAAA,KAA4D;AAC7F,IAAA,IAAI,CAACA,eAAcA,WAAAA,CAAW,KAAA,KAAU,UAAaA,WAAAA,CAAW,KAAA,GAAQ,GAAG,OAAO,GAAA;AAElF,IAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,KAAA,CAAM,GAAG,CAAA;AAC3B,IAAA,MAAM,EAAC,IAAA,EAAM,KAAA,EAAK,GAAIA,WAAAA;AAGtB,IAAA,IAAI,MAAM,MAAA,GAAS,CAAA,IAAK,MAAM,CAAC,CAAA,KAAM,WAAW,OAAO,GAAA;AAQvD,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,QAAA,CAAS,GAAG,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,WAAA,CAAY,GAAG,CAAA,GAAI,CAAC,CAAA,GAAI,IAAA;AAC9E,IAAA,IAAI,KAAA,CAAM,CAAC,CAAA,KAAM,IAAA,IAAQ,MAAM,CAAC,CAAA,KAAM,UAAU,OAAO,GAAA;AAEvD,IAAA,OAAO,CAAC,IAAA,EAAM,MAAA,CAAO,KAAK,CAAA,EAAG,GAAG,KAAA,CAAM,KAAA,CAAM,CAAC,CAAC,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA;AAAA,EAC5D,CAAA;AAEA,EAAA,MAAM,YAAA,GAAe,CAAC,GAAA,EAAU,MAAA,GAAS,EAAA,KACrC,MAAA,CAAO,IAAA,CAAK,GAAA,IAAO,EAAE,CAAA,CAAE,OAAA,CAAQ,CAAC,GAAA,KAAQ;AACpC,IAAA,MAAM,OAAO,MAAA,GAAS,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,GAAG,CAAA,CAAA,GAAK,GAAA;AAC3C,IAAA,OAAO,OAAO,IAAI,GAAG,CAAA,KAAM,YAAY,GAAA,CAAI,GAAG,MAAM,IAAA,GAC9C,CAAC,MAAM,GAAG,YAAA,CAAa,IAAI,GAAG,CAAA,EAAG,IAAI,CAAC,CAAA,GACtC,CAAC,IAAI,CAAA;AAAA,EACf,CAAC,CAAA;AAGL,EAAA,MAAM,YAAA,GAAe,QAAQ,MAAM;AAC/B,IAAA,MAAM,IAAA,mBAAO,IAAI,GAAA,CAAY,CAAC,GAAI,OAAA,CAAQ,IAAA,IAAQ,EAAC,EAAI,GAAI,OAAA,CAAQ,KAAA,EAAO,IAAA,IAAQ,EAAG,CAAC,CAAA;AAEtF,IAAA,IAAI,IAAA,CAAK,GAAA,CAAI,GAAG,CAAA,EAAG;AACf,MAAA,OAAO,YAAA,CAAa,WAAW,CAAA;AAAA,IACnC;AAEA,IAAA,OAAO,KAAA,CAAM,IAAA,CAAK,IAAI,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,KAAM,cAAA,CAAe,CAAA,EAAG,UAAU,CAAC,CAAA;AAAA,EACpE,CAAA,EAAG,CAAC,OAAA,EAAS,UAAU,CAAC,CAAA;AAIxB,EAAA,MAAM,gBAAgB,QAAA,CAAS;AAAA,IAC3B,IAAA,EAAM,YAAA;AAAA,IACN,OAAA,EAAQ,CAAC,UAAA,KAAa;AAClB,MAAA,OAAO,UAAA;AAAA,IACX,CAAA;AAAA,IACA,KAAA,EAAM,IAAA;AAAA,IACN,QAAA,EAAS,aAAa,MAAA,KAAW;AAAA,GACpC,CAAA;AAED,EAAA,SAAA,CAAU,MAAM;AAAA,EAEhB,CAAA,EAAG,CAAC,aAAa,CAAC,CAAA;AAElB,EAAA,MAAM,UAAA,GAAa,aAAA,CAAc,EAAC,OAAA,EAAS,OAAA,EAAS,SAAA,EAAW,SAAA,EAAW,YAAA,KAAiB,OAAA,CAAQ,EAAA,EAAI,UAAA,EAAY,aAAA,EAAc,CAAA;AAEjI,EAAA,MAAM;AAAA,IACF,KAAA;AAAA,IACA,OAAA;AAAA,IACA,MAAA;AAAA,IACA,KAAA,EAAM,YAAA;AAAA,IACN;AAAA,GACJ,GAAI,UAAA;AACJ,EAAA,MAAM,SAAA,GAAY,iBAAiB,KAAA,CAAM,IAAA;AAEzC,EAAA,MAAM,OAAA,GAAU,WAAA,CAAY,MAAM,eAAA,CAAgB,KAAA,CAAM,IAAI,CAAA,EAAG,CAAC,eAAA,EAAiB,KAAA,CAAM,IAAI,CAAC,CAAA;AAC5F,EAA6C,WAAA,CAAY,CAAC,KAAA,KAAU;AAChE,IAAA,MAAM,EAAA,GAAK,KAAA,CAAM,MAAA,CAAO,WAAW,CAAA;AAEnC,IAAA,IAAG,CAAC,EAAA,IAAM,EAAA,CAAG,QAAA,CAAS,CAAA,EAAG,OAAA,CAAQ,EAAE,CAAA,YAAA,CAAc,CAAA,IAAK,EAAA,CAAG,QAAA,CAAS,CAAA,oBAAA,CAAsB,CAAA,EAAE;AACtF,MAAA;AAAA,IACJ;AACA,IAAA,eAAA,CAAgB,EAAE,CAAA;AAClB,IAAA,KAAA,CAAM,MAAA,EAAO;AAAA,EACjB,CAAA,EAAG,CAAC,eAAA,EAAiB,KAAA,CAAM,MAAM,CAAC;AAGlC,EAAA,SAAA,CAAU,MAAM;AACZ,IAAA,IAAG,OAAO,OAAA,IAAW,aAAA,CAAc,WAAW,IAAA,CAAK,SAAA,CAAU,MAAM,CAAA,EAAE;AACjE,MAAA,MAAM,MAAM,kBAAA,EAAmB;AAE/B,MAAA,MAAM,OAAA,GAAU,sBAAA,CAAwC,MAAA,CAAO,OAAA,EAAS,GAAG,CAAA;AAE3E,MAAA,YAAA,GAAe,OAAA,EAAS,QAAA,EAAS,IAAK,IAAI,CAAA;AAE1C,MAAA,aAAA,CAAc,OAAA,GAAU,IAAA,CAAK,SAAA,CAAU,MAAM,CAAA;AAAA,IACjD;AAAA,EAEJ,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAGX,EAAA,IAAI,CAAC,OAAA,IAAW,CAAC,QAAA,EAAU;AACvB,IAAA,OAAO,IAAA;AAAA,EACX;AAKA,EAAA,uBAAO,GAAA;AAAA,IAAC,IAAA;AAAA,IAAA;AAAA,MAAM,GAAG,YAAA,EAAc,SAAA;AAAA,MAAY,GAAG,SAAA;AAAA,MACjC,MAAA,EAAQ,YAAY,GAAA,GAAO,MAAA;AAAA,MAC3B,CAAA,EAAG,MAAA;AAAA,MAAQ,YAAA,EAAc,OAAA;AAAA,MACjC,mBAAS,UAAU;AAAA;AAAA,GACxB;AACJ;;;;"}
|
package/index.d.ts
CHANGED
|
@@ -1357,7 +1357,22 @@ export interface IKitsThemeContextValues {
|
|
|
1357
1357
|
* `theme.shadows[<preset>].native`.
|
|
1358
1358
|
*/
|
|
1359
1359
|
/** Preset names recognised by the cross-platform `shadow` key / prop. */
|
|
1360
|
-
|
|
1360
|
+
/**
|
|
1361
|
+
* ⛔ `up` IS A DIRECTION, NOT A SIZE, AND THAT IS WHY IT SITS OUTSIDE THE SCALE.
|
|
1362
|
+
* `xs → lg` is one axis: how far off the page a thing floats. Every one of them
|
|
1363
|
+
* casts DOWNWARD (`shadowOffset.height` ≥ 0), which is correct for a card on a
|
|
1364
|
+
* page and wrong for anything anchored to the BOTTOM of the screen — a sticky
|
|
1365
|
+
* bid footer, a bottom sheet, a tab bar. Those need the light to come from the
|
|
1366
|
+
* same place it always does and the shadow to fall UPWARD, onto the content
|
|
1367
|
+
* they are covering. Without it a bottom-anchored block reads as painted on
|
|
1368
|
+
* rather than lifted.
|
|
1369
|
+
*
|
|
1370
|
+
* ⚠️ IT IS DELIBERATELY ONE PRESET, NOT A SECOND SCALE. `up-sm`/`up-md`/`up-lg`
|
|
1371
|
+
* would double the vocabulary for a case that has exactly one job: separating a
|
|
1372
|
+
* fixed bar from what scrolls beneath it. Add sizes when a second real use
|
|
1373
|
+
* appears, not before.
|
|
1374
|
+
*/
|
|
1375
|
+
declare const SHADOW_PRESET_KEYS: readonly ["none", "xs", "sm", "md", "lg", "up"];
|
|
1361
1376
|
export type ShadowPreset = (typeof SHADOW_PRESET_KEYS)[number];
|
|
1362
1377
|
/** Type guard: is `value` one of the cross-platform shadow presets? */
|
|
1363
1378
|
declare function isShadowPreset(value: unknown): value is ShadowPreset;
|
|
@@ -1425,6 +1440,16 @@ declare const DEFAULT_NATIVE_SHADOWS: {
|
|
|
1425
1440
|
shadowRadius: number;
|
|
1426
1441
|
elevation: number;
|
|
1427
1442
|
};
|
|
1443
|
+
up: {
|
|
1444
|
+
shadowColor: string;
|
|
1445
|
+
shadowOffset: {
|
|
1446
|
+
width: number;
|
|
1447
|
+
height: number;
|
|
1448
|
+
};
|
|
1449
|
+
shadowOpacity: number;
|
|
1450
|
+
shadowRadius: number;
|
|
1451
|
+
elevation: number;
|
|
1452
|
+
};
|
|
1428
1453
|
};
|
|
1429
1454
|
/** Publish the theme's native shadow scale. Merged over the built-in defaults. */
|
|
1430
1455
|
declare function setActiveNativeShadows(scale: Record<string, INativeShadow>): void;
|
package/package.json
CHANGED
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lmb-it/kitsconcerto-native",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.252",
|
|
4
4
|
"description": "KitsConcerto — Native (React Native) UI components",
|
|
5
|
-
"main": "index.
|
|
5
|
+
"main": "index.cjs",
|
|
6
6
|
"module": "index.js",
|
|
7
7
|
"types": "index.d.ts",
|
|
8
8
|
"react-native": "index.js",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./index.d.ts",
|
|
12
|
+
"require": "./index.cjs",
|
|
13
|
+
"react-native": "./index.js",
|
|
14
|
+
"import": "./index.js",
|
|
15
|
+
"default": "./index.js"
|
|
16
|
+
},
|
|
17
|
+
"./package.json": "./package.json",
|
|
18
|
+
"./*": "./*"
|
|
19
|
+
},
|
|
9
20
|
"sideEffects": false,
|
|
10
21
|
"dependencies": {
|
|
11
22
|
"@dnd-kit/core": "^6.3.1",
|
|
@@ -523,6 +523,9 @@ const defaultTheme = {
|
|
|
523
523
|
sm: { web: "0 1px 2px rgba(15,26,28,0.04)", native: shadows.DEFAULT_NATIVE_SHADOWS.sm },
|
|
524
524
|
md: { web: "0 2px 4px rgba(15,26,28,0.05), 0 6px 16px rgba(15,26,28,0.06)", native: shadows.DEFAULT_NATIVE_SHADOWS.md },
|
|
525
525
|
lg: { web: "0 4px 16px rgba(15,26,28,0.06), 0 20px 40px rgba(15,26,28,0.08)", native: shadows.DEFAULT_NATIVE_SHADOWS.lg },
|
|
526
|
+
// ⛔ THE ONLY PRESET THAT CASTS UPWARD. For blocks anchored to the
|
|
527
|
+
// bottom of the viewport; see the note on `up` in `shadows.ts`.
|
|
528
|
+
up: { web: "0 -3px 5px rgba(15,26,28,0.05), 0 -8px 20px rgba(15,26,28,0.06)", native: shadows.DEFAULT_NATIVE_SHADOWS.up },
|
|
526
529
|
// Web-only legacy levels (no cross-platform native mapping).
|
|
527
530
|
base: "0 1px 2px rgba(15,26,28,0.04), 0 4px 12px rgba(15,26,28,0.04)",
|
|
528
531
|
// AM.shadow
|