@senestia/ui 0.1.0-beta.00c7f75

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"senestia-ui.es.js","names":[],"sources":["../src/stories/Badge.tsx","../src/stories/tokens.ts","../src/stories/InputField.tsx","../src/index.ts"],"sourcesContent":["import React from 'react';\nimport { X } from 'lucide-react';\n\n// ─── Design System Font ──────────────────────────────────────────────────────\nconst font = 'Sarabun, sans-serif';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport type BadgeColor =\n | 'Gray'\n | 'Primary'\n | 'Error'\n | 'Warning'\n | 'Success'\n | 'Blue light'\n | 'Blue'\n | 'Indigo'\n | 'Purple'\n | 'Pink'\n | 'Orange'\n | 'Gray blue';\n\nexport type BadgeSize = 'sm' | 'md' | 'lg';\n\nexport type BadgeType = 'Idle' | 'Status Dot' | 'Closable' | 'Pill color';\n\nexport interface BadgeProps {\n /** Badge label text */\n label?: string;\n /** Color variant */\n color?: BadgeColor;\n /** Size variant */\n size?: BadgeSize;\n /** Type / style variant */\n type?: BadgeType;\n /** Icon to render inside Pill color type (Lucide icon component) */\n icon?: React.ElementType;\n /** Called when close button is clicked (Closable type) */\n onClose?: () => void;\n}\n\n// ─── Color Tokens ────────────────────────────────────────────────────────────\n\nconst COLOR_TOKENS: Record<\n BadgeColor,\n { bg: string; border: string; text: string; dot: string }\n> = {\n Gray: { bg: '#f9fafb', border: '#eaecf0', text: '#06080b', dot: '#667085' },\n Primary: { bg: '#f4f6f9', border: '#e6ebf2', text: '#1e2b3b', dot: '#6c8fbe' },\n Error: { bg: '#fef0ef', border: '#fbd2d0', text: '#cc1a11', dot: '#f04438' },\n Warning: { bg: '#fff9ee', border: '#ffedcc', text: '#db8f02', dot: '#f79009' },\n Success: { bg: '#f0fdfa', border: '#d2f9f1', text: '#15a686', dot: '#12b76a' },\n 'Blue light': { bg: '#f0f9ff', border: '#b9e6fe', text: '#026aa2', dot: '#0ba5ec' },\n Blue: { bg: '#eff8ff', border: '#b2ddff', text: '#175cd3', dot: '#2e90fa' },\n Indigo: { bg: '#eef4ff', border: '#c7d7fe', text: '#3538cd', dot: '#6172f3' },\n Purple: { bg: '#f4f3ff', border: '#d9d6fe', text: '#5925dc', dot: '#7a5af8' },\n Pink: { bg: '#fef6fb', border: '#fce7f6', text: '#c11574', dot: '#ee46bc' },\n Orange: { bg: '#fef6ee', border: '#ffd6ae', text: '#b93815', dot: '#ef6820' },\n 'Gray blue':{ bg: '#f8f9fc', border: '#d5d9eb', text: '#363f72', dot: '#717bbc' },\n};\n\n// ─── Size Tokens ─────────────────────────────────────────────────────────────\n\nconst SIZE_TOKENS: Record<\n BadgeSize,\n {\n paddingX: number;\n paddingY: number;\n paddingXClose: { left: number; right: number };\n paddingXDot: { left: number; right: number };\n paddingPill: number;\n fontSize: number;\n lineHeight: number;\n dotSize: number;\n iconSize: number;\n closeSize: number;\n }\n> = {\n sm: {\n paddingX: 8, paddingY: 2,\n paddingXClose: { left: 8, right: 4 },\n paddingXDot: { left: 6, right: 8 },\n paddingPill: 5,\n fontSize: 12, lineHeight: 18,\n dotSize: 6, iconSize: 12, closeSize: 12,\n },\n md: {\n paddingX: 8, paddingY: 2,\n paddingXClose: { left: 8, right: 4 },\n paddingXDot: { left: 6, right: 8 },\n paddingPill: 6,\n fontSize: 14, lineHeight: 20,\n dotSize: 6, iconSize: 14, closeSize: 14,\n },\n lg: {\n paddingX: 12, paddingY: 4,\n paddingXClose: { left: 12, right: 6 },\n paddingXDot: { left: 8, right: 12 },\n paddingPill: 8,\n fontSize: 14, lineHeight: 20,\n dotSize: 8, iconSize: 16, closeSize: 16,\n },\n};\n\n// ─── Component ───────────────────────────────────────────────────────────────\n\nexport const Badge: React.FC<BadgeProps> = ({\n label = 'Label',\n color = 'Gray',\n size = 'sm',\n type = 'Idle',\n icon: IconComponent,\n onClose,\n}) => {\n const c = COLOR_TOKENS[color];\n const s = SIZE_TOKENS[size];\n\n // ── Shared wrapper style ──────────────────────────────────────────────────\n const baseWrapper: React.CSSProperties = {\n display: 'inline-flex',\n alignItems: 'center',\n backgroundColor: c.bg,\n border: `1px solid ${c.border}`,\n borderRadius: 16,\n boxSizing: 'border-box',\n width: 'fit-content',\n fontFamily: font,\n };\n\n // ── Text style ────────────────────────────────────────────────────────────\n const textStyle: React.CSSProperties = {\n fontSize: s.fontSize,\n lineHeight: `${s.lineHeight}px`,\n fontWeight: 500,\n color: c.text,\n whiteSpace: 'nowrap',\n fontFamily: font,\n margin: 0,\n };\n\n // ── Idle ─────────────────────────────────────────────────────────────────\n if (type === 'Idle') {\n return (\n <span\n style={{\n ...baseWrapper,\n padding: `${s.paddingY}px ${s.paddingX}px`,\n }}\n >\n <span style={textStyle}>{label}</span>\n </span>\n );\n }\n\n // ── Status Dot ────────────────────────────────────────────────────────────\n if (type === 'Status Dot') {\n return (\n <span\n style={{\n ...baseWrapper,\n gap: 4,\n paddingTop: s.paddingY,\n paddingBottom: s.paddingY,\n paddingLeft: s.paddingXDot.left,\n paddingRight: s.paddingXDot.right,\n }}\n >\n {/* Dot */}\n <span\n style={{\n display: 'inline-block',\n width: s.dotSize,\n height: s.dotSize,\n borderRadius: '50%',\n backgroundColor: c.dot,\n flexShrink: 0,\n }}\n />\n <span style={textStyle}>{label}</span>\n </span>\n );\n }\n\n // ── Closable ──────────────────────────────────────────────────────────────\n if (type === 'Closable') {\n return (\n <span\n style={{\n ...baseWrapper,\n gap: 2,\n paddingTop: s.paddingY,\n paddingBottom: s.paddingY,\n paddingLeft: s.paddingXClose.left,\n paddingRight: s.paddingXClose.right,\n }}\n >\n <span style={textStyle}>{label}</span>\n <button\n onClick={onClose}\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n background: 'none',\n border: 'none',\n padding: 2,\n borderRadius: 3,\n cursor: 'pointer',\n color: c.text,\n flexShrink: 0,\n lineHeight: 1,\n }}\n aria-label=\"Remove\"\n >\n <X size={s.closeSize} strokeWidth={2.5} />\n </button>\n </span>\n );\n }\n\n // ── Pill color (icon only) ────────────────────────────────────────────────\n if (type === 'Pill color') {\n const PillIcon = IconComponent;\n return (\n <span\n style={{\n ...baseWrapper,\n padding: s.paddingPill,\n }}\n >\n {PillIcon ? (\n <PillIcon size={s.iconSize} color={c.dot} strokeWidth={2} />\n ) : (\n // Default: colored circle dot\n <span\n style={{\n display: 'inline-block',\n width: s.iconSize,\n height: s.iconSize,\n borderRadius: '50%',\n backgroundColor: c.dot,\n }}\n />\n )}\n </span>\n );\n }\n\n return null;\n};\n","/**\n * Senestia Design System — Semantic Color Tokens\n *\n * ┌─────────────────────────────────────────────────────────────┐\n * │ Source: Figma DS-01 · Foundation → Semantic Color │\n * │ Use these tokens in components instead of raw hex values. │\n * └─────────────────────────────────────────────────────────────┘\n */\n\n// ─── Gray-Neutral ─────────────────────────────────────────────────────────────\n\nexport const grayNeutral = {\n /** Semantic/Gray-Neutral/fg/high — primary text, dark icons */\n fgHigh: '#1B1D22',\n /** Semantic/Gray-Neutral/fg/mid-on-white — label text, helper text, secondary icons */\n fgMid: '#6A6E83',\n /** Semantic/Gray-Neutral/fg/low-on-white — placeholder, disabled text */\n fgLow: '#9A9DAD',\n\n /** Semantic/Gray-Neutral/bg/white */\n bgWhite: '#FFFFFF',\n /** Semantic/Gray-Neutral/bg/lightgray — counter pill, subtle backgrounds */\n bgLightgray: '#F8F8F9',\n /** Semantic/Gray-Neutral/bg/disabled */\n bgDisabled: '#F3F4F6',\n\n /** Semantic/Gray-Neutral/border/midgray — default input border */\n borderMidgray: '#CFD1D9',\n /** Semantic/Gray-Neutral/border/light — dividers, card borders */\n borderLight: '#EBECEF',\n /** Disabled border (checkbox, input) */\n borderDisabled: '#E5EAF1',\n\n /** Gray/300 — icon separators */\n gray300: '#D0D5DD',\n /** Skeleton loading shimmer */\n bgSkeleton: '#EEF0F3',\n /** Strong disabled background (checkbox) */\n bgDisabledStrong: '#EBECEF',\n} as const;\n\n// ─── Status ───────────────────────────────────────────────────────────────────\n\nexport const status = {\n /** Error / invalid state */\n error: '#DB1439',\n /** Warning */\n warning: '#DB8F02',\n /** Success */\n success: '#15A686',\n} as const;\n\n// ─── CI Primary (Senestia brand purple) ───────────────────────────────────────\n\nexport const ciPrimary = {\n fgLow: '#C4B3F6',\n fgHigh: '#802CEB',\n bgLow: '#D4C9F6',\n bgMid: '#802CEB',\n bgHigh: '#5A1DA9',\n borderMid: '#996CF3',\n borderHigh: '#802CEB',\n} as const;\n\n// ─── OE Theme (OE brand blue) ─────────────────────────────────────────────────\n\nexport const oePrimary = {\n fgLow: '#5273A1',\n fgHigh: '#3A5274',\n bgLowest: '#EFF2F6', // selected item background in pickers\n bgLow: '#6E90BF',\n bgMid: '#5273A1',\n bgHigh: '#3A5274',\n borderLowest:'#C6D2E3', // focused item border\n borderLow: '#9CB3D2',\n borderMid: '#5273A1',\n /** Primary/300 — supporting/helper text on blue-tinted UI */\n p300: '#809BBF',\n /** Indigo/300 — \"Set to now\" link color */\n linkBlue: '#3C5BD5',\n} as const;\n\n// ─── Interactive states ───────────────────────────────────────────────────────\n\nexport const interactive = {\n /** Active / focused border */\n activeBorder: oePrimary.fgLow, // #5273A1\n /** Hover border */\n hoverBorder: '#3B82F6',\n /** Focus ring shadow */\n focusRing: '#D7DFEA',\n /** Invalid border */\n invalidBorder: status.error,\n} as const;\n\n// ─── Padding tokens (PD) ─────────────────────────────────────────────────────\n\nexport const padding = {\n 'PD-0': 0,\n 'PD-1': 1,\n 'PD-2': 2,\n 'PD-3': 4,\n 'PD-4': 6,\n 'PD-5': 8,\n 'PD-6': 12,\n 'PD-7': 16,\n 'PD-8': 24,\n 'PD-9': 32,\n 'PD-10': 40,\n 'PD-11': 48,\n 'PD-12': 56,\n 'PD-13': 64,\n 'PD-14': 72,\n 'PD-15': 80,\n} as const;\n\n// ─── Corner Radius tokens (CR) ───────────────────────────────────────────────\n\nexport const radius = {\n 'CR-0': 0,\n 'CR-1': 2,\n 'CR-2': 4,\n 'CR-3': 8,\n 'CR-4': 12,\n 'CR-5': 16,\n 'CR-6': 20,\n 'CR-7': 24,\n 'CR-8': 32,\n 'CR-9': 48,\n 'CR-10': 100,\n} as const;\n\n// ─── Typography ───────────────────────────────────────────────────────────────\n\nexport const font = 'Sarabun, sans-serif';\n","import React, { useState } from 'react';\nimport { Circle, Info, XCircle, Search, Eye } from 'lucide-react';\nimport { grayNeutral, status, interactive, font } from './tokens';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport type InputFieldState =\n | 'Idle'\n | 'Idle hover'\n | 'Active'\n | 'Idle invalid'\n | 'Idle invalid hover'\n | 'Idle invalid active'\n | 'Filled'\n | 'Filled hover'\n | 'Filled active'\n | 'Filled invalid'\n | 'Filled invalid hover'\n | 'Filled invalid active'\n | 'Disable'\n | 'Keyboard focus'\n | 'Skeleton';\n\nexport type InputFieldSize = 'sm' | 'md' | 'lg';\n\nexport type InputFieldTrailingIcon = 'clear' | 'password' | 'search';\n\nexport type InputFieldCounter = {\n value: number;\n max: number;\n};\n\nexport interface InputFieldProps {\n /** Label text displayed above the input */\n label: string;\n /** Show required asterisk (*) */\n required?: boolean;\n /** Remark text next to label e.g. \"(optional)\" */\n labelRemark?: string;\n /** Placeholder text */\n placeholder?: string;\n /** Current value */\n value?: string;\n /** Called when value changes */\n onChange?: (value: string) => void;\n /** Helper text below the input (hidden when invalid) */\n helperText?: string;\n /** Error message below the input (shown when invalid state) */\n errorText?: string;\n /** Visual state from Figma */\n state?: InputFieldState;\n /** Input size — affects height, font, and padding */\n size?: InputFieldSize;\n /** Leading dropdown prefix text */\n leadingDropdownText?: string;\n /**\n * Leading icon inside the input (left side).\n * - `true` → default Circle icon\n * - `React.ElementType` → custom Lucide icon\n * - `false/undefined` → no icon\n */\n leadingIcon?: React.ElementType | boolean;\n /** Counter pill e.g. `{ value: 0, max: 20 }` */\n counter?: InputFieldCounter;\n /** Unit text inside the input */\n unit?: string;\n /** Single trailing icon */\n trailingIcon?: InputFieldTrailingIcon;\n /** Multiple trailing icons (overrides trailingIcon) */\n trailingIcons?: InputFieldTrailingIcon[];\n /** Show separator between trailing icons */\n trailingIconSeparator?: boolean;\n}\n\n// ─── Design tokens (from Figma) ──────────────────────────────────────────────\n\n/**\n * Size tokens — matched to Figma nodes:\n * sm (Sm-32): label C5 12/18 · input C4 14/20 · helper C5 12/18\n * md (Md-40): label C4 14/20 · input C3 16/24 · helper C4 14/20\n * lg (Lg-48): label C3 16/24 · input C3 16/24 · helper C4 14/20\n *\n * Spacing (per Figma sm node — gap-4 between all elements):\n * labelGap = gap between label row and input box\n * helperGap = gap between input box and helper/error text\n */\nconst sizeTokens = {\n sm: {\n inputHeight: 32,\n labelFontSize: 12, labelLineHeight: 18,\n inputFontSize: 14, inputLineHeight: 20,\n helperFontSize: 12, helperLineHeight: 18,\n paddingX: 12,\n iconSize: 16,\n labelIconSize: 12,\n labelGap: 4, // label row → input\n helperGap: 4, // input → helper text\n leadingPl: 8, leadingPr: 4, leadingPy: 6,\n chevronSize: 14,\n counterPadding: '1px 4px',\n counterRadius: 4,\n counterFontSize: 12, counterLineHeight: 18,\n skeletonLabelH: 18, skeletonInputH: 32,\n },\n md: {\n inputHeight: 40,\n labelFontSize: 14, labelLineHeight: 20,\n inputFontSize: 16, inputLineHeight: 24,\n helperFontSize: 14, helperLineHeight: 20,\n paddingX: 16,\n iconSize: 20,\n labelIconSize: 14,\n labelGap: 6, // label row → input\n helperGap: 4, // input → helper text\n leadingPl: 12, leadingPr: 8, leadingPy: 8,\n chevronSize: 16,\n counterPadding: '2px 8px',\n counterRadius: 8,\n counterFontSize: 14, counterLineHeight: 20,\n skeletonLabelH: 20, skeletonInputH: 40,\n },\n lg: {\n inputHeight: 48,\n labelFontSize: 16, labelLineHeight: 24,\n inputFontSize: 16, inputLineHeight: 24,\n helperFontSize: 14, helperLineHeight: 20,\n paddingX: 16,\n iconSize: 20,\n labelIconSize: 16,\n labelGap: 8, // label row → input\n helperGap: 6, // input → helper text\n leadingPl: 16, leadingPr: 8, leadingPy: 12,\n chevronSize: 16,\n counterPadding: '2px 8px',\n counterRadius: 8,\n counterFontSize: 14, counterLineHeight: 20,\n skeletonLabelH: 24, skeletonInputH: 48,\n },\n} as const;\n\n// ─── State helpers ────────────────────────────────────────────────────────────\n\nconst focusRingStyle = `0 0 0 2px ${interactive.focusRing}`;\n\nconst getStateFlags = (state: InputFieldState | undefined) => {\n const s = state ?? 'Idle';\n const isInvalid = s.includes('invalid');\n const isHover = s.includes('hover');\n const isActive = s === 'Active' || s === 'Keyboard focus' || s.endsWith('active');\n const isDisabled = s === 'Disable';\n const isSkeleton = s === 'Skeleton';\n const isFilled = s.includes('Filled');\n const isKeyboardFocus = s === 'Keyboard focus';\n return { s, isInvalid, isHover, isActive, isDisabled, isSkeleton, isFilled, isKeyboardFocus };\n};\n\n// ─── Trailing icon map → Lucide ───────────────────────────────────────────────\n\nconst TRAILING_ICON_MAP = {\n clear: XCircle,\n search: Search,\n password: Eye,\n} as const;\n\n// ─── Component ───────────────────────────────────────────────────────────────\n\nexport const InputField = ({\n label,\n required = false,\n labelRemark,\n placeholder = 'Placeholder',\n value,\n onChange: _onChange,\n helperText,\n errorText,\n state = 'Idle',\n size = 'md',\n leadingDropdownText,\n leadingIcon,\n counter,\n unit,\n trailingIcon,\n trailingIcons,\n trailingIconSeparator = false,\n}: InputFieldProps) => {\n const [internalValue, setInternalValue] = useState(value ?? '');\n\n const handleChange = (val: string) => {\n setInternalValue(val);\n _onChange?.(val);\n };\n\n const { isInvalid, isHover, isActive, isDisabled, isSkeleton, isKeyboardFocus } =\n getStateFlags(state);\n\n const t = sizeTokens[size];\n\n // ── Colors — mapped to Semantic/Gray-Neutral tokens ─────────────────────\n const borderColor = isInvalid\n ? interactive.invalidBorder // status.error #DB1439\n : isActive || isKeyboardFocus\n ? interactive.activeBorder // oePrimary.fgLow #5273A1\n : isHover\n ? interactive.hoverBorder // #3B82F6\n : grayNeutral.borderMidgray; // #CFD1D9\n\n const background = isDisabled\n ? grayNeutral.bgDisabled // #F3F4F6\n : isHover\n ? grayNeutral.bgLightgray // #F8F8F9\n : grayNeutral.bgWhite; // #FFFFFF\n\n // Label text → fg/mid-on-white (disabled → fg/low)\n const labelColor = isDisabled ? grayNeutral.fgLow : grayNeutral.fgMid;\n\n // Typed value → fg/high (disabled → fg/low)\n const valueColor = isDisabled ? grayNeutral.fgLow : grayNeutral.fgHigh; // #1B1D22\n\n // ── Icon colors (Figma: Semantic/Gray-Neutral/fg/high for input icons) ───\n // Input leading/trailing icons → fg/high (#1B1D22)\n const inputIconColor = isDisabled ? grayNeutral.fgLow : grayNeutral.fgHigh;\n // Label info icon → fg/mid (same as label text, #6A6E83)\n const labelIconColor = isDisabled ? grayNeutral.fgLow : grayNeutral.fgMid;\n\n const helperColor = isInvalid ? status.error : grayNeutral.fgMid; // #6A6E83\n\n // ── Skeleton ─────────────────────────────────────────────────────────────\n if (isSkeleton) {\n return (\n <div style={{ width: 320, display: 'flex', flexDirection: 'column', gap: t.labelGap }}>\n <div style={{ height: t.skeletonLabelH, background: grayNeutral.bgSkeleton, borderRadius: 6 }} />\n <div style={{ height: t.skeletonInputH, background: grayNeutral.bgSkeleton, border: `1px solid ${grayNeutral.bgSkeleton}`, borderRadius: 24 }} />\n </div>\n );\n }\n\n // ── Leading icon resolve ──────────────────────────────────────────────────\n // true → Circle (Figma default \"Icon/circle\"), component → custom, false → none\n const LeadingIconComponent: React.ElementType | null =\n leadingIcon === true\n ? Circle\n : leadingIcon\n ? (leadingIcon as React.ElementType)\n : null;\n\n // ── Trailing icons ────────────────────────────────────────────────────────\n const iconsToRender: InputFieldTrailingIcon[] =\n trailingIcons?.length ? trailingIcons : trailingIcon ? [trailingIcon] : [];\n\n // ── Text below input ──────────────────────────────────────────────────────\n const showText = isInvalid ? errorText : helperText;\n\n // ── Shared font style ─────────────────────────────────────────────────────\n const baseFont: React.CSSProperties = { fontFamily: font, fontWeight: 500 };\n\n return (\n /*\n * Outer wrapper — two-level gap structure matching Figma:\n * Level 1 (helperGap): separates [label + input block] from [helper text]\n * Level 2 (labelGap): separates [label row] from [input box]\n */\n <div style={{ width: 320, display: 'flex', flexDirection: 'column', gap: t.helperGap }}>\n\n {/* ── Level 1: label + input block ────────────────────────────────── */}\n <div style={{ display: 'flex', flexDirection: 'column', gap: t.labelGap, width: '100%' }}>\n\n {/* Label row */}\n <div style={{ display: 'flex', alignItems: 'center', gap: 4, width: '100%' }}>\n {/* Left: label text + info icon */}\n <div style={{ display: 'flex', flex: '1 0 0', alignItems: 'center', gap: t.labelIconSize === 12 ? 4 : 6, minWidth: 0 }}>\n <span\n style={{\n ...baseFont,\n fontSize: t.labelFontSize,\n lineHeight: `${t.labelLineHeight}px`,\n color: labelColor,\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n }}\n >\n {label}\n </span>\n {/* Info icon — fg/mid-on-white (same shade as label text) */}\n <Info\n size={t.labelIconSize}\n color={labelIconColor}\n strokeWidth={1.8}\n style={{ flexShrink: 0 }}\n />\n </div>\n\n {/* Right: remark or required marker */}\n {labelRemark && (\n <span\n style={{\n ...baseFont,\n fontSize: t.labelFontSize,\n lineHeight: `${t.labelLineHeight}px`,\n color: labelColor,\n whiteSpace: 'nowrap',\n flexShrink: 0,\n }}\n >\n {labelRemark}\n </span>\n )}\n {required && (\n <span style={{ color: status.error, fontWeight: 800, flexShrink: 0 }}>*</span>\n )}\n </div>\n\n {/* Input box */}\n <div\n style={{\n background,\n border: `1px solid ${borderColor}`,\n height: t.inputHeight,\n width: '100%',\n borderRadius: 24,\n display: 'flex',\n alignItems: leadingDropdownText ? 'stretch' : 'center',\n ...(leadingDropdownText ? {} : { gap: 8, padding: `0 ${t.paddingX}px` }),\n overflow: 'hidden',\n boxShadow: isActive || isKeyboardFocus ? focusRingStyle : 'none',\n opacity: isDisabled ? 0.7 : 1,\n boxSizing: 'border-box',\n }}\n >\n {/* Leading: dropdown prefix OR icon */}\n {leadingDropdownText ? (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 4,\n background: grayNeutral.bgWhite,\n padding: `${t.leadingPy}px ${t.leadingPr}px ${t.leadingPy}px ${t.leadingPl}px`,\n flexShrink: 0,\n }}\n >\n <span\n style={{\n ...baseFont,\n fontSize: t.inputFontSize,\n lineHeight: `${t.inputLineHeight}px`,\n color: grayNeutral.fgHigh,\n whiteSpace: 'nowrap',\n }}\n >\n {leadingDropdownText}\n </span>\n <svg width={t.chevronSize} height={t.chevronSize} viewBox=\"0 0 16 16\" fill=\"none\">\n <path d=\"M4 6L8 10L12 6\" stroke={grayNeutral.fgHigh} strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n </div>\n ) : LeadingIconComponent ? (\n /* Leading icon in input → fg/high (#1B1D22) per Figma */\n <LeadingIconComponent\n size={t.iconSize}\n color={inputIconColor}\n strokeWidth={1.6}\n style={{ flexShrink: 0 }}\n />\n ) : null}\n\n {/* Text + accessories area */}\n <div\n style={{\n flex: '1 0 0',\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n height: '100%',\n minWidth: 0,\n ...(leadingDropdownText\n ? { borderLeft: `1px solid ${grayNeutral.borderLight}`, padding: `0 12px` }\n : {}),\n }}\n >\n {/* Input */}\n <input\n type=\"text\"\n value={internalValue}\n onChange={e => handleChange(e.target.value)}\n placeholder={placeholder}\n disabled={isDisabled}\n style={{\n ...baseFont,\n flex: '1 0 0',\n border: 'none',\n outline: 'none',\n background: 'transparent',\n fontSize: t.inputFontSize,\n lineHeight: `${t.inputLineHeight}px`,\n color: valueColor,\n width: '100%',\n minWidth: 0,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n cursor: isDisabled ? 'not-allowed' : 'text',\n }}\n />\n\n {/* Counter pill */}\n {counter && (\n <div\n style={{\n background: grayNeutral.bgLightgray,\n display: 'flex',\n gap: 2,\n alignItems: 'center',\n justifyContent: 'center',\n padding: t.counterPadding,\n borderRadius: t.counterRadius,\n flexShrink: 0,\n opacity: isDisabled ? 0.7 : 1,\n }}\n >\n <span\n style={{\n ...baseFont,\n fontSize: t.counterFontSize,\n lineHeight: `${t.counterLineHeight}px`,\n color: grayNeutral.fgHigh,\n }}\n >\n {counter.value}\n </span>\n <span\n style={{\n ...baseFont,\n fontSize: t.counterFontSize,\n lineHeight: `${t.counterLineHeight}px`,\n color: grayNeutral.fgMid,\n }}\n >\n / {counter.max}\n </span>\n </div>\n )}\n\n {/* Unit text */}\n {unit && (\n <span\n style={{\n ...baseFont,\n fontSize: t.inputFontSize,\n lineHeight: `${t.inputLineHeight}px`,\n color: grayNeutral.fgHigh,\n whiteSpace: 'nowrap',\n flexShrink: 0,\n maxWidth: 64,\n overflow: 'hidden',\n }}\n >\n {unit}\n </span>\n )}\n\n {/* Trailing icons */}\n {iconsToRender.length > 0 && (\n <div style={{ display: 'flex', alignItems: 'center', gap: 4, flexShrink: 0 }}>\n {iconsToRender.map((ic, idx) => {\n const TrailingIcon = TRAILING_ICON_MAP[ic];\n return (\n <React.Fragment key={`${ic}-${idx}`}>\n {trailingIconSeparator && idx > 0 && (\n <div\n style={{\n width: 1,\n height: t.iconSize,\n background: grayNeutral.gray300,\n flexShrink: 0,\n alignSelf: 'center',\n }}\n />\n )}\n {/* Trailing icon → fg/high (#1B1D22) per Figma */}\n <TrailingIcon\n size={t.iconSize}\n color={inputIconColor}\n strokeWidth={1.6}\n style={{ flexShrink: 0 }}\n />\n </React.Fragment>\n );\n })}\n </div>\n )}\n </div>\n </div>\n </div>\n\n {/* ── Level 1: helper / error text ────────────────────────────────── */}\n {/*\n * No extra margin here — the outer helperGap (4px sm / 4px md / 6px lg)\n * from the flex container already provides the correct spacing from Figma.\n */}\n {showText && (\n <p\n style={{\n ...baseFont,\n margin: 0,\n fontSize: t.helperFontSize,\n lineHeight: `${t.helperLineHeight}px`,\n color: helperColor,\n width: '100%',\n }}\n >\n {showText}\n </p>\n )}\n </div>\n );\n};\n","// ─── Senestia Design System — Public API ─────────────────────────────────────\n//\n// import { Badge, InputField, tokens } from '@senestia/ui'\n//\n// ─────────────────────────────────────────────────────────────────────────────\n\n// ── Components ────────────────────────────────────────────────────────────────\nexport { Badge } from './stories/Badge';\nexport { InputField } from './stories/InputField';\n\n// ── Component types ───────────────────────────────────────────────────────────\nexport type { BadgeProps, BadgeColor, BadgeSize, BadgeType } from './stories/Badge';\nexport type {\n InputFieldProps,\n InputFieldState,\n InputFieldSize,\n InputFieldTrailingIcon,\n InputFieldCounter,\n} from './stories/InputField';\n\n// ── Design Tokens ─────────────────────────────────────────────────────────────\nexport {\n // Colors\n grayNeutral,\n status,\n ciPrimary,\n oePrimary,\n interactive,\n // Spacing\n padding,\n radius,\n // Typography\n font,\n} from './stories/tokens';\n\n// ── Tokens namespace (for tree-shaking friendly usage) ────────────────────────\nimport * as _tokens from './stories/tokens';\nexport const tokens = _tokens;\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ (function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require(`react`),require(`lucide-react`),require(`react/jsx-runtime`)):typeof define==`function`&&define.amd?define([`exports`,`react`,`lucide-react`,`react/jsx-runtime`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.SenestiaUI={},e.React,e.LucideReact,e.react_jsx_runtime))})(this,function(e,t,n,r){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var i=Object.create,a=Object.defineProperty,o=Object.getOwnPropertyDescriptor,s=Object.getOwnPropertyNames,c=Object.getPrototypeOf,l=Object.prototype.hasOwnProperty,u=(e,t)=>{let n={};for(var r in e)a(n,r,{get:e[r],enumerable:!0});return t||a(n,Symbol.toStringTag,{value:`Module`}),n},d=(e,t,n,r)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var i=s(t),c=0,u=i.length,d;c<u;c++)d=i[c],!l.call(e,d)&&d!==n&&a(e,d,{get:(e=>t[e]).bind(null,d),enumerable:!(r=o(t,d))||r.enumerable});return e};t=((e,t,n)=>(n=e==null?{}:i(c(e)),d(t||!e||!e.__esModule?a(n,`default`,{value:e,enumerable:!0}):n,e)))(t);var f=`Sarabun, sans-serif`,p={Gray:{bg:`#f9fafb`,border:`#eaecf0`,text:`#06080b`,dot:`#667085`},Primary:{bg:`#f4f6f9`,border:`#e6ebf2`,text:`#1e2b3b`,dot:`#6c8fbe`},Error:{bg:`#fef0ef`,border:`#fbd2d0`,text:`#cc1a11`,dot:`#f04438`},Warning:{bg:`#fff9ee`,border:`#ffedcc`,text:`#db8f02`,dot:`#f79009`},Success:{bg:`#f0fdfa`,border:`#d2f9f1`,text:`#15a686`,dot:`#12b76a`},"Blue light":{bg:`#f0f9ff`,border:`#b9e6fe`,text:`#026aa2`,dot:`#0ba5ec`},Blue:{bg:`#eff8ff`,border:`#b2ddff`,text:`#175cd3`,dot:`#2e90fa`},Indigo:{bg:`#eef4ff`,border:`#c7d7fe`,text:`#3538cd`,dot:`#6172f3`},Purple:{bg:`#f4f3ff`,border:`#d9d6fe`,text:`#5925dc`,dot:`#7a5af8`},Pink:{bg:`#fef6fb`,border:`#fce7f6`,text:`#c11574`,dot:`#ee46bc`},Orange:{bg:`#fef6ee`,border:`#ffd6ae`,text:`#b93815`,dot:`#ef6820`},"Gray blue":{bg:`#f8f9fc`,border:`#d5d9eb`,text:`#363f72`,dot:`#717bbc`}},m={sm:{paddingX:8,paddingY:2,paddingXClose:{left:8,right:4},paddingXDot:{left:6,right:8},paddingPill:5,fontSize:12,lineHeight:18,dotSize:6,iconSize:12,closeSize:12},md:{paddingX:8,paddingY:2,paddingXClose:{left:8,right:4},paddingXDot:{left:6,right:8},paddingPill:6,fontSize:14,lineHeight:20,dotSize:6,iconSize:14,closeSize:14},lg:{paddingX:12,paddingY:4,paddingXClose:{left:12,right:6},paddingXDot:{left:8,right:12},paddingPill:8,fontSize:14,lineHeight:20,dotSize:8,iconSize:16,closeSize:16}},h=({label:e=`Label`,color:t=`Gray`,size:i=`sm`,type:a=`Idle`,icon:o,onClose:s})=>{let c=p[t],l=m[i],u={display:`inline-flex`,alignItems:`center`,backgroundColor:c.bg,border:`1px solid ${c.border}`,borderRadius:16,boxSizing:`border-box`,width:`fit-content`,fontFamily:f},d={fontSize:l.fontSize,lineHeight:`${l.lineHeight}px`,fontWeight:500,color:c.text,whiteSpace:`nowrap`,fontFamily:f,margin:0};if(a===`Idle`)return(0,r.jsx)(`span`,{style:{...u,padding:`${l.paddingY}px ${l.paddingX}px`},children:(0,r.jsx)(`span`,{style:d,children:e})});if(a===`Status Dot`)return(0,r.jsxs)(`span`,{style:{...u,gap:4,paddingTop:l.paddingY,paddingBottom:l.paddingY,paddingLeft:l.paddingXDot.left,paddingRight:l.paddingXDot.right},children:[(0,r.jsx)(`span`,{style:{display:`inline-block`,width:l.dotSize,height:l.dotSize,borderRadius:`50%`,backgroundColor:c.dot,flexShrink:0}}),(0,r.jsx)(`span`,{style:d,children:e})]});if(a===`Closable`)return(0,r.jsxs)(`span`,{style:{...u,gap:2,paddingTop:l.paddingY,paddingBottom:l.paddingY,paddingLeft:l.paddingXClose.left,paddingRight:l.paddingXClose.right},children:[(0,r.jsx)(`span`,{style:d,children:e}),(0,r.jsx)(`button`,{onClick:s,style:{display:`inline-flex`,alignItems:`center`,justifyContent:`center`,background:`none`,border:`none`,padding:2,borderRadius:3,cursor:`pointer`,color:c.text,flexShrink:0,lineHeight:1},"aria-label":`Remove`,children:(0,r.jsx)(n.X,{size:l.closeSize,strokeWidth:2.5})})]});if(a===`Pill color`){let e=o;return(0,r.jsx)(`span`,{style:{...u,padding:l.paddingPill},children:e?(0,r.jsx)(e,{size:l.iconSize,color:c.dot,strokeWidth:2}):(0,r.jsx)(`span`,{style:{display:`inline-block`,width:l.iconSize,height:l.iconSize,borderRadius:`50%`,backgroundColor:c.dot}})})}return null},g=u({ciPrimary:()=>y,font:()=>w,grayNeutral:()=>_,interactive:()=>x,oePrimary:()=>b,padding:()=>S,radius:()=>C,status:()=>v}),_={fgHigh:`#1B1D22`,fgMid:`#6A6E83`,fgLow:`#9A9DAD`,bgWhite:`#FFFFFF`,bgLightgray:`#F8F8F9`,bgDisabled:`#F3F4F6`,borderMidgray:`#CFD1D9`,borderLight:`#EBECEF`,borderDisabled:`#E5EAF1`,gray300:`#D0D5DD`,bgSkeleton:`#EEF0F3`,bgDisabledStrong:`#EBECEF`},v={error:`#DB1439`,warning:`#DB8F02`,success:`#15A686`},y={fgLow:`#C4B3F6`,fgHigh:`#802CEB`,bgLow:`#D4C9F6`,bgMid:`#802CEB`,bgHigh:`#5A1DA9`,borderMid:`#996CF3`,borderHigh:`#802CEB`},b={fgLow:`#5273A1`,fgHigh:`#3A5274`,bgLowest:`#EFF2F6`,bgLow:`#6E90BF`,bgMid:`#5273A1`,bgHigh:`#3A5274`,borderLowest:`#C6D2E3`,borderLow:`#9CB3D2`,borderMid:`#5273A1`,p300:`#809BBF`,linkBlue:`#3C5BD5`},x={activeBorder:b.fgLow,hoverBorder:`#3B82F6`,focusRing:`#D7DFEA`,invalidBorder:v.error},S={"PD-0":0,"PD-1":1,"PD-2":2,"PD-3":4,"PD-4":6,"PD-5":8,"PD-6":12,"PD-7":16,"PD-8":24,"PD-9":32,"PD-10":40,"PD-11":48,"PD-12":56,"PD-13":64,"PD-14":72,"PD-15":80},C={"CR-0":0,"CR-1":2,"CR-2":4,"CR-3":8,"CR-4":12,"CR-5":16,"CR-6":20,"CR-7":24,"CR-8":32,"CR-9":48,"CR-10":100},w=`Sarabun, sans-serif`,T={sm:{inputHeight:32,labelFontSize:12,labelLineHeight:18,inputFontSize:14,inputLineHeight:20,helperFontSize:12,helperLineHeight:18,paddingX:12,iconSize:16,labelIconSize:12,labelGap:4,helperGap:4,leadingPl:8,leadingPr:4,leadingPy:6,chevronSize:14,counterPadding:`1px 4px`,counterRadius:4,counterFontSize:12,counterLineHeight:18,skeletonLabelH:18,skeletonInputH:32},md:{inputHeight:40,labelFontSize:14,labelLineHeight:20,inputFontSize:16,inputLineHeight:24,helperFontSize:14,helperLineHeight:20,paddingX:16,iconSize:20,labelIconSize:14,labelGap:6,helperGap:4,leadingPl:12,leadingPr:8,leadingPy:8,chevronSize:16,counterPadding:`2px 8px`,counterRadius:8,counterFontSize:14,counterLineHeight:20,skeletonLabelH:20,skeletonInputH:40},lg:{inputHeight:48,labelFontSize:16,labelLineHeight:24,inputFontSize:16,inputLineHeight:24,helperFontSize:14,helperLineHeight:20,paddingX:16,iconSize:20,labelIconSize:16,labelGap:8,helperGap:6,leadingPl:16,leadingPr:8,leadingPy:12,chevronSize:16,counterPadding:`2px 8px`,counterRadius:8,counterFontSize:14,counterLineHeight:20,skeletonLabelH:24,skeletonInputH:48}},E=`0 0 0 2px ${x.focusRing}`,D=e=>{let t=e??`Idle`;return{s:t,isInvalid:t.includes(`invalid`),isHover:t.includes(`hover`),isActive:t===`Active`||t===`Keyboard focus`||t.endsWith(`active`),isDisabled:t===`Disable`,isSkeleton:t===`Skeleton`,isFilled:t.includes(`Filled`),isKeyboardFocus:t===`Keyboard focus`}},O={clear:n.XCircle,search:n.Search,password:n.Eye},k=({label:e,required:i=!1,labelRemark:a,placeholder:o=`Placeholder`,value:s,onChange:c,helperText:l,errorText:u,state:d=`Idle`,size:f=`md`,leadingDropdownText:p,leadingIcon:m,counter:h,unit:g,trailingIcon:y,trailingIcons:b,trailingIconSeparator:S=!1})=>{let[C,k]=(0,t.useState)(s??``),A=e=>{k(e),c?.(e)},{isInvalid:j,isHover:M,isActive:N,isDisabled:P,isSkeleton:F,isKeyboardFocus:I}=D(d),L=T[f],R=j?x.invalidBorder:N||I?x.activeBorder:M?x.hoverBorder:_.borderMidgray,z=P?_.bgDisabled:M?_.bgLightgray:_.bgWhite,B=P?_.fgLow:_.fgMid,V=P?_.fgLow:_.fgHigh,H=P?_.fgLow:_.fgHigh,U=P?_.fgLow:_.fgMid,W=j?v.error:_.fgMid;if(F)return(0,r.jsxs)(`div`,{style:{width:320,display:`flex`,flexDirection:`column`,gap:L.labelGap},children:[(0,r.jsx)(`div`,{style:{height:L.skeletonLabelH,background:_.bgSkeleton,borderRadius:6}}),(0,r.jsx)(`div`,{style:{height:L.skeletonInputH,background:_.bgSkeleton,border:`1px solid ${_.bgSkeleton}`,borderRadius:24}})]});let G=m===!0?n.Circle:m||null,K=b?.length?b:y?[y]:[],q=j?u:l,J={fontFamily:w,fontWeight:500};return(0,r.jsxs)(`div`,{style:{width:320,display:`flex`,flexDirection:`column`,gap:L.helperGap},children:[(0,r.jsxs)(`div`,{style:{display:`flex`,flexDirection:`column`,gap:L.labelGap,width:`100%`},children:[(0,r.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:4,width:`100%`},children:[(0,r.jsxs)(`div`,{style:{display:`flex`,flex:`1 0 0`,alignItems:`center`,gap:L.labelIconSize===12?4:6,minWidth:0},children:[(0,r.jsx)(`span`,{style:{...J,fontSize:L.labelFontSize,lineHeight:`${L.labelLineHeight}px`,color:B,whiteSpace:`nowrap`,overflow:`hidden`,textOverflow:`ellipsis`},children:e}),(0,r.jsx)(n.Info,{size:L.labelIconSize,color:U,strokeWidth:1.8,style:{flexShrink:0}})]}),a&&(0,r.jsx)(`span`,{style:{...J,fontSize:L.labelFontSize,lineHeight:`${L.labelLineHeight}px`,color:B,whiteSpace:`nowrap`,flexShrink:0},children:a}),i&&(0,r.jsx)(`span`,{style:{color:v.error,fontWeight:800,flexShrink:0},children:`*`})]}),(0,r.jsxs)(`div`,{style:{background:z,border:`1px solid ${R}`,height:L.inputHeight,width:`100%`,borderRadius:24,display:`flex`,alignItems:p?`stretch`:`center`,...p?{}:{gap:8,padding:`0 ${L.paddingX}px`},overflow:`hidden`,boxShadow:N||I?E:`none`,opacity:P?.7:1,boxSizing:`border-box`},children:[p?(0,r.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:4,background:_.bgWhite,padding:`${L.leadingPy}px ${L.leadingPr}px ${L.leadingPy}px ${L.leadingPl}px`,flexShrink:0},children:[(0,r.jsx)(`span`,{style:{...J,fontSize:L.inputFontSize,lineHeight:`${L.inputLineHeight}px`,color:_.fgHigh,whiteSpace:`nowrap`},children:p}),(0,r.jsx)(`svg`,{width:L.chevronSize,height:L.chevronSize,viewBox:`0 0 16 16`,fill:`none`,children:(0,r.jsx)(`path`,{d:`M4 6L8 10L12 6`,stroke:_.fgHigh,strokeWidth:`1.5`,strokeLinecap:`round`,strokeLinejoin:`round`})})]}):G?(0,r.jsx)(G,{size:L.iconSize,color:H,strokeWidth:1.6,style:{flexShrink:0}}):null,(0,r.jsxs)(`div`,{style:{flex:`1 0 0`,display:`flex`,alignItems:`center`,gap:8,height:`100%`,minWidth:0,...p?{borderLeft:`1px solid ${_.borderLight}`,padding:`0 12px`}:{}},children:[(0,r.jsx)(`input`,{type:`text`,value:C,onChange:e=>A(e.target.value),placeholder:o,disabled:P,style:{...J,flex:`1 0 0`,border:`none`,outline:`none`,background:`transparent`,fontSize:L.inputFontSize,lineHeight:`${L.inputLineHeight}px`,color:V,width:`100%`,minWidth:0,overflow:`hidden`,textOverflow:`ellipsis`,whiteSpace:`nowrap`,cursor:P?`not-allowed`:`text`}}),h&&(0,r.jsxs)(`div`,{style:{background:_.bgLightgray,display:`flex`,gap:2,alignItems:`center`,justifyContent:`center`,padding:L.counterPadding,borderRadius:L.counterRadius,flexShrink:0,opacity:P?.7:1},children:[(0,r.jsx)(`span`,{style:{...J,fontSize:L.counterFontSize,lineHeight:`${L.counterLineHeight}px`,color:_.fgHigh},children:h.value}),(0,r.jsxs)(`span`,{style:{...J,fontSize:L.counterFontSize,lineHeight:`${L.counterLineHeight}px`,color:_.fgMid},children:[`/ `,h.max]})]}),g&&(0,r.jsx)(`span`,{style:{...J,fontSize:L.inputFontSize,lineHeight:`${L.inputLineHeight}px`,color:_.fgHigh,whiteSpace:`nowrap`,flexShrink:0,maxWidth:64,overflow:`hidden`},children:g}),K.length>0&&(0,r.jsx)(`div`,{style:{display:`flex`,alignItems:`center`,gap:4,flexShrink:0},children:K.map((e,n)=>{let i=O[e];return(0,r.jsxs)(t.default.Fragment,{children:[S&&n>0&&(0,r.jsx)(`div`,{style:{width:1,height:L.iconSize,background:_.gray300,flexShrink:0,alignSelf:`center`}}),(0,r.jsx)(i,{size:L.iconSize,color:H,strokeWidth:1.6,style:{flexShrink:0}})]},`${e}-${n}`)})})]})]})]}),q&&(0,r.jsx)(`p`,{style:{...J,margin:0,fontSize:L.helperFontSize,lineHeight:`${L.helperLineHeight}px`,color:W,width:`100%`},children:q})]})},A=g;e.Badge=h,e.InputField=k,e.ciPrimary=y,e.font=w,e.grayNeutral=_,e.interactive=x,e.oePrimary=b,e.padding=S,e.radius=C,e.status=v,e.tokens=A});
2
+ //# sourceMappingURL=senestia-ui.umd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"senestia-ui.umd.js","names":[],"sources":["../src/stories/Badge.tsx","../src/stories/tokens.ts","../src/stories/InputField.tsx","../src/index.ts"],"sourcesContent":["import React from 'react';\nimport { X } from 'lucide-react';\n\n// ─── Design System Font ──────────────────────────────────────────────────────\nconst font = 'Sarabun, sans-serif';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport type BadgeColor =\n | 'Gray'\n | 'Primary'\n | 'Error'\n | 'Warning'\n | 'Success'\n | 'Blue light'\n | 'Blue'\n | 'Indigo'\n | 'Purple'\n | 'Pink'\n | 'Orange'\n | 'Gray blue';\n\nexport type BadgeSize = 'sm' | 'md' | 'lg';\n\nexport type BadgeType = 'Idle' | 'Status Dot' | 'Closable' | 'Pill color';\n\nexport interface BadgeProps {\n /** Badge label text */\n label?: string;\n /** Color variant */\n color?: BadgeColor;\n /** Size variant */\n size?: BadgeSize;\n /** Type / style variant */\n type?: BadgeType;\n /** Icon to render inside Pill color type (Lucide icon component) */\n icon?: React.ElementType;\n /** Called when close button is clicked (Closable type) */\n onClose?: () => void;\n}\n\n// ─── Color Tokens ────────────────────────────────────────────────────────────\n\nconst COLOR_TOKENS: Record<\n BadgeColor,\n { bg: string; border: string; text: string; dot: string }\n> = {\n Gray: { bg: '#f9fafb', border: '#eaecf0', text: '#06080b', dot: '#667085' },\n Primary: { bg: '#f4f6f9', border: '#e6ebf2', text: '#1e2b3b', dot: '#6c8fbe' },\n Error: { bg: '#fef0ef', border: '#fbd2d0', text: '#cc1a11', dot: '#f04438' },\n Warning: { bg: '#fff9ee', border: '#ffedcc', text: '#db8f02', dot: '#f79009' },\n Success: { bg: '#f0fdfa', border: '#d2f9f1', text: '#15a686', dot: '#12b76a' },\n 'Blue light': { bg: '#f0f9ff', border: '#b9e6fe', text: '#026aa2', dot: '#0ba5ec' },\n Blue: { bg: '#eff8ff', border: '#b2ddff', text: '#175cd3', dot: '#2e90fa' },\n Indigo: { bg: '#eef4ff', border: '#c7d7fe', text: '#3538cd', dot: '#6172f3' },\n Purple: { bg: '#f4f3ff', border: '#d9d6fe', text: '#5925dc', dot: '#7a5af8' },\n Pink: { bg: '#fef6fb', border: '#fce7f6', text: '#c11574', dot: '#ee46bc' },\n Orange: { bg: '#fef6ee', border: '#ffd6ae', text: '#b93815', dot: '#ef6820' },\n 'Gray blue':{ bg: '#f8f9fc', border: '#d5d9eb', text: '#363f72', dot: '#717bbc' },\n};\n\n// ─── Size Tokens ─────────────────────────────────────────────────────────────\n\nconst SIZE_TOKENS: Record<\n BadgeSize,\n {\n paddingX: number;\n paddingY: number;\n paddingXClose: { left: number; right: number };\n paddingXDot: { left: number; right: number };\n paddingPill: number;\n fontSize: number;\n lineHeight: number;\n dotSize: number;\n iconSize: number;\n closeSize: number;\n }\n> = {\n sm: {\n paddingX: 8, paddingY: 2,\n paddingXClose: { left: 8, right: 4 },\n paddingXDot: { left: 6, right: 8 },\n paddingPill: 5,\n fontSize: 12, lineHeight: 18,\n dotSize: 6, iconSize: 12, closeSize: 12,\n },\n md: {\n paddingX: 8, paddingY: 2,\n paddingXClose: { left: 8, right: 4 },\n paddingXDot: { left: 6, right: 8 },\n paddingPill: 6,\n fontSize: 14, lineHeight: 20,\n dotSize: 6, iconSize: 14, closeSize: 14,\n },\n lg: {\n paddingX: 12, paddingY: 4,\n paddingXClose: { left: 12, right: 6 },\n paddingXDot: { left: 8, right: 12 },\n paddingPill: 8,\n fontSize: 14, lineHeight: 20,\n dotSize: 8, iconSize: 16, closeSize: 16,\n },\n};\n\n// ─── Component ───────────────────────────────────────────────────────────────\n\nexport const Badge: React.FC<BadgeProps> = ({\n label = 'Label',\n color = 'Gray',\n size = 'sm',\n type = 'Idle',\n icon: IconComponent,\n onClose,\n}) => {\n const c = COLOR_TOKENS[color];\n const s = SIZE_TOKENS[size];\n\n // ── Shared wrapper style ──────────────────────────────────────────────────\n const baseWrapper: React.CSSProperties = {\n display: 'inline-flex',\n alignItems: 'center',\n backgroundColor: c.bg,\n border: `1px solid ${c.border}`,\n borderRadius: 16,\n boxSizing: 'border-box',\n width: 'fit-content',\n fontFamily: font,\n };\n\n // ── Text style ────────────────────────────────────────────────────────────\n const textStyle: React.CSSProperties = {\n fontSize: s.fontSize,\n lineHeight: `${s.lineHeight}px`,\n fontWeight: 500,\n color: c.text,\n whiteSpace: 'nowrap',\n fontFamily: font,\n margin: 0,\n };\n\n // ── Idle ─────────────────────────────────────────────────────────────────\n if (type === 'Idle') {\n return (\n <span\n style={{\n ...baseWrapper,\n padding: `${s.paddingY}px ${s.paddingX}px`,\n }}\n >\n <span style={textStyle}>{label}</span>\n </span>\n );\n }\n\n // ── Status Dot ────────────────────────────────────────────────────────────\n if (type === 'Status Dot') {\n return (\n <span\n style={{\n ...baseWrapper,\n gap: 4,\n paddingTop: s.paddingY,\n paddingBottom: s.paddingY,\n paddingLeft: s.paddingXDot.left,\n paddingRight: s.paddingXDot.right,\n }}\n >\n {/* Dot */}\n <span\n style={{\n display: 'inline-block',\n width: s.dotSize,\n height: s.dotSize,\n borderRadius: '50%',\n backgroundColor: c.dot,\n flexShrink: 0,\n }}\n />\n <span style={textStyle}>{label}</span>\n </span>\n );\n }\n\n // ── Closable ──────────────────────────────────────────────────────────────\n if (type === 'Closable') {\n return (\n <span\n style={{\n ...baseWrapper,\n gap: 2,\n paddingTop: s.paddingY,\n paddingBottom: s.paddingY,\n paddingLeft: s.paddingXClose.left,\n paddingRight: s.paddingXClose.right,\n }}\n >\n <span style={textStyle}>{label}</span>\n <button\n onClick={onClose}\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n background: 'none',\n border: 'none',\n padding: 2,\n borderRadius: 3,\n cursor: 'pointer',\n color: c.text,\n flexShrink: 0,\n lineHeight: 1,\n }}\n aria-label=\"Remove\"\n >\n <X size={s.closeSize} strokeWidth={2.5} />\n </button>\n </span>\n );\n }\n\n // ── Pill color (icon only) ────────────────────────────────────────────────\n if (type === 'Pill color') {\n const PillIcon = IconComponent;\n return (\n <span\n style={{\n ...baseWrapper,\n padding: s.paddingPill,\n }}\n >\n {PillIcon ? (\n <PillIcon size={s.iconSize} color={c.dot} strokeWidth={2} />\n ) : (\n // Default: colored circle dot\n <span\n style={{\n display: 'inline-block',\n width: s.iconSize,\n height: s.iconSize,\n borderRadius: '50%',\n backgroundColor: c.dot,\n }}\n />\n )}\n </span>\n );\n }\n\n return null;\n};\n","/**\n * Senestia Design System — Semantic Color Tokens\n *\n * ┌─────────────────────────────────────────────────────────────┐\n * │ Source: Figma DS-01 · Foundation → Semantic Color │\n * │ Use these tokens in components instead of raw hex values. │\n * └─────────────────────────────────────────────────────────────┘\n */\n\n// ─── Gray-Neutral ─────────────────────────────────────────────────────────────\n\nexport const grayNeutral = {\n /** Semantic/Gray-Neutral/fg/high — primary text, dark icons */\n fgHigh: '#1B1D22',\n /** Semantic/Gray-Neutral/fg/mid-on-white — label text, helper text, secondary icons */\n fgMid: '#6A6E83',\n /** Semantic/Gray-Neutral/fg/low-on-white — placeholder, disabled text */\n fgLow: '#9A9DAD',\n\n /** Semantic/Gray-Neutral/bg/white */\n bgWhite: '#FFFFFF',\n /** Semantic/Gray-Neutral/bg/lightgray — counter pill, subtle backgrounds */\n bgLightgray: '#F8F8F9',\n /** Semantic/Gray-Neutral/bg/disabled */\n bgDisabled: '#F3F4F6',\n\n /** Semantic/Gray-Neutral/border/midgray — default input border */\n borderMidgray: '#CFD1D9',\n /** Semantic/Gray-Neutral/border/light — dividers, card borders */\n borderLight: '#EBECEF',\n /** Disabled border (checkbox, input) */\n borderDisabled: '#E5EAF1',\n\n /** Gray/300 — icon separators */\n gray300: '#D0D5DD',\n /** Skeleton loading shimmer */\n bgSkeleton: '#EEF0F3',\n /** Strong disabled background (checkbox) */\n bgDisabledStrong: '#EBECEF',\n} as const;\n\n// ─── Status ───────────────────────────────────────────────────────────────────\n\nexport const status = {\n /** Error / invalid state */\n error: '#DB1439',\n /** Warning */\n warning: '#DB8F02',\n /** Success */\n success: '#15A686',\n} as const;\n\n// ─── CI Primary (Senestia brand purple) ───────────────────────────────────────\n\nexport const ciPrimary = {\n fgLow: '#C4B3F6',\n fgHigh: '#802CEB',\n bgLow: '#D4C9F6',\n bgMid: '#802CEB',\n bgHigh: '#5A1DA9',\n borderMid: '#996CF3',\n borderHigh: '#802CEB',\n} as const;\n\n// ─── OE Theme (OE brand blue) ─────────────────────────────────────────────────\n\nexport const oePrimary = {\n fgLow: '#5273A1',\n fgHigh: '#3A5274',\n bgLowest: '#EFF2F6', // selected item background in pickers\n bgLow: '#6E90BF',\n bgMid: '#5273A1',\n bgHigh: '#3A5274',\n borderLowest:'#C6D2E3', // focused item border\n borderLow: '#9CB3D2',\n borderMid: '#5273A1',\n /** Primary/300 — supporting/helper text on blue-tinted UI */\n p300: '#809BBF',\n /** Indigo/300 — \"Set to now\" link color */\n linkBlue: '#3C5BD5',\n} as const;\n\n// ─── Interactive states ───────────────────────────────────────────────────────\n\nexport const interactive = {\n /** Active / focused border */\n activeBorder: oePrimary.fgLow, // #5273A1\n /** Hover border */\n hoverBorder: '#3B82F6',\n /** Focus ring shadow */\n focusRing: '#D7DFEA',\n /** Invalid border */\n invalidBorder: status.error,\n} as const;\n\n// ─── Padding tokens (PD) ─────────────────────────────────────────────────────\n\nexport const padding = {\n 'PD-0': 0,\n 'PD-1': 1,\n 'PD-2': 2,\n 'PD-3': 4,\n 'PD-4': 6,\n 'PD-5': 8,\n 'PD-6': 12,\n 'PD-7': 16,\n 'PD-8': 24,\n 'PD-9': 32,\n 'PD-10': 40,\n 'PD-11': 48,\n 'PD-12': 56,\n 'PD-13': 64,\n 'PD-14': 72,\n 'PD-15': 80,\n} as const;\n\n// ─── Corner Radius tokens (CR) ───────────────────────────────────────────────\n\nexport const radius = {\n 'CR-0': 0,\n 'CR-1': 2,\n 'CR-2': 4,\n 'CR-3': 8,\n 'CR-4': 12,\n 'CR-5': 16,\n 'CR-6': 20,\n 'CR-7': 24,\n 'CR-8': 32,\n 'CR-9': 48,\n 'CR-10': 100,\n} as const;\n\n// ─── Typography ───────────────────────────────────────────────────────────────\n\nexport const font = 'Sarabun, sans-serif';\n","import React, { useState } from 'react';\nimport { Circle, Info, XCircle, Search, Eye } from 'lucide-react';\nimport { grayNeutral, status, interactive, font } from './tokens';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport type InputFieldState =\n | 'Idle'\n | 'Idle hover'\n | 'Active'\n | 'Idle invalid'\n | 'Idle invalid hover'\n | 'Idle invalid active'\n | 'Filled'\n | 'Filled hover'\n | 'Filled active'\n | 'Filled invalid'\n | 'Filled invalid hover'\n | 'Filled invalid active'\n | 'Disable'\n | 'Keyboard focus'\n | 'Skeleton';\n\nexport type InputFieldSize = 'sm' | 'md' | 'lg';\n\nexport type InputFieldTrailingIcon = 'clear' | 'password' | 'search';\n\nexport type InputFieldCounter = {\n value: number;\n max: number;\n};\n\nexport interface InputFieldProps {\n /** Label text displayed above the input */\n label: string;\n /** Show required asterisk (*) */\n required?: boolean;\n /** Remark text next to label e.g. \"(optional)\" */\n labelRemark?: string;\n /** Placeholder text */\n placeholder?: string;\n /** Current value */\n value?: string;\n /** Called when value changes */\n onChange?: (value: string) => void;\n /** Helper text below the input (hidden when invalid) */\n helperText?: string;\n /** Error message below the input (shown when invalid state) */\n errorText?: string;\n /** Visual state from Figma */\n state?: InputFieldState;\n /** Input size — affects height, font, and padding */\n size?: InputFieldSize;\n /** Leading dropdown prefix text */\n leadingDropdownText?: string;\n /**\n * Leading icon inside the input (left side).\n * - `true` → default Circle icon\n * - `React.ElementType` → custom Lucide icon\n * - `false/undefined` → no icon\n */\n leadingIcon?: React.ElementType | boolean;\n /** Counter pill e.g. `{ value: 0, max: 20 }` */\n counter?: InputFieldCounter;\n /** Unit text inside the input */\n unit?: string;\n /** Single trailing icon */\n trailingIcon?: InputFieldTrailingIcon;\n /** Multiple trailing icons (overrides trailingIcon) */\n trailingIcons?: InputFieldTrailingIcon[];\n /** Show separator between trailing icons */\n trailingIconSeparator?: boolean;\n}\n\n// ─── Design tokens (from Figma) ──────────────────────────────────────────────\n\n/**\n * Size tokens — matched to Figma nodes:\n * sm (Sm-32): label C5 12/18 · input C4 14/20 · helper C5 12/18\n * md (Md-40): label C4 14/20 · input C3 16/24 · helper C4 14/20\n * lg (Lg-48): label C3 16/24 · input C3 16/24 · helper C4 14/20\n *\n * Spacing (per Figma sm node — gap-4 between all elements):\n * labelGap = gap between label row and input box\n * helperGap = gap between input box and helper/error text\n */\nconst sizeTokens = {\n sm: {\n inputHeight: 32,\n labelFontSize: 12, labelLineHeight: 18,\n inputFontSize: 14, inputLineHeight: 20,\n helperFontSize: 12, helperLineHeight: 18,\n paddingX: 12,\n iconSize: 16,\n labelIconSize: 12,\n labelGap: 4, // label row → input\n helperGap: 4, // input → helper text\n leadingPl: 8, leadingPr: 4, leadingPy: 6,\n chevronSize: 14,\n counterPadding: '1px 4px',\n counterRadius: 4,\n counterFontSize: 12, counterLineHeight: 18,\n skeletonLabelH: 18, skeletonInputH: 32,\n },\n md: {\n inputHeight: 40,\n labelFontSize: 14, labelLineHeight: 20,\n inputFontSize: 16, inputLineHeight: 24,\n helperFontSize: 14, helperLineHeight: 20,\n paddingX: 16,\n iconSize: 20,\n labelIconSize: 14,\n labelGap: 6, // label row → input\n helperGap: 4, // input → helper text\n leadingPl: 12, leadingPr: 8, leadingPy: 8,\n chevronSize: 16,\n counterPadding: '2px 8px',\n counterRadius: 8,\n counterFontSize: 14, counterLineHeight: 20,\n skeletonLabelH: 20, skeletonInputH: 40,\n },\n lg: {\n inputHeight: 48,\n labelFontSize: 16, labelLineHeight: 24,\n inputFontSize: 16, inputLineHeight: 24,\n helperFontSize: 14, helperLineHeight: 20,\n paddingX: 16,\n iconSize: 20,\n labelIconSize: 16,\n labelGap: 8, // label row → input\n helperGap: 6, // input → helper text\n leadingPl: 16, leadingPr: 8, leadingPy: 12,\n chevronSize: 16,\n counterPadding: '2px 8px',\n counterRadius: 8,\n counterFontSize: 14, counterLineHeight: 20,\n skeletonLabelH: 24, skeletonInputH: 48,\n },\n} as const;\n\n// ─── State helpers ────────────────────────────────────────────────────────────\n\nconst focusRingStyle = `0 0 0 2px ${interactive.focusRing}`;\n\nconst getStateFlags = (state: InputFieldState | undefined) => {\n const s = state ?? 'Idle';\n const isInvalid = s.includes('invalid');\n const isHover = s.includes('hover');\n const isActive = s === 'Active' || s === 'Keyboard focus' || s.endsWith('active');\n const isDisabled = s === 'Disable';\n const isSkeleton = s === 'Skeleton';\n const isFilled = s.includes('Filled');\n const isKeyboardFocus = s === 'Keyboard focus';\n return { s, isInvalid, isHover, isActive, isDisabled, isSkeleton, isFilled, isKeyboardFocus };\n};\n\n// ─── Trailing icon map → Lucide ───────────────────────────────────────────────\n\nconst TRAILING_ICON_MAP = {\n clear: XCircle,\n search: Search,\n password: Eye,\n} as const;\n\n// ─── Component ───────────────────────────────────────────────────────────────\n\nexport const InputField = ({\n label,\n required = false,\n labelRemark,\n placeholder = 'Placeholder',\n value,\n onChange: _onChange,\n helperText,\n errorText,\n state = 'Idle',\n size = 'md',\n leadingDropdownText,\n leadingIcon,\n counter,\n unit,\n trailingIcon,\n trailingIcons,\n trailingIconSeparator = false,\n}: InputFieldProps) => {\n const [internalValue, setInternalValue] = useState(value ?? '');\n\n const handleChange = (val: string) => {\n setInternalValue(val);\n _onChange?.(val);\n };\n\n const { isInvalid, isHover, isActive, isDisabled, isSkeleton, isKeyboardFocus } =\n getStateFlags(state);\n\n const t = sizeTokens[size];\n\n // ── Colors — mapped to Semantic/Gray-Neutral tokens ─────────────────────\n const borderColor = isInvalid\n ? interactive.invalidBorder // status.error #DB1439\n : isActive || isKeyboardFocus\n ? interactive.activeBorder // oePrimary.fgLow #5273A1\n : isHover\n ? interactive.hoverBorder // #3B82F6\n : grayNeutral.borderMidgray; // #CFD1D9\n\n const background = isDisabled\n ? grayNeutral.bgDisabled // #F3F4F6\n : isHover\n ? grayNeutral.bgLightgray // #F8F8F9\n : grayNeutral.bgWhite; // #FFFFFF\n\n // Label text → fg/mid-on-white (disabled → fg/low)\n const labelColor = isDisabled ? grayNeutral.fgLow : grayNeutral.fgMid;\n\n // Typed value → fg/high (disabled → fg/low)\n const valueColor = isDisabled ? grayNeutral.fgLow : grayNeutral.fgHigh; // #1B1D22\n\n // ── Icon colors (Figma: Semantic/Gray-Neutral/fg/high for input icons) ───\n // Input leading/trailing icons → fg/high (#1B1D22)\n const inputIconColor = isDisabled ? grayNeutral.fgLow : grayNeutral.fgHigh;\n // Label info icon → fg/mid (same as label text, #6A6E83)\n const labelIconColor = isDisabled ? grayNeutral.fgLow : grayNeutral.fgMid;\n\n const helperColor = isInvalid ? status.error : grayNeutral.fgMid; // #6A6E83\n\n // ── Skeleton ─────────────────────────────────────────────────────────────\n if (isSkeleton) {\n return (\n <div style={{ width: 320, display: 'flex', flexDirection: 'column', gap: t.labelGap }}>\n <div style={{ height: t.skeletonLabelH, background: grayNeutral.bgSkeleton, borderRadius: 6 }} />\n <div style={{ height: t.skeletonInputH, background: grayNeutral.bgSkeleton, border: `1px solid ${grayNeutral.bgSkeleton}`, borderRadius: 24 }} />\n </div>\n );\n }\n\n // ── Leading icon resolve ──────────────────────────────────────────────────\n // true → Circle (Figma default \"Icon/circle\"), component → custom, false → none\n const LeadingIconComponent: React.ElementType | null =\n leadingIcon === true\n ? Circle\n : leadingIcon\n ? (leadingIcon as React.ElementType)\n : null;\n\n // ── Trailing icons ────────────────────────────────────────────────────────\n const iconsToRender: InputFieldTrailingIcon[] =\n trailingIcons?.length ? trailingIcons : trailingIcon ? [trailingIcon] : [];\n\n // ── Text below input ──────────────────────────────────────────────────────\n const showText = isInvalid ? errorText : helperText;\n\n // ── Shared font style ─────────────────────────────────────────────────────\n const baseFont: React.CSSProperties = { fontFamily: font, fontWeight: 500 };\n\n return (\n /*\n * Outer wrapper — two-level gap structure matching Figma:\n * Level 1 (helperGap): separates [label + input block] from [helper text]\n * Level 2 (labelGap): separates [label row] from [input box]\n */\n <div style={{ width: 320, display: 'flex', flexDirection: 'column', gap: t.helperGap }}>\n\n {/* ── Level 1: label + input block ────────────────────────────────── */}\n <div style={{ display: 'flex', flexDirection: 'column', gap: t.labelGap, width: '100%' }}>\n\n {/* Label row */}\n <div style={{ display: 'flex', alignItems: 'center', gap: 4, width: '100%' }}>\n {/* Left: label text + info icon */}\n <div style={{ display: 'flex', flex: '1 0 0', alignItems: 'center', gap: t.labelIconSize === 12 ? 4 : 6, minWidth: 0 }}>\n <span\n style={{\n ...baseFont,\n fontSize: t.labelFontSize,\n lineHeight: `${t.labelLineHeight}px`,\n color: labelColor,\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n }}\n >\n {label}\n </span>\n {/* Info icon — fg/mid-on-white (same shade as label text) */}\n <Info\n size={t.labelIconSize}\n color={labelIconColor}\n strokeWidth={1.8}\n style={{ flexShrink: 0 }}\n />\n </div>\n\n {/* Right: remark or required marker */}\n {labelRemark && (\n <span\n style={{\n ...baseFont,\n fontSize: t.labelFontSize,\n lineHeight: `${t.labelLineHeight}px`,\n color: labelColor,\n whiteSpace: 'nowrap',\n flexShrink: 0,\n }}\n >\n {labelRemark}\n </span>\n )}\n {required && (\n <span style={{ color: status.error, fontWeight: 800, flexShrink: 0 }}>*</span>\n )}\n </div>\n\n {/* Input box */}\n <div\n style={{\n background,\n border: `1px solid ${borderColor}`,\n height: t.inputHeight,\n width: '100%',\n borderRadius: 24,\n display: 'flex',\n alignItems: leadingDropdownText ? 'stretch' : 'center',\n ...(leadingDropdownText ? {} : { gap: 8, padding: `0 ${t.paddingX}px` }),\n overflow: 'hidden',\n boxShadow: isActive || isKeyboardFocus ? focusRingStyle : 'none',\n opacity: isDisabled ? 0.7 : 1,\n boxSizing: 'border-box',\n }}\n >\n {/* Leading: dropdown prefix OR icon */}\n {leadingDropdownText ? (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 4,\n background: grayNeutral.bgWhite,\n padding: `${t.leadingPy}px ${t.leadingPr}px ${t.leadingPy}px ${t.leadingPl}px`,\n flexShrink: 0,\n }}\n >\n <span\n style={{\n ...baseFont,\n fontSize: t.inputFontSize,\n lineHeight: `${t.inputLineHeight}px`,\n color: grayNeutral.fgHigh,\n whiteSpace: 'nowrap',\n }}\n >\n {leadingDropdownText}\n </span>\n <svg width={t.chevronSize} height={t.chevronSize} viewBox=\"0 0 16 16\" fill=\"none\">\n <path d=\"M4 6L8 10L12 6\" stroke={grayNeutral.fgHigh} strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n </div>\n ) : LeadingIconComponent ? (\n /* Leading icon in input → fg/high (#1B1D22) per Figma */\n <LeadingIconComponent\n size={t.iconSize}\n color={inputIconColor}\n strokeWidth={1.6}\n style={{ flexShrink: 0 }}\n />\n ) : null}\n\n {/* Text + accessories area */}\n <div\n style={{\n flex: '1 0 0',\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n height: '100%',\n minWidth: 0,\n ...(leadingDropdownText\n ? { borderLeft: `1px solid ${grayNeutral.borderLight}`, padding: `0 12px` }\n : {}),\n }}\n >\n {/* Input */}\n <input\n type=\"text\"\n value={internalValue}\n onChange={e => handleChange(e.target.value)}\n placeholder={placeholder}\n disabled={isDisabled}\n style={{\n ...baseFont,\n flex: '1 0 0',\n border: 'none',\n outline: 'none',\n background: 'transparent',\n fontSize: t.inputFontSize,\n lineHeight: `${t.inputLineHeight}px`,\n color: valueColor,\n width: '100%',\n minWidth: 0,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n cursor: isDisabled ? 'not-allowed' : 'text',\n }}\n />\n\n {/* Counter pill */}\n {counter && (\n <div\n style={{\n background: grayNeutral.bgLightgray,\n display: 'flex',\n gap: 2,\n alignItems: 'center',\n justifyContent: 'center',\n padding: t.counterPadding,\n borderRadius: t.counterRadius,\n flexShrink: 0,\n opacity: isDisabled ? 0.7 : 1,\n }}\n >\n <span\n style={{\n ...baseFont,\n fontSize: t.counterFontSize,\n lineHeight: `${t.counterLineHeight}px`,\n color: grayNeutral.fgHigh,\n }}\n >\n {counter.value}\n </span>\n <span\n style={{\n ...baseFont,\n fontSize: t.counterFontSize,\n lineHeight: `${t.counterLineHeight}px`,\n color: grayNeutral.fgMid,\n }}\n >\n / {counter.max}\n </span>\n </div>\n )}\n\n {/* Unit text */}\n {unit && (\n <span\n style={{\n ...baseFont,\n fontSize: t.inputFontSize,\n lineHeight: `${t.inputLineHeight}px`,\n color: grayNeutral.fgHigh,\n whiteSpace: 'nowrap',\n flexShrink: 0,\n maxWidth: 64,\n overflow: 'hidden',\n }}\n >\n {unit}\n </span>\n )}\n\n {/* Trailing icons */}\n {iconsToRender.length > 0 && (\n <div style={{ display: 'flex', alignItems: 'center', gap: 4, flexShrink: 0 }}>\n {iconsToRender.map((ic, idx) => {\n const TrailingIcon = TRAILING_ICON_MAP[ic];\n return (\n <React.Fragment key={`${ic}-${idx}`}>\n {trailingIconSeparator && idx > 0 && (\n <div\n style={{\n width: 1,\n height: t.iconSize,\n background: grayNeutral.gray300,\n flexShrink: 0,\n alignSelf: 'center',\n }}\n />\n )}\n {/* Trailing icon → fg/high (#1B1D22) per Figma */}\n <TrailingIcon\n size={t.iconSize}\n color={inputIconColor}\n strokeWidth={1.6}\n style={{ flexShrink: 0 }}\n />\n </React.Fragment>\n );\n })}\n </div>\n )}\n </div>\n </div>\n </div>\n\n {/* ── Level 1: helper / error text ────────────────────────────────── */}\n {/*\n * No extra margin here — the outer helperGap (4px sm / 4px md / 6px lg)\n * from the flex container already provides the correct spacing from Figma.\n */}\n {showText && (\n <p\n style={{\n ...baseFont,\n margin: 0,\n fontSize: t.helperFontSize,\n lineHeight: `${t.helperLineHeight}px`,\n color: helperColor,\n width: '100%',\n }}\n >\n {showText}\n </p>\n )}\n </div>\n );\n};\n","// ─── Senestia Design System — Public API ─────────────────────────────────────\n//\n// import { Badge, InputField, tokens } from '@senestia/ui'\n//\n// ─────────────────────────────────────────────────────────────────────────────\n\n// ── Components ────────────────────────────────────────────────────────────────\nexport { Badge } from './stories/Badge';\nexport { InputField } from './stories/InputField';\n\n// ── Component types ───────────────────────────────────────────────────────────\nexport type { BadgeProps, BadgeColor, BadgeSize, BadgeType } from './stories/Badge';\nexport type {\n InputFieldProps,\n InputFieldState,\n InputFieldSize,\n InputFieldTrailingIcon,\n InputFieldCounter,\n} from './stories/InputField';\n\n// ── Design Tokens ─────────────────────────────────────────────────────────────\nexport {\n // Colors\n grayNeutral,\n status,\n ciPrimary,\n oePrimary,\n interactive,\n // Spacing\n padding,\n radius,\n // Typography\n font,\n} from './stories/tokens';\n\n// ── Tokens namespace (for tree-shaking friendly usage) ────────────────────────\nimport * as _tokens from './stories/tokens';\nexport const tokens = _tokens;\n"],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import React from 'react';
2
+ export type BadgeColor = 'Gray' | 'Primary' | 'Error' | 'Warning' | 'Success' | 'Blue light' | 'Blue' | 'Indigo' | 'Purple' | 'Pink' | 'Orange' | 'Gray blue';
3
+ export type BadgeSize = 'sm' | 'md' | 'lg';
4
+ export type BadgeType = 'Idle' | 'Status Dot' | 'Closable' | 'Pill color';
5
+ export interface BadgeProps {
6
+ /** Badge label text */
7
+ label?: string;
8
+ /** Color variant */
9
+ color?: BadgeColor;
10
+ /** Size variant */
11
+ size?: BadgeSize;
12
+ /** Type / style variant */
13
+ type?: BadgeType;
14
+ /** Icon to render inside Pill color type (Lucide icon component) */
15
+ icon?: React.ElementType;
16
+ /** Called when close button is clicked (Closable type) */
17
+ onClose?: () => void;
18
+ }
19
+ export declare const Badge: React.FC<BadgeProps>;
@@ -0,0 +1,50 @@
1
+ import React from 'react';
2
+ export type InputFieldState = 'Idle' | 'Idle hover' | 'Active' | 'Idle invalid' | 'Idle invalid hover' | 'Idle invalid active' | 'Filled' | 'Filled hover' | 'Filled active' | 'Filled invalid' | 'Filled invalid hover' | 'Filled invalid active' | 'Disable' | 'Keyboard focus' | 'Skeleton';
3
+ export type InputFieldSize = 'sm' | 'md' | 'lg';
4
+ export type InputFieldTrailingIcon = 'clear' | 'password' | 'search';
5
+ export type InputFieldCounter = {
6
+ value: number;
7
+ max: number;
8
+ };
9
+ export interface InputFieldProps {
10
+ /** Label text displayed above the input */
11
+ label: string;
12
+ /** Show required asterisk (*) */
13
+ required?: boolean;
14
+ /** Remark text next to label e.g. "(optional)" */
15
+ labelRemark?: string;
16
+ /** Placeholder text */
17
+ placeholder?: string;
18
+ /** Current value */
19
+ value?: string;
20
+ /** Called when value changes */
21
+ onChange?: (value: string) => void;
22
+ /** Helper text below the input (hidden when invalid) */
23
+ helperText?: string;
24
+ /** Error message below the input (shown when invalid state) */
25
+ errorText?: string;
26
+ /** Visual state from Figma */
27
+ state?: InputFieldState;
28
+ /** Input size — affects height, font, and padding */
29
+ size?: InputFieldSize;
30
+ /** Leading dropdown prefix text */
31
+ leadingDropdownText?: string;
32
+ /**
33
+ * Leading icon inside the input (left side).
34
+ * - `true` → default Circle icon
35
+ * - `React.ElementType` → custom Lucide icon
36
+ * - `false/undefined` → no icon
37
+ */
38
+ leadingIcon?: React.ElementType | boolean;
39
+ /** Counter pill e.g. `{ value: 0, max: 20 }` */
40
+ counter?: InputFieldCounter;
41
+ /** Unit text inside the input */
42
+ unit?: string;
43
+ /** Single trailing icon */
44
+ trailingIcon?: InputFieldTrailingIcon;
45
+ /** Multiple trailing icons (overrides trailingIcon) */
46
+ trailingIcons?: InputFieldTrailingIcon[];
47
+ /** Show separator between trailing icons */
48
+ trailingIconSeparator?: boolean;
49
+ }
50
+ export declare const InputField: ({ label, required, labelRemark, placeholder, value, onChange: _onChange, helperText, errorText, state, size, leadingDropdownText, leadingIcon, counter, unit, trailingIcon, trailingIcons, trailingIconSeparator, }: InputFieldProps) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Senestia Design System — Semantic Color Tokens
3
+ *
4
+ * ┌─────────────────────────────────────────────────────────────┐
5
+ * │ Source: Figma DS-01 · Foundation → Semantic Color │
6
+ * │ Use these tokens in components instead of raw hex values. │
7
+ * └─────────────────────────────────────────────────────────────┘
8
+ */
9
+ export declare const grayNeutral: {
10
+ /** Semantic/Gray-Neutral/fg/high — primary text, dark icons */
11
+ readonly fgHigh: "#1B1D22";
12
+ /** Semantic/Gray-Neutral/fg/mid-on-white — label text, helper text, secondary icons */
13
+ readonly fgMid: "#6A6E83";
14
+ /** Semantic/Gray-Neutral/fg/low-on-white — placeholder, disabled text */
15
+ readonly fgLow: "#9A9DAD";
16
+ /** Semantic/Gray-Neutral/bg/white */
17
+ readonly bgWhite: "#FFFFFF";
18
+ /** Semantic/Gray-Neutral/bg/lightgray — counter pill, subtle backgrounds */
19
+ readonly bgLightgray: "#F8F8F9";
20
+ /** Semantic/Gray-Neutral/bg/disabled */
21
+ readonly bgDisabled: "#F3F4F6";
22
+ /** Semantic/Gray-Neutral/border/midgray — default input border */
23
+ readonly borderMidgray: "#CFD1D9";
24
+ /** Semantic/Gray-Neutral/border/light — dividers, card borders */
25
+ readonly borderLight: "#EBECEF";
26
+ /** Disabled border (checkbox, input) */
27
+ readonly borderDisabled: "#E5EAF1";
28
+ /** Gray/300 — icon separators */
29
+ readonly gray300: "#D0D5DD";
30
+ /** Skeleton loading shimmer */
31
+ readonly bgSkeleton: "#EEF0F3";
32
+ /** Strong disabled background (checkbox) */
33
+ readonly bgDisabledStrong: "#EBECEF";
34
+ };
35
+ export declare const status: {
36
+ /** Error / invalid state */
37
+ readonly error: "#DB1439";
38
+ /** Warning */
39
+ readonly warning: "#DB8F02";
40
+ /** Success */
41
+ readonly success: "#15A686";
42
+ };
43
+ export declare const ciPrimary: {
44
+ readonly fgLow: "#C4B3F6";
45
+ readonly fgHigh: "#802CEB";
46
+ readonly bgLow: "#D4C9F6";
47
+ readonly bgMid: "#802CEB";
48
+ readonly bgHigh: "#5A1DA9";
49
+ readonly borderMid: "#996CF3";
50
+ readonly borderHigh: "#802CEB";
51
+ };
52
+ export declare const oePrimary: {
53
+ readonly fgLow: "#5273A1";
54
+ readonly fgHigh: "#3A5274";
55
+ readonly bgLowest: "#EFF2F6";
56
+ readonly bgLow: "#6E90BF";
57
+ readonly bgMid: "#5273A1";
58
+ readonly bgHigh: "#3A5274";
59
+ readonly borderLowest: "#C6D2E3";
60
+ readonly borderLow: "#9CB3D2";
61
+ readonly borderMid: "#5273A1";
62
+ /** Primary/300 — supporting/helper text on blue-tinted UI */
63
+ readonly p300: "#809BBF";
64
+ /** Indigo/300 — "Set to now" link color */
65
+ readonly linkBlue: "#3C5BD5";
66
+ };
67
+ export declare const interactive: {
68
+ /** Active / focused border */
69
+ readonly activeBorder: "#5273A1";
70
+ /** Hover border */
71
+ readonly hoverBorder: "#3B82F6";
72
+ /** Focus ring shadow */
73
+ readonly focusRing: "#D7DFEA";
74
+ /** Invalid border */
75
+ readonly invalidBorder: "#DB1439";
76
+ };
77
+ export declare const padding: {
78
+ readonly 'PD-0': 0;
79
+ readonly 'PD-1': 1;
80
+ readonly 'PD-2': 2;
81
+ readonly 'PD-3': 4;
82
+ readonly 'PD-4': 6;
83
+ readonly 'PD-5': 8;
84
+ readonly 'PD-6': 12;
85
+ readonly 'PD-7': 16;
86
+ readonly 'PD-8': 24;
87
+ readonly 'PD-9': 32;
88
+ readonly 'PD-10': 40;
89
+ readonly 'PD-11': 48;
90
+ readonly 'PD-12': 56;
91
+ readonly 'PD-13': 64;
92
+ readonly 'PD-14': 72;
93
+ readonly 'PD-15': 80;
94
+ };
95
+ export declare const radius: {
96
+ readonly 'CR-0': 0;
97
+ readonly 'CR-1': 2;
98
+ readonly 'CR-2': 4;
99
+ readonly 'CR-3': 8;
100
+ readonly 'CR-4': 12;
101
+ readonly 'CR-5': 16;
102
+ readonly 'CR-6': 20;
103
+ readonly 'CR-7': 24;
104
+ readonly 'CR-8': 32;
105
+ readonly 'CR-9': 48;
106
+ readonly 'CR-10': 100;
107
+ };
108
+ export declare const font = "Sarabun, sans-serif";
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "@senestia/ui",
3
+ "version": "0.1.0-beta.00c7f75",
4
+ "type": "module",
5
+ "description": "Senestia Design System — React component library",
6
+ "main": "./dist/senestia-ui.umd.js",
7
+ "module": "./dist/senestia-ui.es.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/senestia-ui.es.js",
12
+ "require": "./dist/senestia-ui.umd.js",
13
+ "types": "./dist/index.d.ts"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "scripts": {
20
+ "dev": "vite",
21
+ "build": "tsc -b && vite build",
22
+ "build:lib": "vite build --config vite.config.lib.ts && tsc --emitDeclarationOnly --declaration --declarationDir dist --project tsconfig.lib.json",
23
+ "lint": "eslint .",
24
+ "preview": "vite preview",
25
+ "storybook": "storybook dev -p 6006",
26
+ "build-storybook": "storybook build",
27
+ "chromatic": "npx chromatic --project-token=chpt_250b9f6bb9afaff"
28
+ },
29
+ "peerDependencies": {
30
+ "lucide-react": ">=0.400.0",
31
+ "react": ">=18",
32
+ "react-dom": ">=18"
33
+ },
34
+ "dependencies": {
35
+ "@fluentui/web-components": "^2.6.1",
36
+ "@react-three/drei": "^10.7.7",
37
+ "lucide-react": ">=0.400.0"
38
+ },
39
+ "devDependencies": {
40
+ "@chromatic-com/storybook": "^5.0.1",
41
+ "@eslint/js": "^9.39.4",
42
+ "@storybook/addon-a11y": "^10.3.0",
43
+ "@storybook/addon-docs": "^10.3.0",
44
+ "@storybook/addon-onboarding": "^10.3.0",
45
+ "@storybook/addon-vitest": "^10.3.0",
46
+ "@storybook/react-vite": "^10.3.0",
47
+ "@types/node": "^24.12.0",
48
+ "@types/react": "^19.2.14",
49
+ "@types/react-dom": "^19.2.3",
50
+ "@vitejs/plugin-react": "^6.0.1",
51
+ "@vitest/browser-playwright": "^4.1.0",
52
+ "@vitest/coverage-v8": "^4.1.0",
53
+ "chromatic": "^15.3.0",
54
+ "eslint": "^9.39.4",
55
+ "eslint-plugin-react-hooks": "^7.0.1",
56
+ "eslint-plugin-react-refresh": "^0.5.2",
57
+ "eslint-plugin-storybook": "^10.3.0",
58
+ "globals": "^17.4.0",
59
+ "playwright": "^1.58.2",
60
+ "storybook": "^10.3.0",
61
+ "typescript": "~5.9.3",
62
+ "typescript-eslint": "^8.57.0",
63
+ "vite": "^8.0.7",
64
+ "vitest": "^4.1.0"
65
+ }
66
+ }