@omnifyjp/ui 2.3.0 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-ML2VNJ7R.js → chunk-37CQS347.js} +3 -3
- package/dist/{chunk-ML2VNJ7R.js.map → chunk-37CQS347.js.map} +1 -1
- package/dist/{chunk-2OPOSDEH.js → chunk-CTNIYDQI.js} +3 -3
- package/dist/{chunk-2OPOSDEH.js.map → chunk-CTNIYDQI.js.map} +1 -1
- package/dist/{chunk-Q5KDFNRD.js → chunk-CTZOQ5BS.js} +4 -4
- package/dist/chunk-CTZOQ5BS.js.map +1 -0
- package/dist/{chunk-7HHL5OFD.js → chunk-JNLYTMN5.js} +3 -3
- package/dist/chunk-JNLYTMN5.js.map +1 -0
- package/dist/{chunk-M22SIS5Z.js → chunk-JTVWK3BC.js} +3 -3
- package/dist/{chunk-M22SIS5Z.js.map → chunk-JTVWK3BC.js.map} +1 -1
- package/dist/{chunk-BBFOFRWT.js → chunk-QHHPWBZM.js} +5 -46
- package/dist/chunk-QHHPWBZM.js.map +1 -0
- package/dist/chunk-RWMRHEWT.js +8 -0
- package/dist/chunk-RWMRHEWT.js.map +1 -0
- package/dist/chunk-SWQDP3VC.js +47 -0
- package/dist/chunk-SWQDP3VC.js.map +1 -0
- package/dist/{chunk-DB42CM3T.js → chunk-TSRLQI2O.js} +4 -4
- package/dist/chunk-TSRLQI2O.js.map +1 -0
- package/dist/{chunk-YU55YBID.js → chunk-XJSGU5XZ.js} +2 -2
- package/dist/chunk-XJSGU5XZ.js.map +1 -0
- package/dist/components/domain/calendar/calendar-event-chip.js +2 -1
- package/dist/components/domain/calendar/calendar-event-chip.js.map +1 -1
- package/dist/components/domain/calendar/calendar-event-sheet.js +2 -1
- package/dist/components/domain/calendar/calendar-event-sheet.js.map +1 -1
- package/dist/components/domain/calendar/calendar-mini.js +2 -1
- package/dist/components/domain/calendar/calendar-mini.js.map +1 -1
- package/dist/components/domain/calendar/calendar-toolbar.js +2 -1
- package/dist/components/domain/calendar/calendar-toolbar.js.map +1 -1
- package/dist/components/inputs/color-picker.js +5 -4
- package/dist/components/inputs/date-picker.js +2 -1
- package/dist/components/inputs/date-picker.js.map +1 -1
- package/dist/components/inputs/input.d.ts +1 -2
- package/dist/components/inputs/input.js +4 -3
- package/dist/components/inputs/password-input.d.ts +1 -2
- package/dist/components/inputs/password-input.js +5 -4
- package/dist/components/inputs/slug-input.js +5 -4
- package/dist/components/inputs/textarea.d.ts +1 -2
- package/dist/components/inputs/textarea.js +4 -3
- package/dist/components/inputs/time-picker.js +4 -3
- package/dist/components/inputs/time-picker.js.map +1 -1
- package/dist/components/inputs/translatable-field.d.ts +1 -1
- package/dist/components/inputs/translatable-field.js +1 -1
- package/dist/components/navigation/locale-switcher.js +3 -2
- package/dist/index.d.ts +45 -1
- package/dist/index.js +10 -8
- package/dist/providers/ui-provider.d.ts +2 -63
- package/dist/providers/ui-provider.js +2 -1
- package/dist/ui-context-D0zD0xa5.d.ts +25 -0
- package/package.json +2 -2
- package/dist/chunk-7HHL5OFD.js.map +0 -1
- package/dist/chunk-BBFOFRWT.js.map +0 -1
- package/dist/chunk-DB42CM3T.js.map +0 -1
- package/dist/chunk-Q5KDFNRD.js.map +0 -1
- package/dist/chunk-YU55YBID.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/providers/ui-provider.tsx"],"names":[],"mappings":"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","file":"chunk-BBFOFRWT.js","sourcesContent":["import { createContext, useContext, useState, useEffect, useCallback, ReactNode } from 'react';\n\n// ─── Theme ────────────────────────────────────────────────────────────────────\n\nexport type Theme = 'light' | 'dark' | 'system';\n\n// ─── Locale ───────────────────────────────────────────────────────────────────\n\nexport type LocaleCode = string;\n\n/**\n * Map of locale code → display label.\n * @example { en: 'English', vi: 'Tiếng Việt', ja: '日本語' }\n */\nexport type LocaleMap = Record<LocaleCode, string>;\n\n/** Value shape for translatable fields: locale code → string content. */\nexport type TranslatableValue = Record<LocaleCode, string>;\n\n/** Locale configuration used by UIProvider and translatable fields. */\nexport interface UILocaleConfig {\n /** Available locales. e.g. `{ en: 'English', vi: 'Tiếng Việt' }` */\n locales: LocaleMap;\n /** Locale shown by default when a translatable field is first rendered. */\n defaultLocale: LocaleCode;\n /** Locale to fall back to when the active locale has no value. */\n fallbackLocale: LocaleCode;\n}\n\n/**\n * `true` — inherit UIProvider's locale config.\n * `object` — override per-field (merged with provider config).\n */\nexport type TranslatableConfig = true | Partial<UILocaleConfig>;\n\n// ─── Context ──────────────────────────────────────────────────────────────────\n\ninterface UIContextValue {\n theme: Theme;\n setTheme: (theme: Theme) => void;\n locale: UILocaleConfig | undefined;\n currentLocale: LocaleCode;\n setLocale: (locale: LocaleCode) => void;\n dateFnsLocale: object | undefined;\n timezone: string;\n setTimezone: (tz: string) => void;\n}\n\nconst UIContext = createContext<UIContextValue | undefined>(undefined);\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction loadSavedTheme(): Theme {\n if (typeof window === 'undefined') return 'system';\n const saved = localStorage.getItem('omnify_theme');\n if (saved === 'light' || saved === 'dark' || saved === 'system') return saved;\n return 'system';\n}\n\nfunction applyTheme(theme: Theme) {\n const root = document.documentElement;\n if (theme === 'system') {\n root.classList.toggle('dark', window.matchMedia('(prefers-color-scheme: dark)').matches);\n } else {\n root.classList.toggle('dark', theme === 'dark');\n }\n}\n\n// ─── UIProvider ───────────────────────────────────────────────────────────────\n\nexport interface UIProviderProps {\n children: ReactNode;\n /**\n * Initial theme. Defaults to user's saved localStorage value or `'system'`.\n */\n defaultTheme?: Theme;\n /**\n * Available locales for translatable fields.\n * @example { en: 'English', vi: 'Tiếng Việt', ja: '日本語' }\n */\n locales?: LocaleMap;\n /**\n * Locale shown first in translatable fields.\n * Defaults to the first key in `locales`.\n */\n defaultLocale?: LocaleCode;\n /**\n * Locale used when a field has no value for the active locale.\n * Defaults to `defaultLocale`.\n */\n fallbackLocale?: LocaleCode;\n /**\n * date-fns `Locale` object used by date components (DatePicker, CalendarMini, etc.).\n * Typed as `object` to avoid importing date-fns as a direct dependency.\n *\n * @example\n * ```tsx\n * import { ja } from 'date-fns/locale';\n * <UIProvider dateFnsLocale={ja}>{children}</UIProvider>\n * ```\n */\n dateFnsLocale?: object;\n /**\n * Callback fired when the active locale changes via `setLocale`.\n * Use this to sync with i18n libraries, localStorage, etc.\n */\n onLocaleChange?: (locale: LocaleCode) => void;\n /**\n * IANA timezone string (e.g. `'Asia/Tokyo'`).\n * Defaults to the browser's local timezone.\n */\n timezone?: string;\n /**\n * Callback fired when the timezone changes via `setTimezone`.\n * Use this to sync with backend, localStorage, etc.\n */\n onTimezoneChange?: (timezone: string) => void;\n}\n\n/**\n * Root provider for @omnifyjp/ui — handles dark mode and translatable field config.\n *\n * @example\n * ```tsx\n * <UIProvider\n * locales={{ en: 'English', vi: 'Tiếng Việt', ja: '日本語' }}\n * defaultLocale=\"en\"\n * fallbackLocale=\"en\"\n * >\n * {children}\n * </UIProvider>\n * ```\n */\nexport function UIProvider({\n children,\n defaultTheme,\n locales,\n defaultLocale,\n fallbackLocale,\n dateFnsLocale,\n onLocaleChange,\n timezone: timezoneProp,\n onTimezoneChange,\n}: UIProviderProps) {\n const [theme, setThemeState] = useState<Theme>(() => defaultTheme ?? loadSavedTheme());\n\n const setTheme = useCallback((t: Theme) => setThemeState(t), []);\n\n useEffect(() => {\n localStorage.setItem('omnify_theme', theme);\n applyTheme(theme);\n }, [theme]);\n\n useEffect(() => {\n if (theme !== 'system') return;\n const mq = window.matchMedia('(prefers-color-scheme: dark)');\n const handler = () => applyTheme('system');\n mq.addEventListener('change', handler);\n return () => mq.removeEventListener('change', handler);\n }, [theme]);\n\n const firstLocale = locales ? Object.keys(locales)[0] : undefined;\n const resolvedDefaultLocale = defaultLocale ?? firstLocale ?? '';\n const locale: UILocaleConfig | undefined =\n locales && firstLocale\n ? {\n locales,\n defaultLocale: resolvedDefaultLocale,\n fallbackLocale: fallbackLocale ?? resolvedDefaultLocale,\n }\n : undefined;\n\n const [currentLocale, setCurrentLocale] = useState<LocaleCode>(\n () => resolvedDefaultLocale,\n );\n\n const setLocale = useCallback(\n (loc: LocaleCode) => {\n setCurrentLocale(loc);\n onLocaleChange?.(loc);\n },\n [onLocaleChange],\n );\n\n // Auto-set <html lang> for accessibility/SEO\n useEffect(() => {\n if (currentLocale) {\n document.documentElement.lang = currentLocale;\n }\n }, [currentLocale]);\n\n // ── Timezone ──\n const [timezone, setTimezoneState] = useState<string>(\n () => timezoneProp ?? Intl.DateTimeFormat().resolvedOptions().timeZone,\n );\n\n const setTimezone = useCallback(\n (tz: string) => {\n setTimezoneState(tz);\n onTimezoneChange?.(tz);\n },\n [onTimezoneChange],\n );\n\n // Sync when prop changes externally (e.g. Inertia page props update)\n useEffect(() => {\n if (timezoneProp !== undefined) {\n setTimezoneState(timezoneProp);\n }\n }, [timezoneProp]);\n\n return (\n <UIContext.Provider value={{ theme, setTheme, locale, currentLocale, setLocale, dateFnsLocale, timezone, setTimezone }}>\n {children}\n </UIContext.Provider>\n );\n}\n\n// ─── Hooks ────────────────────────────────────────────────────────────────────\n\n/** Access theme and setTheme. Must be inside UIProvider. */\nexport function useTheme(): { theme: Theme; setTheme: (t: Theme) => void } {\n const ctx = useContext(UIContext);\n if (!ctx) throw new Error('useTheme must be used within UIProvider');\n return { theme: ctx.theme, setTheme: ctx.setTheme };\n}\n\n/**\n * Returns the locale config from UIProvider.\n * Returns `undefined` when no `locales` prop was passed to UIProvider.\n */\nexport function useUILocales(): UILocaleConfig | undefined {\n return useContext(UIContext)?.locale;\n}\n\n/**\n * Returns the active locale state and locale config from UIProvider.\n * Must be used inside UIProvider.\n */\nexport function useLocale(): {\n currentLocale: LocaleCode;\n setLocale: (locale: LocaleCode) => void;\n locales: LocaleMap;\n defaultLocale: LocaleCode;\n fallbackLocale: LocaleCode;\n} {\n const ctx = useContext(UIContext);\n if (!ctx) throw new Error('useLocale must be used within UIProvider');\n const config = ctx.locale ?? { locales: {}, defaultLocale: '', fallbackLocale: '' };\n return {\n currentLocale: ctx.currentLocale,\n setLocale: ctx.setLocale,\n locales: config.locales,\n defaultLocale: config.defaultLocale,\n fallbackLocale: config.fallbackLocale,\n };\n}\n\n/**\n * Returns the active timezone and setter from UIProvider.\n * Must be used inside UIProvider.\n */\nexport function useTimezone(): {\n timezone: string;\n setTimezone: (tz: string) => void;\n} {\n const ctx = useContext(UIContext);\n if (!ctx) throw new Error('useTimezone must be used within UIProvider');\n return { timezone: ctx.timezone, setTimezone: ctx.setTimezone };\n}\n\n/**\n * Returns the date-fns `Locale` object from UIProvider.\n * Returns `undefined` when no `dateFnsLocale` prop was passed.\n */\nexport function useDateFnsLocale(): object | undefined {\n return useContext(UIContext)?.dateFnsLocale;\n}\n\n/**\n * Resolves the effective UILocaleConfig for a translatable field.\n * Merges inline `TranslatableConfig` with the provider's locale config.\n */\nexport function resolveTranslatableConfig(\n translatable: TranslatableConfig,\n providerLocales: UILocaleConfig | undefined,\n): UILocaleConfig | undefined {\n if (translatable === true) {\n return providerLocales;\n }\n const base = providerLocales ?? { locales: {}, defaultLocale: '', fallbackLocale: '' };\n const merged: UILocaleConfig = {\n locales: translatable.locales ?? base.locales,\n defaultLocale: translatable.defaultLocale ?? base.defaultLocale,\n fallbackLocale: translatable.fallbackLocale ?? base.fallbackLocale,\n };\n return Object.keys(merged.locales).length > 0 ? merged : undefined;\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/inputs/input.tsx"],"names":["inputRest","value","onChange"],"mappings":"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","file":"chunk-DB42CM3T.js","sourcesContent":["import * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"../../lib/utils\";\nimport { TranslatableField } from './translatable-field';\nimport { useUILocales, resolveTranslatableConfig } from '../../providers/ui-provider';\nimport type { TranslatableConfig, TranslatableValue } from '../../providers/ui-provider';\n\n// ─── Variants ─────────────────────────────────────────────────────────────────\n\nconst inputVariants = cva(\n \"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex w-full min-w-0 rounded-md border bg-input-background transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive\",\n {\n variants: {\n size: {\n xs: \"h-element-xs px-2 text-xs\",\n sm: \"h-element-sm px-2.5 text-sm\",\n default: \"h-element px-3 py-1 text-base md:text-sm\",\n lg: \"h-element-lg px-4 text-sm\",\n xl: \"h-element-xl px-4 text-base\",\n },\n },\n defaultVariants: {\n size: \"default\",\n },\n },\n);\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ntype InputSize = VariantProps<typeof inputVariants>['size'];\ntype NativeInputProps = Omit<React.ComponentProps<'input'>, 'value' | 'onChange' | 'size'>;\n\ninterface StandardInputProps extends NativeInputProps {\n size?: InputSize;\n /** Translatable mode disabled (default). */\n translatable?: never;\n value?: string;\n onChange?: React.ChangeEventHandler<HTMLInputElement>;\n}\n\ninterface TranslatableInputProps extends NativeInputProps {\n size?: InputSize;\n /**\n * Enable locale-switching tabs on this input.\n * - `true` — inherit UIProvider's locale config\n * - `object` — override locales/defaultLocale/fallbackLocale per field\n *\n * @example\n * ```tsx\n * // Uses UIProvider config\n * <Input translatable value={val} onChange={setVal} />\n *\n * // Custom per-field config\n * <Input\n * translatable={{ locales: { en: 'English', vi: 'Tiếng Việt' }, fallbackLocale: 'en' }}\n * value={val}\n * onChange={setVal}\n * />\n * ```\n */\n translatable: TranslatableConfig;\n value?: TranslatableValue;\n onChange?: (value: TranslatableValue) => void;\n /**\n * Per-locale validation errors. Truthy string = that locale is invalid.\n * The active locale's error is forwarded as `aria-invalid` on the input;\n * all locale tabs with errors show a red dot indicator.\n *\n * @example `{ en: 'Required', vi: 'Too long (120/100)' }`\n */\n errors?: Partial<Record<string, string>>;\n}\n\nexport type InputProps = StandardInputProps | TranslatableInputProps;\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\n/**\n * Text input component with multiple size variants.\n * Supports translatable mode via the `translatable` prop.\n *\n * @example\n * ```tsx\n * // Standard\n * <Input placeholder=\"Enter text...\" />\n * <Input size=\"sm\" value={val} onChange={(e) => setVal(e.target.value)} />\n *\n * // Translatable — uses UIProvider's locale config\n * <Input translatable value={val} onChange={setVal} />\n *\n * // Translatable — custom config\n * <Input\n * translatable={{ locales: { en: 'English', vi: 'Tiếng Việt' }, fallbackLocale: 'en' }}\n * value={val}\n * onChange={setVal}\n * />\n * ```\n */\nconst Input = React.forwardRef<HTMLInputElement, InputProps>(\n (props, ref) => {\n const { className, type, size, translatable, ...rest } = props as TranslatableInputProps & { type?: string; className?: string };\n\n const providerLocales = useUILocales();\n\n // ── Translatable mode ──────────────────────────────────────────────────\n if (translatable !== undefined) {\n const config = resolveTranslatableConfig(translatable, providerLocales);\n\n // Fallback: if no locale config available, render standard input\n if (!config) {\n const { value: _v, onChange: _oc, ...inputRest } = rest as TranslatableInputProps;\n return (\n <input\n type={type}\n ref={ref}\n data-slot=\"input\"\n className={cn(inputVariants({ size, className }))}\n {...(inputRest as NativeInputProps)}\n />\n );\n }\n\n const { value = {}, onChange, errors, ...inputRest } = rest as TranslatableInputProps;\n\n return (\n <TranslatableField\n config={config}\n value={value as TranslatableValue}\n onChange={onChange ?? (() => {})}\n errors={errors}\n >\n {({ value: localeValue, onChange: localeChange, fallbackPlaceholder, hasError }) => (\n <input\n type={type}\n ref={ref}\n data-slot=\"input\"\n data-translatable\n className={cn(inputVariants({ size, className }))}\n value={localeValue}\n placeholder={fallbackPlaceholder ?? (inputRest as React.InputHTMLAttributes<HTMLInputElement>).placeholder}\n onChange={(e) => localeChange(e.target.value)}\n {...(inputRest as React.InputHTMLAttributes<HTMLInputElement>)}\n aria-invalid={hasError || (inputRest as React.InputHTMLAttributes<HTMLInputElement>)['aria-invalid'] || undefined}\n />\n )}\n </TranslatableField>\n );\n }\n\n // ── Standard mode ──────────────────────────────────────────────────────\n const { value, onChange, ...inputRest } = rest as StandardInputProps;\n return (\n <input\n type={type}\n ref={ref}\n data-slot=\"input\"\n className={cn(inputVariants({ size, className }))}\n value={value}\n onChange={onChange}\n {...(inputRest as NativeInputProps)}\n />\n );\n },\n);\nInput.displayName = \"Input\";\n\nexport { Input, inputVariants };\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/inputs/textarea.tsx"],"names":["textareaRest","value","onChange"],"mappings":";;;;;;AAsDA,IAAM,aAAA,GACJ,4cAAA;AAyBF,IAAM,QAAA,GAAiB,KAAA,CAAA,UAAA;AAAA,EACrB,CAAC,OAAO,GAAA,KAAQ;AACd,IAAA,MAAM,EAAE,SAAA,EAAW,YAAA,EAAc,GAAG,MAAK,GAAI,KAAA;AAE7C,IAAA,MAAM,kBAAkB,YAAA,EAAa;AAGrC,IAAA,IAAI,iBAAiB,MAAA,EAAW;AAC9B,MAAA,MAAM,MAAA,GAAS,yBAAA,CAA0B,YAAA,EAAc,eAAe,CAAA;AAGtE,MAAA,IAAI,CAAC,MAAA,EAAQ;AACX,QAAA,MAAM,EAAE,KAAA,EAAO,EAAA,EAAI,UAAU,GAAA,EAAK,GAAGA,eAAa,GAAI,IAAA;AACtD,QAAA,uBACE,GAAA;AAAA,UAAC,UAAA;AAAA,UAAA;AAAA,YACC,GAAA;AAAA,YACA,WAAA,EAAU,UAAA;AAAA,YACV,SAAA,EAAW,EAAA,CAAG,aAAA,EAAe,SAAS,CAAA;AAAA,YACrC,GAAIA;AAAA;AAAA,SACP;AAAA,MAEJ;AAEA,MAAA,MAAM,EAAE,KAAA,EAAAC,MAAAA,GAAQ,EAAC,EAAG,UAAAC,SAAAA,EAAU,MAAA,EAAQ,GAAGF,aAAAA,EAAa,GAAI,IAAA;AAE1D,MAAA,uBACE,GAAA;AAAA,QAAC,iBAAA;AAAA,QAAA;AAAA,UACC,MAAA;AAAA,UACA,KAAA,EAAOC,MAAAA;AAAA,UACP,QAAA,EAAUC,cAAa,MAAM;AAAA,UAAC,CAAA,CAAA;AAAA,UAC9B,MAAA;AAAA,UAEC,QAAA,EAAA,CAAC,EAAE,KAAA,EAAO,WAAA,EAAa,UAAU,YAAA,EAAc,mBAAA,EAAqB,UAAS,qBAC5E,GAAA;AAAA,YAAC,UAAA;AAAA,YAAA;AAAA,cACC,GAAA;AAAA,cACA,WAAA,EAAU,UAAA;AAAA,cACV,mBAAA,EAAiB,IAAA;AAAA,cACjB,SAAA,EAAW,EAAA,CAAG,aAAA,EAAe,SAAS,CAAA;AAAA,cACtC,KAAA,EAAO,WAAA;AAAA,cACP,WAAA,EAAa,uBAAwBF,aAAAA,CAAmE,WAAA;AAAA,cACxG,UAAU,CAAC,CAAA,KAAM,YAAA,CAAa,CAAA,CAAE,OAAO,KAAK,CAAA;AAAA,cAC3C,GAAIA,aAAAA;AAAA,cACL,cAAA,EAAc,QAAA,IAAaA,aAAAA,CAAmE,cAAc,CAAA,IAAK;AAAA;AAAA;AACnH;AAAA,OAEJ;AAAA,IAEJ;AAGA,IAAA,MAAM,EAAE,KAAA,EAAO,QAAA,EAAU,GAAG,cAAa,GAAI,IAAA;AAC7C,IAAA,uBACE,GAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,WAAA,EAAU,UAAA;AAAA,QACV,SAAA,EAAW,EAAA,CAAG,aAAA,EAAe,SAAS,CAAA;AAAA,QACtC,KAAA;AAAA,QACA,QAAA;AAAA,QACC,GAAI;AAAA;AAAA,KACP;AAAA,EAEJ;AACF;AACA,QAAA,CAAS,WAAA,GAAc,UAAA","file":"chunk-Q5KDFNRD.js","sourcesContent":["import * as React from \"react\";\n\nimport { cn } from \"../../lib/utils\";\nimport { TranslatableField } from './translatable-field';\nimport { useUILocales, resolveTranslatableConfig } from '../../providers/ui-provider';\nimport type { TranslatableConfig, TranslatableValue } from '../../providers/ui-provider';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ntype NativeTextareaProps = Omit<React.ComponentProps<'textarea'>, 'value' | 'onChange'>;\n\ninterface StandardTextareaProps extends NativeTextareaProps {\n /** Translatable mode disabled (default). */\n translatable?: never;\n value?: string;\n onChange?: React.ChangeEventHandler<HTMLTextAreaElement>;\n}\n\ninterface TranslatableTextareaProps extends NativeTextareaProps {\n /**\n * Enable locale-switching tabs on this textarea.\n * - `true` — inherit UIProvider's locale config\n * - `object` — override locales/defaultLocale/fallbackLocale per field\n *\n * @example\n * ```tsx\n * // Uses UIProvider config\n * <Textarea translatable value={val} onChange={setVal} />\n *\n * // Custom per-field config\n * <Textarea\n * translatable={{ locales: { en: 'English', vi: 'Tiếng Việt' }, fallbackLocale: 'en' }}\n * value={val}\n * onChange={setVal}\n * />\n * ```\n */\n translatable: TranslatableConfig;\n value?: TranslatableValue;\n onChange?: (value: TranslatableValue) => void;\n /**\n * Per-locale validation errors. Truthy string = that locale is invalid.\n * The active locale's error is forwarded as `aria-invalid` on the textarea;\n * all locale tabs with errors show a red dot indicator.\n *\n * @example `{ en: 'Required', vi: 'Too long (120/100)' }`\n */\n errors?: Partial<Record<string, string>>;\n}\n\nexport type TextareaProps = StandardTextareaProps | TranslatableTextareaProps;\n\n// ─── Base class ───────────────────────────────────────────────────────────────\n\nconst textareaClass =\n \"resize-none border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-input-background px-3 py-2 text-base transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\";\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\n/**\n * Multi-line text input with auto-sizing via `field-sizing-content`.\n * Supports translatable mode via the `translatable` prop.\n *\n * @example\n * ```tsx\n * // Standard\n * <Textarea placeholder=\"Enter a description...\" />\n * <Textarea value={content} onChange={(e) => setContent(e.target.value)} />\n *\n * // Translatable — uses UIProvider's locale config\n * <Textarea translatable value={val} onChange={setVal} />\n *\n * // Translatable — custom config\n * <Textarea\n * translatable={{ locales: { en: 'English', vi: 'Tiếng Việt' }, fallbackLocale: 'en' }}\n * value={val}\n * onChange={setVal}\n * />\n * ```\n */\nconst Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(\n (props, ref) => {\n const { className, translatable, ...rest } = props as TranslatableTextareaProps & { className?: string };\n\n const providerLocales = useUILocales();\n\n // ── Translatable mode ──────────────────────────────────────────────────\n if (translatable !== undefined) {\n const config = resolveTranslatableConfig(translatable, providerLocales);\n\n // Fallback: if no locale config available, render standard textarea\n if (!config) {\n const { value: _v, onChange: _oc, ...textareaRest } = rest as TranslatableTextareaProps;\n return (\n <textarea\n ref={ref}\n data-slot=\"textarea\"\n className={cn(textareaClass, className)}\n {...(textareaRest as NativeTextareaProps)}\n />\n );\n }\n\n const { value = {}, onChange, errors, ...textareaRest } = rest as TranslatableTextareaProps;\n\n return (\n <TranslatableField\n config={config}\n value={value as TranslatableValue}\n onChange={onChange ?? (() => {})}\n errors={errors}\n >\n {({ value: localeValue, onChange: localeChange, fallbackPlaceholder, hasError }) => (\n <textarea\n ref={ref}\n data-slot=\"textarea\"\n data-translatable\n className={cn(textareaClass, className)}\n value={localeValue}\n placeholder={fallbackPlaceholder ?? (textareaRest as React.TextareaHTMLAttributes<HTMLTextAreaElement>).placeholder}\n onChange={(e) => localeChange(e.target.value)}\n {...(textareaRest as React.TextareaHTMLAttributes<HTMLTextAreaElement>)}\n aria-invalid={hasError || (textareaRest as React.TextareaHTMLAttributes<HTMLTextAreaElement>)['aria-invalid'] || undefined}\n />\n )}\n </TranslatableField>\n );\n }\n\n // ── Standard mode ──────────────────────────────────────────────────────\n const { value, onChange, ...textareaRest } = rest as StandardTextareaProps;\n return (\n <textarea\n ref={ref}\n data-slot=\"textarea\"\n className={cn(textareaClass, className)}\n value={value}\n onChange={onChange}\n {...(textareaRest as NativeTextareaProps)}\n />\n );\n },\n);\nTextarea.displayName = \"Textarea\";\n\nexport { Textarea };\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/inputs/translatable-field.tsx"],"names":[],"mappings":"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","file":"chunk-YU55YBID.js","sourcesContent":["import { useState, useRef, useEffect, ReactNode } from 'react';\nimport { CornerDownLeft, ChevronDown } from 'lucide-react';\nimport { cn } from '../../lib/utils';\nimport type { UILocaleConfig, LocaleCode, TranslatableValue } from '../../providers/ui-provider';\n\nexport type { TranslatableValue };\n\n// ─── Config ───────────────────────────────────────────────────────────────────\n\n/** Max locale tabs shown inline; overflow goes into a dropdown. */\nconst MAX_INLINE_TABS = 3;\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface TranslatableRenderProps {\n /** Active locale code, e.g. `'en'` */\n locale: LocaleCode;\n /** String value for the active locale */\n value: string;\n /** Call with the new string to update the active locale's value */\n onChange: (value: string) => void;\n /** Fallback value shown as placeholder when the active locale is empty */\n fallbackPlaceholder: string | undefined;\n /** True when the active locale has a truthy entry in `errors` */\n hasError: boolean;\n}\n\ninterface TranslatableFieldProps {\n config: UILocaleConfig;\n value: TranslatableValue;\n onChange: (value: TranslatableValue) => void;\n /** Render the actual input. Receives locale-scoped value/onChange. */\n children: (props: TranslatableRenderProps) => ReactNode;\n className?: string;\n /**\n * Per-locale validation errors. A truthy string for a locale code marks that\n * locale as invalid: its tab dot turns red and `hasError` is `true` in the\n * render props when that locale is active.\n *\n * @example `{ en: '', vi: 'Too long' }` — only VI has an error\n */\n errors?: Partial<Record<string, string>>;\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\n/**\n * Wraps any text input with a locale switcher tab bar.\n * Used internally by `Input` and `Textarea` when `translatable` prop is set.\n *\n * When more than 3 locales are configured, overflow locales are collapsed into\n * a dropdown button to prevent the tab bar from overflowing.\n *\n * @example\n * ```tsx\n * <TranslatableField config={localeConfig} value={val} onChange={setVal} errors={{ vi: 'Too long' }}>\n * {({ value, onChange, fallbackPlaceholder, hasError }) => (\n * <input aria-invalid={hasError || undefined} value={value} onChange={(e) => onChange(e.target.value)} />\n * )}\n * </TranslatableField>\n * ```\n */\nexport function TranslatableField({\n config,\n value,\n onChange,\n children,\n className,\n errors,\n}: TranslatableFieldProps) {\n const [activeLocale, setActiveLocale] = useState<LocaleCode>(config.defaultLocale);\n const [dropdownOpen, setDropdownOpen] = useState(false);\n const dropdownRef = useRef<HTMLDivElement>(null);\n\n // Close dropdown on outside click\n useEffect(() => {\n if (!dropdownOpen) return;\n const handler = (e: MouseEvent) => {\n if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) {\n setDropdownOpen(false);\n }\n };\n document.addEventListener('mousedown', handler);\n return () => document.removeEventListener('mousedown', handler);\n }, [dropdownOpen]);\n\n const isFallback = activeLocale !== config.fallbackLocale;\n const fallbackPlaceholder = isFallback ? (value[config.fallbackLocale] ?? undefined) : undefined;\n const hasError = !!(errors?.[activeLocale]);\n\n const handleChange = (v: string) => {\n onChange({ ...value, [activeLocale]: v });\n };\n\n // ── Build visible + overflow locale lists ──────────────────────────────────\n const localeEntries = Object.entries(config.locales) as [LocaleCode, string][];\n let visibleEntries: [LocaleCode, string][];\n let overflowEntries: [LocaleCode, string][];\n\n if (localeEntries.length <= MAX_INLINE_TABS) {\n visibleEntries = localeEntries;\n overflowEntries = [];\n } else {\n // Always keep active locale visible; fill remaining slots from the front\n const nonActive = localeEntries.filter(([code]) => code !== activeLocale);\n const visibleNonActive = nonActive.slice(0, MAX_INLINE_TABS - 1);\n const visibleCodes = new Set([...visibleNonActive.map(([c]) => c), activeLocale]);\n // Preserve original order\n visibleEntries = localeEntries.filter(([code]) => visibleCodes.has(code));\n overflowEntries = localeEntries.filter(([code]) => !visibleCodes.has(code));\n }\n\n const activeInOverflow = overflowEntries.some(([code]) => code === activeLocale);\n const overflowHasValue = overflowEntries.some(([code]) => !!(value[code] ?? ''));\n const overflowHasError = overflowEntries.some(([code]) => !!(errors?.[code]));\n\n return (\n <div className={cn('flex flex-col gap-1', className)}>\n {/* Locale tab bar */}\n <div className=\"flex items-center gap-0.5\">\n\n {/* Inline locale tabs */}\n {visibleEntries.map(([code, label]) => {\n const isActive = code === activeLocale;\n const hasValue = !!(value[code] ?? '');\n const hasLocaleError = !!(errors?.[code]);\n\n return (\n <button\n key={code}\n type=\"button\"\n title={label}\n onClick={() => setActiveLocale(code)}\n className={cn(\n 'relative px-2 py-0.5 rounded text-xs font-medium transition-colors select-none',\n isActive\n ? 'bg-primary text-primary-foreground'\n : 'text-muted-foreground hover:text-foreground hover:bg-muted',\n )}\n >\n {code.toUpperCase()}\n {/* dot — locale has a value or an error, but is not active */}\n {(hasValue || hasLocaleError) && !isActive && (\n <span className={cn(\n 'absolute -top-0.5 -right-0.5 w-1.5 h-1.5 rounded-full',\n hasLocaleError ? 'bg-destructive' : 'bg-primary',\n )} />\n )}\n </button>\n );\n })}\n\n {/* Overflow dropdown */}\n {overflowEntries.length > 0 && (\n <div ref={dropdownRef} className=\"relative\">\n <button\n type=\"button\"\n title=\"More languages\"\n onClick={() => setDropdownOpen((o) => !o)}\n className={cn(\n 'relative flex items-center gap-0.5 px-1.5 py-0.5 rounded text-xs font-medium transition-colors select-none',\n activeInOverflow\n ? 'bg-primary text-primary-foreground'\n : 'text-muted-foreground hover:text-foreground hover:bg-muted',\n )}\n >\n {activeInOverflow\n ? activeLocale.toUpperCase()\n : `+${overflowEntries.length}`}\n <ChevronDown className={cn('w-3 h-3 transition-transform', dropdownOpen && 'rotate-180')} />\n {/* dot when overflow has values/errors but active is not in overflow */}\n {(overflowHasValue || overflowHasError) && !activeInOverflow && (\n <span className={cn(\n 'absolute -top-0.5 -right-0.5 w-1.5 h-1.5 rounded-full',\n overflowHasError ? 'bg-destructive' : 'bg-primary',\n )} />\n )}\n </button>\n\n {dropdownOpen && (\n <div className=\"absolute top-full left-0 mt-1 z-10 min-w-[140px] rounded-md border border-border bg-popover shadow-md py-1\">\n {overflowEntries.map(([code, label]) => {\n const isActive = code === activeLocale;\n const hasValue = !!(value[code] ?? '');\n const hasLocaleError = !!(errors?.[code]);\n return (\n <button\n key={code}\n type=\"button\"\n onClick={() => { setActiveLocale(code); setDropdownOpen(false); }}\n className={cn(\n 'w-full flex items-center gap-2 px-3 py-1.5 text-sm transition-colors',\n isActive\n ? 'bg-primary/10 text-primary font-medium'\n : 'hover:bg-accent text-foreground',\n )}\n >\n <span className=\"text-xs font-semibold w-6 shrink-0\">{code.toUpperCase()}</span>\n <span className=\"text-xs text-muted-foreground flex-1 text-left\">{label}</span>\n {(hasValue || hasLocaleError) && (\n <span className={cn(\n 'w-1.5 h-1.5 rounded-full shrink-0',\n hasLocaleError ? 'bg-destructive' : 'bg-primary',\n )} />\n )}\n </button>\n );\n })}\n </div>\n )}\n </div>\n )}\n\n {/* Fallback hint when active locale is empty */}\n {fallbackPlaceholder && !value[activeLocale] && (\n <span data-testid=\"fallback-hint\" className=\"ml-1 flex items-center gap-0.5 text-xs text-muted-foreground\">\n <CornerDownLeft className=\"w-3 h-3\" />\n {config.fallbackLocale.toUpperCase()}\n </span>\n )}\n </div>\n\n {children({\n locale: activeLocale,\n value: value[activeLocale] ?? '',\n onChange: handleChange,\n fallbackPlaceholder,\n hasError,\n })}\n </div>\n );\n}\n"]}
|