@g9tm/lynxcontrols 1.4.2 → 1.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/button/button.css +1 -0
  3. package/dist/button/button.css.map +1 -1
  4. package/dist/button/button.d.ts +2 -0
  5. package/dist/button/button.d.ts.map +1 -1
  6. package/dist/button/button.jsx +3 -2
  7. package/dist/button/button.jsx.map +1 -1
  8. package/dist/fields/outlined-field.d.ts +1 -1
  9. package/dist/fields/outlined-field.d.ts.map +1 -1
  10. package/dist/fields/outlined-field.jsx +13 -3
  11. package/dist/fields/outlined-field.jsx.map +1 -1
  12. package/dist/fields/use-text-input.d.ts.map +1 -1
  13. package/dist/fields/use-text-input.jsx +2 -2
  14. package/dist/fields/use-text-input.jsx.map +1 -1
  15. package/dist/pickers/date-time-picker.d.ts.map +1 -1
  16. package/dist/pickers/date-time-picker.jsx +13 -4
  17. package/dist/pickers/date-time-picker.jsx.map +1 -1
  18. package/dist/pickers/picker-sheet.d.ts +5 -1
  19. package/dist/pickers/picker-sheet.d.ts.map +1 -1
  20. package/dist/pickers/picker-sheet.jsx +3 -17
  21. package/dist/pickers/picker-sheet.jsx.map +1 -1
  22. package/dist/pickers/pickers.css +32 -43
  23. package/dist/pickers/pickers.css.map +1 -1
  24. package/dist/pickers/wheel-column.d.ts +3 -1
  25. package/dist/pickers/wheel-column.d.ts.map +1 -1
  26. package/dist/pickers/wheel-column.jsx +17 -3
  27. package/dist/pickers/wheel-column.jsx.map +1 -1
  28. package/dist/popup/frame.d.ts +6 -0
  29. package/dist/popup/frame.d.ts.map +1 -1
  30. package/dist/popup/frame.jsx +8 -6
  31. package/dist/popup/frame.jsx.map +1 -1
  32. package/dist/popup/popup.css +23 -16
  33. package/dist/popup/popup.css.map +1 -1
  34. package/dist/popup/service.d.ts.map +1 -1
  35. package/dist/popup/service.jsx +3 -2
  36. package/dist/popup/service.jsx.map +1 -1
  37. package/dist/range-slider/range-slider.css +5 -0
  38. package/dist/range-slider/range-slider.css.map +1 -1
  39. package/dist/range-slider/range-slider.jsx +1 -1
  40. package/dist/range-slider/range-slider.jsx.map +1 -1
  41. package/dist/selection/selection-sheet.d.ts.map +1 -1
  42. package/dist/selection/selection-sheet.jsx +4 -2
  43. package/dist/selection/selection-sheet.jsx.map +1 -1
  44. package/dist/selection/selection.css +15 -0
  45. package/dist/selection/selection.css.map +1 -1
  46. package/dist/sheet/frame.d.ts.map +1 -1
  47. package/dist/sheet/frame.jsx +28 -21
  48. package/dist/sheet/frame.jsx.map +1 -1
  49. package/dist/sheet/sheet.css +10 -0
  50. package/dist/sheet/sheet.css.map +1 -1
  51. package/dist/sheet/sizing.d.ts +6 -0
  52. package/dist/sheet/sizing.d.ts.map +1 -1
  53. package/dist/sheet/sizing.jsx +4 -2
  54. package/dist/sheet/sizing.jsx.map +1 -1
  55. package/dist/tabs/model.d.ts +1 -1
  56. package/dist/tabs/model.d.ts.map +1 -1
  57. package/dist/tabs/model.jsx.map +1 -1
  58. package/dist/tabs/tab-view.jsx +1 -1
  59. package/dist/tabs/tab-view.jsx.map +1 -1
  60. package/dist/toggle/chip-group.jsx +8 -2
  61. package/dist/toggle/chip-group.jsx.map +1 -1
  62. package/dist/toggle/switch.jsx +2 -1
  63. package/dist/toggle/switch.jsx.map +1 -1
  64. package/dist/toggle/toggle.css +5 -2
  65. package/dist/toggle/toggle.css.map +1 -1
  66. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"sizing.d.ts","sourceRoot":"","sources":["../../src/sheet/sizing.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sizing.d.ts","sourceRoot":"","sources":["../../src/sheet/sizing.ts"],"names":[],"mappings":"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"}
@@ -36,7 +36,8 @@ function resolveSheetLayout(options, hostHeight, platform = currentPlatform()) {
36
36
  enableDragToClose: false,
37
37
  closeOnOverlayTap: cancelable,
38
38
  fitMaxHeight: Math.max(g9SheetFitMinHeight, Math.floor(hostHeight * ratio)),
39
- fullScreen: false
39
+ fullScreen: false,
40
+ fillBody: false
40
41
  };
41
42
  }
42
43
  const order = [
@@ -69,7 +70,8 @@ function resolveSheetLayout(options, hostHeight, platform = currentPlatform()) {
69
70
  enableDragToClose: cancelable && draggable && !intercepted,
70
71
  closeOnOverlayTap: cancelable,
71
72
  fitMaxHeight: options.expandedFitsContent ? Math.max(g9SheetFitMinHeight, Math.floor(hostHeight * clamp(null != (_options_maxFitToContentHeightRatio = options.maxFitToContentHeightRatio) ? _options_maxFitToContentHeightRatio : 0.75, 0.2, 1))) : null,
72
- fullScreen: 1 === states.length && 'large' === states[0] && full >= 1
73
+ fullScreen: 1 === states.length && 'large' === states[0] && full >= 1,
74
+ fillBody: fixed && !options.expandedFitsContent
73
75
  };
74
76
  }
75
77
  function clampFitHeight(natural, cap, loading) {
@@ -1 +1 @@
1
- {"version":3,"file":"sheet/sizing.jsx","sources":["../../src/sheet/sizing.ts"],"sourcesContent":["import type { G9SheetDetent, G9SheetOptions } from './options';\n\n// Pure sizing rules of the G9 sheet (MAUI G9SheetView + G9BottomSheetHelper sizing; guide 650–980, ADR-0019/0026).\n\n/** MAUI `G9SheetView.MinimizedHeight`: default peek height. */\nexport const g9SheetDefaultPeekHeight = 100;\n/** Absolute minimum of an authoritative fit height (MAUI helper :81). */\nexport const g9SheetFitMinHeight = 80;\n/** Floor while the height is unknown (loading window). */\nexport const g9SheetFitLoadingMinHeight = 180;\n/** Finger travel at release that requests a close (MAUI `DragCloseThreshold`). */\nexport const g9SheetDragCloseThreshold = 72;\n/**\n * Gap between the top safe-area inset and a full-screen sheet's header. MAUI reserves the inset alone\n * (`ResolveFullScreenSheetTopPadding`), but its Android inset is the status-bar band, a few dp below the cutout an\n * immersive Lynx host reports: measured side by side on a Pixel 9 Pro XL (2026-10-10), MAUI's header sat 3 dp lower.\n * (It was 8, which put every full-screen header 5 dp below MAUI's.)\n */\nexport const g9SheetTopSafeAreaBaseGap = 3;\n\nconst clamp = (v: number, lo: number, hi: number) => Math.min(hi, Math.max(lo, v));\n\n/** The platform the app runs on (Lynx `SystemInfo.platform`), lower-cased; `'unknown'` outside Lynx. */\nexport function currentPlatform(): 'android' | 'ios' | 'windows' | 'macos' | 'unknown' {\n const info = (globalThis as { SystemInfo?: { platform?: string } }).SystemInfo;\n const p = (info?.platform ?? '').toLowerCase();\n if (p === 'android') return 'android';\n if (p === 'ios') return 'ios';\n if (p.startsWith('win')) return 'windows';\n if (p === 'macos' || p === 'mac' || p === 'osx') return 'macos';\n return 'unknown';\n}\n\n/** What the adapter passes to lynx-ui `SheetRoot` and how the frame behaves. */\nexport interface G9SheetLayout {\n /** lynx-ui snap points, small to large: pixels, `'NN%'` of the host, or `'fit'`. */\n readonly snapPoints: Array<number | string>;\n readonly initialSnap: number;\n /** One detent, or fit-to-content (MAUI \"fixed\"): no grabber, no detent dragging. */\n readonly fixed: boolean;\n readonly fit: boolean;\n /** Grabber visible (`hasHandle && isDraggable && !fixed`). */\n readonly showHandle: boolean;\n readonly dragDisabled: boolean;\n /** Drag past the lowest detent may request a close. */\n readonly enableDragToClose: boolean;\n /** Backdrop tap requests a close. */\n readonly closeOnOverlayTap: boolean;\n /** The resting body cap for a fit sheet (`maxFitToContentHeightRatio × host`), else `null`. */\n readonly fitMaxHeight: number | null;\n /** Whether the sheet's largest detent is full screen. */\n readonly fullScreen: boolean;\n}\n\nconst pct = (ratio: number) => `${Math.round(ratio * 10000) / 100}%`;\n\n/**\n * Maps G9 options to a lynx-ui layout (the agreed mapping of MAUI `ApplyOptions`): fit → `['fit']` capped at\n * `maxFitToContentHeightRatio`; states → peek pixels / half ratio / full ratio for the allowed detents, the initial\n * detent from `currentState`. The MAUI quirks (a hidden 100 dp detent under `[medium, large]`, a peek-only sheet that\n * may expand) are not carried over: the detents are exactly `states` (docs/sheets.md \"deliberate differences\").\n */\nexport function resolveSheetLayout(\n options: G9SheetOptions,\n hostHeight: number,\n platform = currentPlatform(),\n): G9SheetLayout {\n const fit = options.sizeMode === 'fitToContent';\n const draggable = options.isDraggable !== false;\n const cancelable = options.isCancelable !== false;\n const intercepted = options.onBackRequested != null;\n if (fit) {\n const ratio = clamp(options.maxFitToContentHeightRatio ?? 0.75, 0.2, 1);\n return {\n snapPoints: ['fit'],\n initialSnap: 0,\n fixed: true,\n fit: true,\n showHandle: false,\n dragDisabled: true,\n enableDragToClose: false,\n closeOnOverlayTap: cancelable,\n fitMaxHeight: Math.max(g9SheetFitMinHeight, Math.floor(hostHeight * ratio)),\n fullScreen: false,\n };\n }\n const order: G9SheetDetent[] = ['peek', 'medium', 'large'];\n const requested = options.states && options.states.length > 0 ? options.states : ['medium', 'large'];\n const states = order.filter((d) => requested.includes(d));\n const androidHalf = platform === 'android' ? options.androidHalfExpandedRatio : null;\n const half = clamp(androidHalf ?? options.halfExpandedRatio ?? 0.5, 0.1, 0.9);\n const full = clamp(options.fullExpandedRatio ?? 1, 0.1, 1);\n const peek = Math.max(1, options.collapsedHeight ?? options.peekHeight ?? g9SheetDefaultPeekHeight);\n const snap = (d: G9SheetDetent): number | string =>\n d === 'peek' ? Math.min(peek, Math.max(1, hostHeight)) : d === 'medium' ? pct(half) : pct(full);\n const snapPoints = states.map(snap);\n if (options.expandedFitsContent && snapPoints.length > 0) snapPoints[snapPoints.length - 1] = 'fit';\n const current = options.currentState ?? 'medium';\n const index = states.indexOf(current);\n const fixed = states.length <= 1;\n return {\n snapPoints,\n initialSnap: index >= 0 ? index : 0,\n fixed,\n fit: false,\n showHandle: options.hasHandle !== false && draggable && !fixed,\n dragDisabled: !draggable || intercepted,\n enableDragToClose: cancelable && draggable && !intercepted,\n closeOnOverlayTap: cancelable,\n fitMaxHeight: options.expandedFitsContent\n ? Math.max(\n g9SheetFitMinHeight,\n Math.floor(hostHeight * clamp(options.maxFitToContentHeightRatio ?? 0.75, 0.2, 1)),\n )\n : null,\n fullScreen: states.length === 1 && states[0] === 'large' && full >= 1,\n };\n}\n\n/** Clamps a measured fit body height: `[80, cap]`, or `[180, cap]` while loading (MAUI fit engine). */\nexport function clampFitHeight(natural: number, cap: number, loading: boolean): number {\n const min = loading ? g9SheetFitLoadingMinHeight : g9SheetFitMinHeight;\n if (!(natural > 0)) return Math.min(min, cap);\n return clamp(natural, Math.min(min, cap), cap);\n}\n\n/**\n * Scrim opacity for a visible ratio (MAUI helper overlay rule): between the smallest and the largest detent it runs\n * from `min` to `max`; a single detent uses `max`; below the smallest detent it fades with the ratio.\n */\nexport function sheetOverlayOpacity(\n visibleRatio: number,\n detentRatios: ReadonlyArray<number>,\n min: number,\n max: number,\n): number {\n if (detentRatios.length === 0) return max;\n const lo = Math.min(...detentRatios);\n const hi = Math.max(...detentRatios);\n if (visibleRatio < lo) return (detentRatios.length === 1 ? max : min) * clamp(visibleRatio / lo, 0, 1);\n if (hi === lo) return max;\n return min + (max - min) * clamp((visibleRatio - lo) / (hi - lo), 0, 1);\n}\n\n/** The page recede behind a tall primary sheet (MAUI backdrop card): scale 1 → 0.93, translateY 0 → 12. */\nexport function backdropCardTransform(visibleRatio: number, threshold = 0.75): { scale: number; translateY: number } {\n const raw = clamp((visibleRatio - threshold) / Math.max(1e-6, 1 - threshold), 0, 1);\n const progress = raw <= 0 ? 0 : 1 - (1 - raw) ** (1 / 3);\n return { scale: 1 - 0.07 * progress, translateY: 12 * progress };\n}\n\n/**\n * The visible share of the host a detent sheet covers: its resting height when known, else its opening detent\n * (peek = peek height, medium = half ratio, large = full ratio; `expandedFitsContent` large counts as the fit cap).\n */\nexport function sheetVisibleRatio(\n options: G9SheetOptions,\n hostHeight: number,\n restingHeightPx: number | null,\n platform = currentPlatform(),\n): number {\n const host = Math.max(1, hostHeight);\n if (restingHeightPx != null) return clamp(restingHeightPx / host, 0, 1);\n const layout = resolveSheetLayout(options, host, platform);\n const point = layout.snapPoints[layout.initialSnap];\n if (typeof point === 'number') return clamp(point / host, 0, 1);\n if (point === 'fit' || point === undefined) return (layout.fitMaxHeight ?? 0) / host;\n return clamp(Number.parseFloat(point) / 100, 0, 1);\n}\n\n/** How far a receded parent sheet sinks: `max(24, visible × 0.25)` (MAUI stack recede). */\nexport function parentRecedeOffset(visibleHeight: number): number {\n return Math.max(24, visibleHeight * 0.25);\n}\n\n/** Top padding of a full-screen sheet (MAUI safe-area rule). */\nexport function sheetTopPadding(options: G9SheetOptions, topInset: number): number {\n if (options.topSafeAreaPaddingOverride != null) return Math.max(0, options.topSafeAreaPaddingOverride);\n const inset = options.useTopSafeAreaPadding === false ? 0 : Math.max(0, topInset);\n return inset + g9SheetTopSafeAreaBaseGap + Math.max(0, options.additionalTopSafeAreaPadding ?? 0);\n}\n\n/**\n * Top padding of the sheet surface where it rests. A full-screen sheet always reserves {@link sheetTopPadding}. A\n * detent sheet resting at its `large` detent reserves it too when that detent reaches under the top inset (MAUI\n * state-aware top padding, `G9BottomSheetHelper.ApplyStateAwareTopPadding`): its content never sits under the status\n * bar or the camera cutout. Opted out like MAUI: `useTopSafeAreaPadding: false` with no additional/override padding.\n */\nexport function sheetSurfaceTopPadding(\n options: G9SheetOptions,\n layout: Pick<G9SheetLayout, 'fullScreen' | 'fit'>,\n resting: G9SheetDetent | 'fit',\n topInset: number,\n hostHeight: number,\n): number {\n if (layout.fullScreen) return sheetTopPadding(options, topInset);\n if (layout.fit || resting !== 'large') return 0;\n const opted =\n options.useTopSafeAreaPadding !== false ||\n (options.additionalTopSafeAreaPadding ?? 0) > 0 ||\n (options.topSafeAreaPaddingOverride ?? 0) > 0;\n if (!opted) return 0;\n const full = clamp(options.fullExpandedRatio ?? 1, 0.1, 1);\n const top = Math.max(0, hostHeight) * (1 - full);\n return top < Math.max(0, topInset) + g9SheetTopSafeAreaBaseGap ? sheetTopPadding(options, topInset) : 0;\n}\n\n/**\n * Bottom padding of the sheet surface: the host's bottom safe inset (a visible navigation bar or gesture handle), so\n * the last row and the footer stay above it. `useBottomSafeAreaPadding: false` lets content run under it. An\n * immersive host (system bars hidden, as the AgriPad MAUI app) reports 0 and nothing changes.\n */\nexport function sheetSurfaceBottomPadding(options: G9SheetOptions, bottomInset: number): number {\n return options.useBottomSafeAreaPadding === false ? 0 : Math.max(0, bottomInset);\n}\n\n/**\n * Size-scaled animation duration (MAUI `SizeScaledAnimationDuration`): a partial traversal takes the matching slice\n * of the full duration (at least 1 ms unless the duration is 0).\n */\nexport function scaledSheetDuration(durationMs: number, traversedRatio: number, sizeScaled: boolean): number {\n if (durationMs <= 0) return 0;\n if (!sizeScaled) return durationMs;\n return Math.max(1, Math.round(durationMs * clamp(traversedRatio, 0, 1)));\n}\n\n/** Platform size constraints of the sheet surface (MAUI Android/Windows options, applied where they belong). */\nexport function platformSheetConstraints(\n options: G9SheetOptions,\n platform = currentPlatform(),\n): Record<string, string> {\n const px = (n: number) => `${n}px`;\n const style: Record<string, string> = {};\n if (platform === 'android') {\n if (options.androidMaxWidth != null) style.maxWidth = px(options.androidMaxWidth);\n if (options.androidMaxHeight != null) style.maxHeight = px(options.androidMaxHeight);\n if (options.androidMargin) {\n const [l, t, r, b] = options.androidMargin;\n style.margin = `${t}px ${r}px ${b}px ${l}px`;\n }\n }\n if (platform === 'windows') {\n if (options.windowsMaxWidth != null) style.maxWidth = px(options.windowsMaxWidth);\n if (options.windowsMaxHeight != null) style.maxHeight = px(options.windowsMaxHeight);\n if (options.windowsMinWidth != null) style.minWidth = px(options.windowsMinWidth);\n if (options.windowsMinHeight != null) style.minHeight = px(options.windowsMinHeight);\n }\n if (options.contentWidthMode === 'custom') {\n style.width = px(options.contentWidth ?? 300);\n style.alignSelf = 'center';\n }\n return style;\n}\n"],"names":["g9SheetDefaultPeekHeight","g9SheetFitMinHeight","g9SheetFitLoadingMinHeight","g9SheetDragCloseThreshold","g9SheetTopSafeAreaBaseGap","clamp","v","lo","hi","Math","currentPlatform","info","globalThis","p","pct","ratio","resolveSheetLayout","options","hostHeight","platform","_ref","_options_fullExpandedRatio","_ref1","_options_currentState","_options_maxFitToContentHeightRatio","fit","draggable","cancelable","intercepted","_options_maxFitToContentHeightRatio1","order","requested","states","d","androidHalf","half","full","peek","snap","snapPoints","current","index","fixed","clampFitHeight","natural","cap","loading","min","sheetOverlayOpacity","visibleRatio","detentRatios","max","backdropCardTransform","threshold","raw","progress","sheetVisibleRatio","restingHeightPx","_layout_fitMaxHeight","host","layout","point","undefined","Number","parentRecedeOffset","visibleHeight","sheetTopPadding","topInset","_options_additionalTopSafeAreaPadding","inset","sheetSurfaceTopPadding","resting","_options_topSafeAreaPaddingOverride","opted","top","sheetSurfaceBottomPadding","bottomInset","scaledSheetDuration","durationMs","traversedRatio","sizeScaled","platformSheetConstraints","px","n","style","l","t","r","b","_options_contentWidth"],"mappings":"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"}
1
+ {"version":3,"file":"sheet/sizing.jsx","sources":["../../src/sheet/sizing.ts"],"sourcesContent":["import type { G9SheetDetent, G9SheetOptions } from './options';\n\n// Pure sizing rules of the G9 sheet (MAUI G9SheetView + G9BottomSheetHelper sizing; guide 650–980, ADR-0019/0026).\n\n/** MAUI `G9SheetView.MinimizedHeight`: default peek height. */\nexport const g9SheetDefaultPeekHeight = 100;\n/** Absolute minimum of an authoritative fit height (MAUI helper :81). */\nexport const g9SheetFitMinHeight = 80;\n/** Floor while the height is unknown (loading window). */\nexport const g9SheetFitLoadingMinHeight = 180;\n/** Finger travel at release that requests a close (MAUI `DragCloseThreshold`). */\nexport const g9SheetDragCloseThreshold = 72;\n/**\n * Gap between the top safe-area inset and a full-screen sheet's header. MAUI reserves the inset alone\n * (`ResolveFullScreenSheetTopPadding`), but its Android inset is the status-bar band, a few dp below the cutout an\n * immersive Lynx host reports: measured side by side on a Pixel 9 Pro XL (2026-10-10), MAUI's header sat 3 dp lower.\n * (It was 8, which put every full-screen header 5 dp below MAUI's.)\n */\nexport const g9SheetTopSafeAreaBaseGap = 3;\n\nconst clamp = (v: number, lo: number, hi: number) => Math.min(hi, Math.max(lo, v));\n\n/** The platform the app runs on (Lynx `SystemInfo.platform`), lower-cased; `'unknown'` outside Lynx. */\nexport function currentPlatform(): 'android' | 'ios' | 'windows' | 'macos' | 'unknown' {\n const info = (globalThis as { SystemInfo?: { platform?: string } }).SystemInfo;\n const p = (info?.platform ?? '').toLowerCase();\n if (p === 'android') return 'android';\n if (p === 'ios') return 'ios';\n if (p.startsWith('win')) return 'windows';\n if (p === 'macos' || p === 'mac' || p === 'osx') return 'macos';\n return 'unknown';\n}\n\n/** What the adapter passes to lynx-ui `SheetRoot` and how the frame behaves. */\nexport interface G9SheetLayout {\n /** lynx-ui snap points, small to large: pixels, `'NN%'` of the host, or `'fit'`. */\n readonly snapPoints: Array<number | string>;\n readonly initialSnap: number;\n /** One detent, or fit-to-content (MAUI \"fixed\"): no grabber, no detent dragging. */\n readonly fixed: boolean;\n readonly fit: boolean;\n /** Grabber visible (`hasHandle && isDraggable && !fixed`). */\n readonly showHandle: boolean;\n readonly dragDisabled: boolean;\n /** Drag past the lowest detent may request a close. */\n readonly enableDragToClose: boolean;\n /** Backdrop tap requests a close. */\n readonly closeOnOverlayTap: boolean;\n /** The resting body cap for a fit sheet (`maxFitToContentHeightRatio × host`), else `null`. */\n readonly fitMaxHeight: number | null;\n /** Whether the sheet's largest detent is full screen. */\n readonly fullScreen: boolean;\n /**\n * The body fills the surface, so the footer rests at the sheet's bottom edge: a full-screen sheet, or one fixed detent\n * (`states: ['large']` with `fullExpandedRatio: 0.9`) — its surface has a definite height. A sheet that can move between\n * detents or fits its content keeps a body as tall as the content.\n */\n readonly fillBody: boolean;\n}\n\nconst pct = (ratio: number) => `${Math.round(ratio * 10000) / 100}%`;\n\n/**\n * Maps G9 options to a lynx-ui layout (the agreed mapping of MAUI `ApplyOptions`): fit → `['fit']` capped at\n * `maxFitToContentHeightRatio`; states → peek pixels / half ratio / full ratio for the allowed detents, the initial\n * detent from `currentState`. The MAUI quirks (a hidden 100 dp detent under `[medium, large]`, a peek-only sheet that\n * may expand) are not carried over: the detents are exactly `states` (docs/sheets.md \"deliberate differences\").\n */\nexport function resolveSheetLayout(\n options: G9SheetOptions,\n hostHeight: number,\n platform = currentPlatform(),\n): G9SheetLayout {\n const fit = options.sizeMode === 'fitToContent';\n const draggable = options.isDraggable !== false;\n const cancelable = options.isCancelable !== false;\n const intercepted = options.onBackRequested != null;\n if (fit) {\n const ratio = clamp(options.maxFitToContentHeightRatio ?? 0.75, 0.2, 1);\n return {\n snapPoints: ['fit'],\n initialSnap: 0,\n fixed: true,\n fit: true,\n showHandle: false,\n dragDisabled: true,\n enableDragToClose: false,\n closeOnOverlayTap: cancelable,\n fitMaxHeight: Math.max(g9SheetFitMinHeight, Math.floor(hostHeight * ratio)),\n fullScreen: false,\n fillBody: false,\n };\n }\n const order: G9SheetDetent[] = ['peek', 'medium', 'large'];\n const requested = options.states && options.states.length > 0 ? options.states : ['medium', 'large'];\n const states = order.filter((d) => requested.includes(d));\n const androidHalf = platform === 'android' ? options.androidHalfExpandedRatio : null;\n const half = clamp(androidHalf ?? options.halfExpandedRatio ?? 0.5, 0.1, 0.9);\n const full = clamp(options.fullExpandedRatio ?? 1, 0.1, 1);\n const peek = Math.max(1, options.collapsedHeight ?? options.peekHeight ?? g9SheetDefaultPeekHeight);\n const snap = (d: G9SheetDetent): number | string =>\n d === 'peek' ? Math.min(peek, Math.max(1, hostHeight)) : d === 'medium' ? pct(half) : pct(full);\n const snapPoints = states.map(snap);\n if (options.expandedFitsContent && snapPoints.length > 0) snapPoints[snapPoints.length - 1] = 'fit';\n const current = options.currentState ?? 'medium';\n const index = states.indexOf(current);\n const fixed = states.length <= 1;\n return {\n snapPoints,\n initialSnap: index >= 0 ? index : 0,\n fixed,\n fit: false,\n showHandle: options.hasHandle !== false && draggable && !fixed,\n dragDisabled: !draggable || intercepted,\n enableDragToClose: cancelable && draggable && !intercepted,\n closeOnOverlayTap: cancelable,\n fitMaxHeight: options.expandedFitsContent\n ? Math.max(\n g9SheetFitMinHeight,\n Math.floor(hostHeight * clamp(options.maxFitToContentHeightRatio ?? 0.75, 0.2, 1)),\n )\n : null,\n fullScreen: states.length === 1 && states[0] === 'large' && full >= 1,\n fillBody: fixed && !options.expandedFitsContent,\n };\n}\n\n/** Clamps a measured fit body height: `[80, cap]`, or `[180, cap]` while loading (MAUI fit engine). */\nexport function clampFitHeight(natural: number, cap: number, loading: boolean): number {\n const min = loading ? g9SheetFitLoadingMinHeight : g9SheetFitMinHeight;\n if (!(natural > 0)) return Math.min(min, cap);\n return clamp(natural, Math.min(min, cap), cap);\n}\n\n/**\n * Scrim opacity for a visible ratio (MAUI helper overlay rule): between the smallest and the largest detent it runs\n * from `min` to `max`; a single detent uses `max`; below the smallest detent it fades with the ratio.\n */\nexport function sheetOverlayOpacity(\n visibleRatio: number,\n detentRatios: ReadonlyArray<number>,\n min: number,\n max: number,\n): number {\n if (detentRatios.length === 0) return max;\n const lo = Math.min(...detentRatios);\n const hi = Math.max(...detentRatios);\n if (visibleRatio < lo) return (detentRatios.length === 1 ? max : min) * clamp(visibleRatio / lo, 0, 1);\n if (hi === lo) return max;\n return min + (max - min) * clamp((visibleRatio - lo) / (hi - lo), 0, 1);\n}\n\n/** The page recede behind a tall primary sheet (MAUI backdrop card): scale 1 → 0.93, translateY 0 → 12. */\nexport function backdropCardTransform(visibleRatio: number, threshold = 0.75): { scale: number; translateY: number } {\n const raw = clamp((visibleRatio - threshold) / Math.max(1e-6, 1 - threshold), 0, 1);\n const progress = raw <= 0 ? 0 : 1 - (1 - raw) ** (1 / 3);\n return { scale: 1 - 0.07 * progress, translateY: 12 * progress };\n}\n\n/**\n * The visible share of the host a detent sheet covers: its resting height when known, else its opening detent\n * (peek = peek height, medium = half ratio, large = full ratio; `expandedFitsContent` large counts as the fit cap).\n */\nexport function sheetVisibleRatio(\n options: G9SheetOptions,\n hostHeight: number,\n restingHeightPx: number | null,\n platform = currentPlatform(),\n): number {\n const host = Math.max(1, hostHeight);\n if (restingHeightPx != null) return clamp(restingHeightPx / host, 0, 1);\n const layout = resolveSheetLayout(options, host, platform);\n const point = layout.snapPoints[layout.initialSnap];\n if (typeof point === 'number') return clamp(point / host, 0, 1);\n if (point === 'fit' || point === undefined) return (layout.fitMaxHeight ?? 0) / host;\n return clamp(Number.parseFloat(point) / 100, 0, 1);\n}\n\n/** How far a receded parent sheet sinks: `max(24, visible × 0.25)` (MAUI stack recede). */\nexport function parentRecedeOffset(visibleHeight: number): number {\n return Math.max(24, visibleHeight * 0.25);\n}\n\n/** Top padding of a full-screen sheet (MAUI safe-area rule). */\nexport function sheetTopPadding(options: G9SheetOptions, topInset: number): number {\n if (options.topSafeAreaPaddingOverride != null) return Math.max(0, options.topSafeAreaPaddingOverride);\n const inset = options.useTopSafeAreaPadding === false ? 0 : Math.max(0, topInset);\n return inset + g9SheetTopSafeAreaBaseGap + Math.max(0, options.additionalTopSafeAreaPadding ?? 0);\n}\n\n/**\n * Top padding of the sheet surface where it rests. A full-screen sheet always reserves {@link sheetTopPadding}. A\n * detent sheet resting at its `large` detent reserves it too when that detent reaches under the top inset (MAUI\n * state-aware top padding, `G9BottomSheetHelper.ApplyStateAwareTopPadding`): its content never sits under the status\n * bar or the camera cutout. Opted out like MAUI: `useTopSafeAreaPadding: false` with no additional/override padding.\n */\nexport function sheetSurfaceTopPadding(\n options: G9SheetOptions,\n layout: Pick<G9SheetLayout, 'fullScreen' | 'fit'>,\n resting: G9SheetDetent | 'fit',\n topInset: number,\n hostHeight: number,\n): number {\n if (layout.fullScreen) return sheetTopPadding(options, topInset);\n if (layout.fit || resting !== 'large') return 0;\n const opted =\n options.useTopSafeAreaPadding !== false ||\n (options.additionalTopSafeAreaPadding ?? 0) > 0 ||\n (options.topSafeAreaPaddingOverride ?? 0) > 0;\n if (!opted) return 0;\n const full = clamp(options.fullExpandedRatio ?? 1, 0.1, 1);\n const top = Math.max(0, hostHeight) * (1 - full);\n return top < Math.max(0, topInset) + g9SheetTopSafeAreaBaseGap ? sheetTopPadding(options, topInset) : 0;\n}\n\n/**\n * Bottom padding of the sheet surface: the host's bottom safe inset (a visible navigation bar or gesture handle), so\n * the last row and the footer stay above it. `useBottomSafeAreaPadding: false` lets content run under it. An\n * immersive host (system bars hidden, as the AgriPad MAUI app) reports 0 and nothing changes.\n */\nexport function sheetSurfaceBottomPadding(options: G9SheetOptions, bottomInset: number): number {\n return options.useBottomSafeAreaPadding === false ? 0 : Math.max(0, bottomInset);\n}\n\n/**\n * Size-scaled animation duration (MAUI `SizeScaledAnimationDuration`): a partial traversal takes the matching slice\n * of the full duration (at least 1 ms unless the duration is 0).\n */\nexport function scaledSheetDuration(durationMs: number, traversedRatio: number, sizeScaled: boolean): number {\n if (durationMs <= 0) return 0;\n if (!sizeScaled) return durationMs;\n return Math.max(1, Math.round(durationMs * clamp(traversedRatio, 0, 1)));\n}\n\n/** Platform size constraints of the sheet surface (MAUI Android/Windows options, applied where they belong). */\nexport function platformSheetConstraints(\n options: G9SheetOptions,\n platform = currentPlatform(),\n): Record<string, string> {\n const px = (n: number) => `${n}px`;\n const style: Record<string, string> = {};\n if (platform === 'android') {\n if (options.androidMaxWidth != null) style.maxWidth = px(options.androidMaxWidth);\n if (options.androidMaxHeight != null) style.maxHeight = px(options.androidMaxHeight);\n if (options.androidMargin) {\n const [l, t, r, b] = options.androidMargin;\n style.margin = `${t}px ${r}px ${b}px ${l}px`;\n }\n }\n if (platform === 'windows') {\n if (options.windowsMaxWidth != null) style.maxWidth = px(options.windowsMaxWidth);\n if (options.windowsMaxHeight != null) style.maxHeight = px(options.windowsMaxHeight);\n if (options.windowsMinWidth != null) style.minWidth = px(options.windowsMinWidth);\n if (options.windowsMinHeight != null) style.minHeight = px(options.windowsMinHeight);\n }\n if (options.contentWidthMode === 'custom') {\n style.width = px(options.contentWidth ?? 300);\n style.alignSelf = 'center';\n }\n return style;\n}\n"],"names":["g9SheetDefaultPeekHeight","g9SheetFitMinHeight","g9SheetFitLoadingMinHeight","g9SheetDragCloseThreshold","g9SheetTopSafeAreaBaseGap","clamp","v","lo","hi","Math","currentPlatform","info","globalThis","p","pct","ratio","resolveSheetLayout","options","hostHeight","platform","_ref","_options_fullExpandedRatio","_ref1","_options_currentState","_options_maxFitToContentHeightRatio","fit","draggable","cancelable","intercepted","_options_maxFitToContentHeightRatio1","order","requested","states","d","androidHalf","half","full","peek","snap","snapPoints","current","index","fixed","clampFitHeight","natural","cap","loading","min","sheetOverlayOpacity","visibleRatio","detentRatios","max","backdropCardTransform","threshold","raw","progress","sheetVisibleRatio","restingHeightPx","_layout_fitMaxHeight","host","layout","point","undefined","Number","parentRecedeOffset","visibleHeight","sheetTopPadding","topInset","_options_additionalTopSafeAreaPadding","inset","sheetSurfaceTopPadding","resting","_options_topSafeAreaPaddingOverride","opted","top","sheetSurfaceBottomPadding","bottomInset","scaledSheetDuration","durationMs","traversedRatio","sizeScaled","platformSheetConstraints","px","n","style","l","t","r","b","_options_contentWidth"],"mappings":"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"}
@@ -26,7 +26,7 @@ export interface G9TabItem {
26
26
  readonly badgeText?: string | null;
27
27
  /** A 7×7 dot instead of a pill. */
28
28
  readonly badgeDot?: boolean;
29
- /** Badge fill. Default `var(--g9-error)`. */
29
+ /** Badge fill. Default `var(--g9-primary)` for the pill, `var(--g9-error)` for the dot (MAUI CreateBadge). */
30
30
  readonly badgeColor?: string | null;
31
31
  /** The body shown while this tab is active (MAUI `TabContent`); a function is called when the tab realizes. */
32
32
  readonly content?: ReactNode | (() => ReactNode);
@@ -1 +1 @@
1
- {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/tabs/model.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/tabs/model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,aAAa,EAAE,KAAK,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AACzG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,uDAAuD;AACvD,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,MAAM,CAAC;AAC/C,4GAA4G;AAC5G,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,YAAY,CAAC;AAC/C,4EAA4E;AAC5E,MAAM,MAAM,gBAAgB,GAAG,KAAK,GAAG,QAAQ,CAAC;AAEhD;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,+GAA+G;IAC/G,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,IAAI,CAAC,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IACtC,4DAA4D;IAC5D,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,kEAAkE;IAClE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,mCAAmC;IACnC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,8GAA8G;IAC9G,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,+GAA+G;IAC/G,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,GAAG,CAAC,MAAM,SAAS,CAAC,CAAC;CAClD;AAED,2FAA2F;AAC3F,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;EAe3B,CAAC;AAEH,gHAAgH;AAChH,wBAAgB,iBAAiB,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAI9E;AAED,wDAAwD;AACxD,wBAAgB,MAAM,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED,gHAAgH;AAChH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,SAAS,EAAE,eAAe,EAAE,iBAAiB,GAAG,KAAK,GAAG,MAAM,GAAG,IAAI,CAKnG;AAED,6GAA6G;AAC7G,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,MAAM,EAAE,CAIpE;AAED,mHAAmH;AACnH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,MAAM,CAIxF;AAED,sFAAsF;AACtF,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,MAAM,CAItE;AAED;;;;GAIG;AACH,qBAAa,aAAa;IAKZ,OAAO,CAAC,QAAQ,CAAC,KAAK;IAJlC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAC9C,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,UAAU,CAAK;gBAEM,KAAK,EAAE,MAAM,OAAO;IAEjD,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIhC,+GAA+G;IAC/G,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM;IAUvC,oGAAoG;IACpG,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAO/B,qFAAqF;IACrF,MAAM,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI;IAMrC,IAAI,UAAU,IAAI,MAAM,GAAG,IAAI,CAE9B;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"tabs/model.jsx","sources":["../../src/tabs/model.ts"],"sourcesContent":["import { formatNumber, type G9ImageSource, type G9NumberingSystem } from '@g9tm/lynxcontrols-foundation';\nimport type { ReactNode } from '@lynx-js/react';\nimport type { G9IconInput } from '../icon/icon';\n\n/** Visual treatment of the bar (MAUI `G9TabStyle`). */\nexport type G9TabStyle = 'underlined' | 'pill';\n/** Cell sizing (MAUI `G9TabMode`): equal full-width cells, or auto-width cells in a horizontal scroller. */\nexport type G9TabMode = 'fixed' | 'scrollable';\n/** Where the bar sits relative to the content (MAUI `G9TabBarPosition`). */\nexport type G9TabBarPosition = 'top' | 'bottom';\n\n/**\n * One tab (MAUI `G9TabItem`). A plain descriptor: change a field and re-render, and only that cell updates (MAUI\n * `VisualChanged` + in-place `UpdateCellContent`). Icon slot precedence as everywhere: emoji → icon → image path →\n * image source.\n */\nexport interface G9TabItem {\n /** Stable identity; keeps a realized body (and its state) with its tab across reorders. Default: the index. */\n readonly key?: string;\n readonly text?: string | null;\n readonly emoji?: string | null;\n readonly icon?: G9IconInput;\n readonly imagePath?: string | null;\n readonly image?: G9ImageSource | null;\n /** Inline pill badge with the formatted count; 0 = none. */\n readonly badgeCount?: number;\n /** Inline pill badge with custom text; wins over `badgeCount`. */\n readonly badgeText?: string | null;\n /** A 7×7 dot instead of a pill. */\n readonly badgeDot?: boolean;\n /** Badge fill. Default `var(--g9-error)`. */\n readonly badgeColor?: string | null;\n /** The body shown while this tab is active (MAUI `TabContent`); a function is called when the tab realizes. */\n readonly content?: ReactNode | (() => ReactNode);\n}\n\n/** Tab view metrics (MAUI `G9Metrics` tab constants and `G9TabView` private constants). */\nexport const g9TabViewMetrics = Object.freeze({\n tabHeight: 52,\n barInnerPadding: 4,\n barRadius: 26,\n pillVerticalInset: 2,\n pillRadius: 18,\n barToContentGap: 8,\n underlineThickness: 2.5,\n edgeLineThickness: 1,\n separatorVerticalInset: 12,\n indicatorMoveMs: 240,\n contentTransitionMs: 160,\n contentSlidePx: 6,\n lazyRealizeDelayMs: 240,\n contentPadding: Object.freeze([10, 10, 10, 0] as const),\n});\n\n/** The index actually shown: the selection clamped into the items (MAUI `EffectiveIndex`); -1 with no items. */\nexport function effectiveTabIndex(selectedIndex: number, count: number): number {\n if (count <= 0) return -1;\n if (!Number.isFinite(selectedIndex)) return 0;\n return Math.min(Math.max(Math.trunc(selectedIndex), 0), count - 1);\n}\n\n/** The identity of a tab: its `key`, else its index. */\nexport function tabKey(item: G9TabItem, index: number): string {\n return item.key ?? `#${index}`;\n}\n\n/** The inline badge of a tab: `null`, `'dot'`, or the pill text (custom text wins; counts are `N0`-grouped). */\nexport function tabBadge(item: G9TabItem, numberingSystem: G9NumberingSystem): 'dot' | string | null {\n if (item.badgeText != null && item.badgeText.trim() !== '') return item.badgeText;\n const count = item.badgeCount ?? 0;\n if (Number.isFinite(count) && count > 0) return formatNumber(Math.trunc(count), { numberingSystem });\n return item.badgeDot ? 'dot' : null;\n}\n\n/** Visual (left-to-right) order of the cells: reversed in RTL so the first tab sits at the reading start. */\nexport function tabVisualOrder(count: number, rtl: boolean): number[] {\n const order: number[] = [];\n for (let i = 0; i < count; i++) order.push(rtl ? count - 1 - i : i);\n return order;\n}\n\n/** Content slide start offset: forward arrives from the trailing edge, back from the leading edge; RTL mirrors. */\nexport function tabContentSlideFrom(previous: number, next: number, rtl: boolean): number {\n const forward = previous < 0 || next > previous;\n const sign = (forward ? 1 : -1) * (rtl ? -1 : 1);\n return g9TabViewMetrics.contentSlidePx * sign;\n}\n\n/** CSS padding from a MAUI-style thickness: one value, `[h, v]` or `[l, t, r, b]`. */\nexport function thicknessCss(value: number | readonly number[]): string {\n const parts = typeof value === 'number' ? [value] : value;\n const [l = 0, t = l, r = l, b = t] = parts.length === 2 ? [parts[0], parts[1], parts[0], parts[1]] : parts;\n return `${t}px ${r}px ${b}px ${l}px`;\n}\n\n/**\n * Lazy-once realization (MAUI \"Lazy tab-body rendering\"): a body is built on its first activation, after\n * `lazyRealizeDelayMs` (a spinner meanwhile), and then stays built, hidden while inactive, so its state survives. A\n * pending token makes rapid switches realize only the latest selection. `eager` builds everything up front.\n */\nexport class G9TabRealizer {\n private readonly realized = new Set<string>();\n private pending: string | null = null;\n private generation = 0;\n\n constructor(private readonly eager: () => boolean) {}\n\n isRealized(key: string): boolean {\n return this.eager() || this.realized.has(key);\n }\n\n /** Marks `key` active: returns `'shown'` when it is already built, else a token to pass to {@link realize}. */\n activate(key: string): 'shown' | number {\n if (this.isRealized(key)) {\n this.pending = null;\n return 'shown';\n }\n this.pending = key;\n this.generation += 1;\n return this.generation;\n }\n\n /** Realizes the pending key when `token` is still the latest activation; returns whether it did. */\n realize(token: number): boolean {\n if (token !== this.generation || this.pending === null) return false;\n this.realized.add(this.pending);\n this.pending = null;\n return true;\n }\n\n /** Drops keys that are no longer in the items (a removed tab's body is released). */\n retain(keys: readonly string[]): void {\n const live = new Set(keys);\n for (const key of Array.from(this.realized)) if (!live.has(key)) this.realized.delete(key);\n if (this.pending !== null && !live.has(this.pending)) this.pending = null;\n }\n\n get pendingKey(): string | null {\n return this.pending;\n }\n}\n"],"names":["g9TabViewMetrics","Object","effectiveTabIndex","selectedIndex","count","Number","Math","tabKey","item","index","_item_key","tabBadge","numberingSystem","_item_badgeCount","formatNumber","tabVisualOrder","rtl","order","i","tabContentSlideFrom","previous","next","forward","sign","thicknessCss","value","parts","l","t","r","b","G9TabRealizer","key","token","keys","live","Set","Array","eager"],"mappings":"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"}
1
+ {"version":3,"file":"tabs/model.jsx","sources":["../../src/tabs/model.ts"],"sourcesContent":["import { formatNumber, type G9ImageSource, type G9NumberingSystem } from '@g9tm/lynxcontrols-foundation';\nimport type { ReactNode } from '@lynx-js/react';\nimport type { G9IconInput } from '../icon/icon';\n\n/** Visual treatment of the bar (MAUI `G9TabStyle`). */\nexport type G9TabStyle = 'underlined' | 'pill';\n/** Cell sizing (MAUI `G9TabMode`): equal full-width cells, or auto-width cells in a horizontal scroller. */\nexport type G9TabMode = 'fixed' | 'scrollable';\n/** Where the bar sits relative to the content (MAUI `G9TabBarPosition`). */\nexport type G9TabBarPosition = 'top' | 'bottom';\n\n/**\n * One tab (MAUI `G9TabItem`). A plain descriptor: change a field and re-render, and only that cell updates (MAUI\n * `VisualChanged` + in-place `UpdateCellContent`). Icon slot precedence as everywhere: emoji → icon → image path →\n * image source.\n */\nexport interface G9TabItem {\n /** Stable identity; keeps a realized body (and its state) with its tab across reorders. Default: the index. */\n readonly key?: string;\n readonly text?: string | null;\n readonly emoji?: string | null;\n readonly icon?: G9IconInput;\n readonly imagePath?: string | null;\n readonly image?: G9ImageSource | null;\n /** Inline pill badge with the formatted count; 0 = none. */\n readonly badgeCount?: number;\n /** Inline pill badge with custom text; wins over `badgeCount`. */\n readonly badgeText?: string | null;\n /** A 7×7 dot instead of a pill. */\n readonly badgeDot?: boolean;\n /** Badge fill. Default `var(--g9-primary)` for the pill, `var(--g9-error)` for the dot (MAUI CreateBadge). */\n readonly badgeColor?: string | null;\n /** The body shown while this tab is active (MAUI `TabContent`); a function is called when the tab realizes. */\n readonly content?: ReactNode | (() => ReactNode);\n}\n\n/** Tab view metrics (MAUI `G9Metrics` tab constants and `G9TabView` private constants). */\nexport const g9TabViewMetrics = Object.freeze({\n tabHeight: 52,\n barInnerPadding: 4,\n barRadius: 26,\n pillVerticalInset: 2,\n pillRadius: 18,\n barToContentGap: 8,\n underlineThickness: 2.5,\n edgeLineThickness: 1,\n separatorVerticalInset: 12,\n indicatorMoveMs: 240,\n contentTransitionMs: 160,\n contentSlidePx: 6,\n lazyRealizeDelayMs: 240,\n contentPadding: Object.freeze([10, 10, 10, 0] as const),\n});\n\n/** The index actually shown: the selection clamped into the items (MAUI `EffectiveIndex`); -1 with no items. */\nexport function effectiveTabIndex(selectedIndex: number, count: number): number {\n if (count <= 0) return -1;\n if (!Number.isFinite(selectedIndex)) return 0;\n return Math.min(Math.max(Math.trunc(selectedIndex), 0), count - 1);\n}\n\n/** The identity of a tab: its `key`, else its index. */\nexport function tabKey(item: G9TabItem, index: number): string {\n return item.key ?? `#${index}`;\n}\n\n/** The inline badge of a tab: `null`, `'dot'`, or the pill text (custom text wins; counts are `N0`-grouped). */\nexport function tabBadge(item: G9TabItem, numberingSystem: G9NumberingSystem): 'dot' | string | null {\n if (item.badgeText != null && item.badgeText.trim() !== '') return item.badgeText;\n const count = item.badgeCount ?? 0;\n if (Number.isFinite(count) && count > 0) return formatNumber(Math.trunc(count), { numberingSystem });\n return item.badgeDot ? 'dot' : null;\n}\n\n/** Visual (left-to-right) order of the cells: reversed in RTL so the first tab sits at the reading start. */\nexport function tabVisualOrder(count: number, rtl: boolean): number[] {\n const order: number[] = [];\n for (let i = 0; i < count; i++) order.push(rtl ? count - 1 - i : i);\n return order;\n}\n\n/** Content slide start offset: forward arrives from the trailing edge, back from the leading edge; RTL mirrors. */\nexport function tabContentSlideFrom(previous: number, next: number, rtl: boolean): number {\n const forward = previous < 0 || next > previous;\n const sign = (forward ? 1 : -1) * (rtl ? -1 : 1);\n return g9TabViewMetrics.contentSlidePx * sign;\n}\n\n/** CSS padding from a MAUI-style thickness: one value, `[h, v]` or `[l, t, r, b]`. */\nexport function thicknessCss(value: number | readonly number[]): string {\n const parts = typeof value === 'number' ? [value] : value;\n const [l = 0, t = l, r = l, b = t] = parts.length === 2 ? [parts[0], parts[1], parts[0], parts[1]] : parts;\n return `${t}px ${r}px ${b}px ${l}px`;\n}\n\n/**\n * Lazy-once realization (MAUI \"Lazy tab-body rendering\"): a body is built on its first activation, after\n * `lazyRealizeDelayMs` (a spinner meanwhile), and then stays built, hidden while inactive, so its state survives. A\n * pending token makes rapid switches realize only the latest selection. `eager` builds everything up front.\n */\nexport class G9TabRealizer {\n private readonly realized = new Set<string>();\n private pending: string | null = null;\n private generation = 0;\n\n constructor(private readonly eager: () => boolean) {}\n\n isRealized(key: string): boolean {\n return this.eager() || this.realized.has(key);\n }\n\n /** Marks `key` active: returns `'shown'` when it is already built, else a token to pass to {@link realize}. */\n activate(key: string): 'shown' | number {\n if (this.isRealized(key)) {\n this.pending = null;\n return 'shown';\n }\n this.pending = key;\n this.generation += 1;\n return this.generation;\n }\n\n /** Realizes the pending key when `token` is still the latest activation; returns whether it did. */\n realize(token: number): boolean {\n if (token !== this.generation || this.pending === null) return false;\n this.realized.add(this.pending);\n this.pending = null;\n return true;\n }\n\n /** Drops keys that are no longer in the items (a removed tab's body is released). */\n retain(keys: readonly string[]): void {\n const live = new Set(keys);\n for (const key of Array.from(this.realized)) if (!live.has(key)) this.realized.delete(key);\n if (this.pending !== null && !live.has(this.pending)) this.pending = null;\n }\n\n get pendingKey(): string | null {\n return this.pending;\n }\n}\n"],"names":["g9TabViewMetrics","Object","effectiveTabIndex","selectedIndex","count","Number","Math","tabKey","item","index","_item_key","tabBadge","numberingSystem","_item_badgeCount","formatNumber","tabVisualOrder","rtl","order","i","tabContentSlideFrom","previous","next","forward","sign","thicknessCss","value","parts","l","t","r","b","G9TabRealizer","key","token","keys","live","Set","Array","eager"],"mappings":"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"}
@@ -192,7 +192,7 @@ function TabBarStrip(props) {
192
192
  {'dot' === badge ? <view className="g9-tabs__badge-dot" style={{
193
193
  backgroundColor: null != (_item_badgeColor = item.badgeColor) ? _item_badgeColor : 'var(--g9-error)'
194
194
  }}/> : null !== badge ? <view className="g9-tabs__badge" style={{
195
- backgroundColor: null != (_item_badgeColor1 = item.badgeColor) ? _item_badgeColor1 : 'var(--g9-error)'
195
+ backgroundColor: null != (_item_badgeColor1 = item.badgeColor) ? _item_badgeColor1 : 'var(--g9-primary)'
196
196
  }}>
197
197
  <text className="g9-tabs__badge-text">{badge}</text>
198
198
  </view> : null}
@@ -1 +1 @@
1
- {"version":3,"file":"tabs/tab-view.jsx","sources":["../../src/tabs/tab-view.tsx"],"sourcesContent":["import './tabs.css';\nimport { ViewPager, type ViewPagerRef } from '@lynx-js/lynx-ui-view-pager';\nimport { type ReactNode, useEffect, useMemo, useRef, useState } from '@lynx-js/react';\nimport { g9RowStyle } from '../base/control';\nimport { startTimer, stopTimer } from '../base/timers';\nimport { useControllableState } from '../base/use-controllable-state';\nimport { G9ActivityIndicator } from '../display/activity-indicator';\nimport { type G9NodeRef, invokeNodeWhenReady } from '../fields/use-text-input';\nimport { G9IconSlot } from '../icon/slot';\nimport { useG9Culture, useG9Direction } from '../provider/hooks';\nimport {\n effectiveTabIndex,\n type G9TabBarPosition,\n type G9TabItem,\n type G9TabMode,\n G9TabRealizer,\n type G9TabStyle,\n g9TabViewMetrics,\n tabBadge,\n tabContentSlideFrom,\n tabKey,\n tabVisualOrder,\n thicknessCss,\n} from './model';\n\n/** Props of {@link G9TabView}. */\nexport interface G9TabViewProps {\n /** The tabs (MAUI `Items`). Add, remove or reorder freely; give tabs a `key` so their bodies follow them. */\n readonly items: readonly G9TabItem[];\n /** Controlled selection (MAUI `SelectedIndex`, two-way). Out-of-range values show the nearest tab. */\n readonly selectedIndex?: number;\n /** Initial selection while uncontrolled. Default 0. */\n readonly defaultSelectedIndex?: number;\n /** A tab was chosen by a tap or a swipe (MAUI `SelectionChanged`). */\n readonly onSelectionChange?: (index: number) => void;\n /** Bar height (MAUI `TabHeight`). Default 52. */\n readonly tabHeight?: number;\n /** Bar background (MAUI `TabBarBackground`). Default transparent (underlined) / surface-container-low (pill). */\n readonly tabBarBackground?: string | null;\n /** Underline colour (underlined) or active text colour (pill) (MAUI `IndicatorColor`). Default primary. */\n readonly indicatorColor?: string | null;\n /** `'fixed'` (default): equal full-width cells. `'scrollable'`: auto-width cells that scroll. */\n readonly mode?: G9TabMode;\n /** `'top'` (default) or `'bottom'`. */\n readonly barPosition?: G9TabBarPosition;\n /** `'underlined'` (default) or `'pill'` (MAUI `Style`). */\n readonly tabStyle?: G9TabStyle;\n /** Only the bar: no content panel; drive your own host from `onSelectionChange` (MAUI `HeaderOnly`). */\n readonly headerOnly?: boolean;\n /** Border and rounded corners around the content (MAUI `ShowFrame`). Default false. */\n readonly showFrame?: boolean;\n /** Content border radius when `showFrame` (MAUI `FrameCornerRadius`). Default 0. */\n readonly frameCornerRadius?: number;\n /** Inset around the active body (MAUI `ContentPadding`): one value, `[h, v]` or `[l, t, r, b]`. Default 10,10,10,0. */\n readonly contentPadding?: number | readonly number[];\n /** Build every body up front instead of lazily on first activation (MAUI `EagerContent`). Default false. */\n readonly eagerContent?: boolean;\n /** Swipe between bodies (lynx-ui ViewPager). A Lynx addition; default false, as in MAUI. */\n readonly swipeEnabled?: boolean;\n readonly className?: string;\n}\n\n/**\n * Tabs with a bar and lazily realized bodies (MAUI `G9TabView`). Underlined (default) or pill style, fixed or\n * scrollable cells, bar on top or bottom, header-only; the indicator slides between cells; bodies are built on first\n * activation (spinner, then {@link g9TabViewMetrics} `lazyRealizeDelayMs`) and kept, hidden, so scroll and form state\n * survive; RTL reverses the cell order. With `swipeEnabled` the bodies sit in a lynx-ui ViewPager.\n */\nexport function G9TabView(props: G9TabViewProps): ReactNode {\n const items = props.items;\n const tabStyle = props.tabStyle ?? 'underlined';\n const mode = props.mode ?? 'fixed';\n const barPosition = props.barPosition ?? 'top';\n const tabHeight = props.tabHeight ?? g9TabViewMetrics.tabHeight;\n const rtl = useG9Direction() === 'rtl';\n const culture = useG9Culture();\n const [selected, setSelected] = useControllableState<number>({\n value: props.selectedIndex,\n defaultValue: props.defaultSelectedIndex ?? 0,\n onChange: props.onSelectionChange,\n name: 'G9TabView.selectedIndex',\n });\n const count = items.length;\n const effective = effectiveTabIndex(selected, count);\n const keys = items.map((item, i) => tabKey(item, i));\n const activeKey = effective >= 0 ? (keys[effective] as string) : null;\n\n const eager = props.eagerContent === true;\n const eagerRef = useRef(eager);\n eagerRef.current = eager;\n const realizer = useMemo(() => new G9TabRealizer(() => eagerRef.current), []);\n realizer.retain(keys);\n const [, setVersion] = useState(0);\n const [pending, setPending] = useState<string | null>(null);\n\n // Lazy-once realization of the active body; rapid switches realize only the latest.\n useEffect(() => {\n if (activeKey === null || props.headerOnly) {\n setPending(null);\n return;\n }\n const token = realizer.activate(activeKey);\n if (token === 'shown') {\n setPending(null);\n return;\n }\n setPending(activeKey);\n const t = startTimer(() => {\n if (realizer.realize(token)) {\n setPending(null);\n setVersion((v) => v + 1);\n }\n }, g9TabViewMetrics.lazyRealizeDelayMs);\n return () => stopTimer(t);\n }, [activeKey, props.headerOnly]);\n\n // Directional reveal of the newly active body (6 px slide + fade, 160 ms CubicOut).\n const previous = useRef(-1);\n const [reveal, setReveal] = useState<{ key: string | null; from: number; settled: boolean }>({\n key: activeKey,\n from: 0,\n settled: true,\n });\n useEffect(() => {\n const prev = previous.current;\n previous.current = effective;\n if (prev === effective || prev < 0) return;\n setReveal({ key: activeKey, from: tabContentSlideFrom(prev, effective, rtl), settled: false });\n const t = startTimer(() => setReveal((r) => ({ ...r, settled: true })), 16);\n return () => stopTimer(t);\n }, [effective]);\n\n const select = (index: number) => {\n if (index !== effective) setSelected(index);\n };\n\n const pager = useRef<ViewPagerRef | null>(null);\n const swipe = props.swipeEnabled === true && !props.headerOnly;\n const order = tabVisualOrder(count, rtl);\n const visualIndex = order.indexOf(effective);\n useEffect(() => {\n if (!swipe || visualIndex < 0) return;\n try {\n pager.current?.scrollToPage(visualIndex, true);\n } catch {\n // The pager is gone (unmounted mid-switch).\n }\n }, [visualIndex, swipe]);\n\n const bar = (\n <TabBarStrip\n items={items}\n order={order}\n effective={effective}\n tabStyle={tabStyle}\n mode={mode}\n tabHeight={tabHeight}\n background={props.tabBarBackground ?? null}\n indicatorColor={props.indicatorColor ?? null}\n numbering={culture.numberingSystem}\n onSelect={select}\n />\n );\n\n const bodyOf = (index: number): ReactNode => {\n const item = items[index] as G9TabItem;\n const key = keys[index] as string;\n const active = index === effective;\n if (!realizer.isRealized(key)) {\n return active && pending === key ? <Spinner /> : null;\n }\n const revealing = active && reveal.key === key && !reveal.settled;\n const style = {\n display: (active || swipe ? 'flex' : 'none') as 'flex' | 'none',\n transform: `translateX(${revealing ? reveal.from : 0}px)`,\n opacity: revealing ? 0 : 1,\n transition:\n revealing || swipe\n ? 'none'\n : `transform ${g9TabViewMetrics.contentTransitionMs}ms ease-out, opacity ${g9TabViewMetrics.contentTransitionMs}ms ease-out`,\n };\n const content = typeof item.content === 'function' ? (item.content as () => ReactNode)() : item.content;\n return (\n <view key={key} className={`g9-tabs__panel${active ? ' g9-tabs__panel--active' : ''}`} style={style}>\n {content}\n </view>\n );\n };\n\n const frame = props.showFrame\n ? {\n borderWidth: '1px',\n borderStyle: 'solid',\n borderColor: 'var(--g9-outline-variant)',\n borderRadius: `${props.frameCornerRadius ?? 0}px`,\n }\n : {};\n const content = props.headerOnly ? null : (\n <view\n className=\"g9-tabs__content\"\n style={{ padding: thicknessCss(props.contentPadding ?? g9TabViewMetrics.contentPadding), ...frame }}\n >\n {swipe ? (\n <ViewPager\n ref={pager}\n className=\"g9-tabs__pager\"\n data={order}\n getItemKey={(index: number) => keys[index] as string}\n initialSelectIndex={Math.max(visualIndex, 0)}\n onPageChange={(event) => {\n const next = order[Number(event.detail.index)];\n if (next !== undefined) select(next);\n }}\n >\n {(index: number) => <view className=\"g9-tabs__page\">{bodyOf(index)}</view>}\n </ViewPager>\n ) : (\n <>\n {items.map((_, i) => bodyOf(i))}\n {pending !== null && pending === activeKey ? <Spinner /> : null}\n </>\n )}\n </view>\n );\n\n return (\n <view\n className={`g9-tabs g9-tabs--${tabStyle} g9-tabs--${barPosition}${props.headerOnly ? ' g9-tabs--header-only' : ''} ${props.className ?? ''}`}\n >\n {barPosition === 'bottom' ? content : null}\n {bar}\n {barPosition === 'top' ? content : null}\n </view>\n );\n}\n\nfunction Spinner(): ReactNode {\n return (\n <view className=\"g9-tabs__spinner\">\n <G9ActivityIndicator running size={24} />\n </view>\n );\n}\n\ninterface StripProps {\n readonly items: readonly G9TabItem[];\n readonly order: readonly number[];\n readonly effective: number;\n readonly tabStyle: G9TabStyle;\n readonly mode: G9TabMode;\n readonly tabHeight: number;\n readonly background: string | null;\n readonly indicatorColor: string | null;\n readonly numbering: Parameters<typeof tabBadge>[1];\n readonly onSelect: (index: number) => void;\n}\n\nfunction TabBarStrip(props: StripProps): ReactNode {\n const { items, order, effective, tabStyle, mode } = props;\n const count = order.length;\n const pill = tabStyle === 'pill';\n const direction = useG9Direction();\n const indicator = props.indicatorColor ?? 'var(--g9-primary)';\n const fixed = mode === 'fixed';\n const cellRefs = useRef<Array<G9NodeRef | null>>([]);\n useEffect(() => {\n if (fixed || effective < 0) return;\n invokeNodeWhenReady({ current: cellRefs.current[effective] ?? null }, 'scrollIntoView', {\n scrollIntoViewOptions: { behavior: 'smooth', block: 'center', inline: 'center' },\n });\n }, [effective, fixed]);\n\n const cells = order.map((index, position) => {\n const item = items[index] as G9TabItem;\n const active = index === effective;\n const badge = tabBadge(item, props.numbering);\n const textColor = active\n ? pill\n ? (props.indicatorColor ?? 'var(--g9-on-primary-container)')\n : 'var(--g9-on-surface)'\n : 'var(--g9-on-surface-variant)';\n return (\n <view\n key={tabKey(item, index)}\n ref={(node: G9NodeRef | null) => {\n cellRefs.current[index] = node;\n }}\n className={`g9-tabs__cell${active ? ' g9-tabs__cell--active' : ''}`}\n style={fixed ? { flex: '1' } : {}}\n accessibility-element\n accessibility-traits=\"button\"\n accessibility-label={item.text ?? ''}\n bindtap={() => props.onSelect(index)}\n >\n {/* Sibling order is paint order in Lynx: the active pill goes first, behind the label. */}\n {!fixed && active ? (\n <view\n className={pill ? 'g9-tabs__cell-pill' : 'g9-tabs__cell-underline'}\n style={pill ? {} : { backgroundColor: indicator }}\n />\n ) : null}\n {position > 0 && !pill ? <view className=\"g9-tabs__separator\" /> : null}\n <view className=\"g9-tabs__cell-row\" style={g9RowStyle(direction)}>\n <G9IconSlot\n emoji={item.emoji}\n icon={item.icon}\n imagePath={item.imagePath}\n image={item.image}\n size={18}\n color={textColor}\n />\n {item.text ? (\n <text className=\"g9-tabs__label\" style={{ color: textColor, fontWeight: active ? '600' : '400' }}>\n {item.text}\n </text>\n ) : null}\n {badge === 'dot' ? (\n <view className=\"g9-tabs__badge-dot\" style={{ backgroundColor: item.badgeColor ?? 'var(--g9-error)' }} />\n ) : badge !== null ? (\n <view className=\"g9-tabs__badge\" style={{ backgroundColor: item.badgeColor ?? 'var(--g9-error)' }}>\n <text className=\"g9-tabs__badge-text\">{badge}</text>\n </view>\n ) : null}\n </view>\n </view>\n );\n });\n\n const position = order.indexOf(effective);\n const slider =\n fixed && position >= 0 ? (\n <view\n className={pill ? 'g9-tabs__pill' : 'g9-tabs__underline'}\n style={{\n left: `${(position * 100) / count}%`,\n width: `${100 / count}%`,\n transition: `left ${g9TabViewMetrics.indicatorMoveMs}ms cubic-bezier(0.215, 0.61, 0.355, 1)`,\n }}\n >\n <view\n className={pill ? 'g9-tabs__pill-fill' : 'g9-tabs__underline-fill'}\n style={pill ? {} : { backgroundColor: indicator }}\n />\n </view>\n ) : null;\n\n const barStyle = {\n height: `${props.tabHeight}px`,\n backgroundColor: props.background ?? (pill ? 'var(--g9-surface-container-low)' : 'transparent'),\n };\n return (\n <view className=\"g9-tabs__bar\" style={barStyle}>\n {fixed ? (\n <view className=\"g9-tabs__cells\">\n {slider}\n {cells}\n </view>\n ) : (\n <scroll-view className=\"g9-tabs__scroller\" scroll-orientation=\"horizontal\">\n <view className=\"g9-tabs__cells g9-tabs__cells--scrollable\">{cells}</view>\n </scroll-view>\n )}\n </view>\n );\n}\n"],"names":["G9TabView","props","_props_tabStyle","_props_mode","_props_barPosition","_props_tabHeight","_props_defaultSelectedIndex","_props_tabBarBackground","_props_indicatorColor","_props_frameCornerRadius","_props_contentPadding","_props_className","items","tabStyle","mode","barPosition","tabHeight","g9TabViewMetrics","rtl","useG9Direction","culture","useG9Culture","selected","setSelected","useControllableState","count","effective","effectiveTabIndex","keys","item","i","tabKey","activeKey","eager","eagerRef","useRef","realizer","useMemo","G9TabRealizer","setVersion","useState","pending","setPending","useEffect","token","t","startTimer","v","stopTimer","previous","reveal","setReveal","prev","tabContentSlideFrom","r","select","index","pager","swipe","order","tabVisualOrder","visualIndex","_pager_current","bar","TabBarStrip","bodyOf","key","active","Spinner","revealing","style","content","view","frame","thicknessCss","ViewPager","Math","event","next","Number","undefined","_","G9ActivityIndicator","_props_background","pill","direction","indicator","fixed","cellRefs","_cellRefs_current_effective","invokeNodeWhenReady","cells","position","_item_text","_item_badgeColor","_item_badgeColor1","badge","tabBadge","textColor","node","g9RowStyle","G9IconSlot","text","slider","barStyle","scroll-view"],"mappings":"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"}
1
+ {"version":3,"file":"tabs/tab-view.jsx","sources":["../../src/tabs/tab-view.tsx"],"sourcesContent":["import './tabs.css';\nimport { ViewPager, type ViewPagerRef } from '@lynx-js/lynx-ui-view-pager';\nimport { type ReactNode, useEffect, useMemo, useRef, useState } from '@lynx-js/react';\nimport { g9RowStyle } from '../base/control';\nimport { startTimer, stopTimer } from '../base/timers';\nimport { useControllableState } from '../base/use-controllable-state';\nimport { G9ActivityIndicator } from '../display/activity-indicator';\nimport { type G9NodeRef, invokeNodeWhenReady } from '../fields/use-text-input';\nimport { G9IconSlot } from '../icon/slot';\nimport { useG9Culture, useG9Direction } from '../provider/hooks';\nimport {\n effectiveTabIndex,\n type G9TabBarPosition,\n type G9TabItem,\n type G9TabMode,\n G9TabRealizer,\n type G9TabStyle,\n g9TabViewMetrics,\n tabBadge,\n tabContentSlideFrom,\n tabKey,\n tabVisualOrder,\n thicknessCss,\n} from './model';\n\n/** Props of {@link G9TabView}. */\nexport interface G9TabViewProps {\n /** The tabs (MAUI `Items`). Add, remove or reorder freely; give tabs a `key` so their bodies follow them. */\n readonly items: readonly G9TabItem[];\n /** Controlled selection (MAUI `SelectedIndex`, two-way). Out-of-range values show the nearest tab. */\n readonly selectedIndex?: number;\n /** Initial selection while uncontrolled. Default 0. */\n readonly defaultSelectedIndex?: number;\n /** A tab was chosen by a tap or a swipe (MAUI `SelectionChanged`). */\n readonly onSelectionChange?: (index: number) => void;\n /** Bar height (MAUI `TabHeight`). Default 52. */\n readonly tabHeight?: number;\n /** Bar background (MAUI `TabBarBackground`). Default transparent (underlined) / surface-container-low (pill). */\n readonly tabBarBackground?: string | null;\n /** Underline colour (underlined) or active text colour (pill) (MAUI `IndicatorColor`). Default primary. */\n readonly indicatorColor?: string | null;\n /** `'fixed'` (default): equal full-width cells. `'scrollable'`: auto-width cells that scroll. */\n readonly mode?: G9TabMode;\n /** `'top'` (default) or `'bottom'`. */\n readonly barPosition?: G9TabBarPosition;\n /** `'underlined'` (default) or `'pill'` (MAUI `Style`). */\n readonly tabStyle?: G9TabStyle;\n /** Only the bar: no content panel; drive your own host from `onSelectionChange` (MAUI `HeaderOnly`). */\n readonly headerOnly?: boolean;\n /** Border and rounded corners around the content (MAUI `ShowFrame`). Default false. */\n readonly showFrame?: boolean;\n /** Content border radius when `showFrame` (MAUI `FrameCornerRadius`). Default 0. */\n readonly frameCornerRadius?: number;\n /** Inset around the active body (MAUI `ContentPadding`): one value, `[h, v]` or `[l, t, r, b]`. Default 10,10,10,0. */\n readonly contentPadding?: number | readonly number[];\n /** Build every body up front instead of lazily on first activation (MAUI `EagerContent`). Default false. */\n readonly eagerContent?: boolean;\n /** Swipe between bodies (lynx-ui ViewPager). A Lynx addition; default false, as in MAUI. */\n readonly swipeEnabled?: boolean;\n readonly className?: string;\n}\n\n/**\n * Tabs with a bar and lazily realized bodies (MAUI `G9TabView`). Underlined (default) or pill style, fixed or\n * scrollable cells, bar on top or bottom, header-only; the indicator slides between cells; bodies are built on first\n * activation (spinner, then {@link g9TabViewMetrics} `lazyRealizeDelayMs`) and kept, hidden, so scroll and form state\n * survive; RTL reverses the cell order. With `swipeEnabled` the bodies sit in a lynx-ui ViewPager.\n */\nexport function G9TabView(props: G9TabViewProps): ReactNode {\n const items = props.items;\n const tabStyle = props.tabStyle ?? 'underlined';\n const mode = props.mode ?? 'fixed';\n const barPosition = props.barPosition ?? 'top';\n const tabHeight = props.tabHeight ?? g9TabViewMetrics.tabHeight;\n const rtl = useG9Direction() === 'rtl';\n const culture = useG9Culture();\n const [selected, setSelected] = useControllableState<number>({\n value: props.selectedIndex,\n defaultValue: props.defaultSelectedIndex ?? 0,\n onChange: props.onSelectionChange,\n name: 'G9TabView.selectedIndex',\n });\n const count = items.length;\n const effective = effectiveTabIndex(selected, count);\n const keys = items.map((item, i) => tabKey(item, i));\n const activeKey = effective >= 0 ? (keys[effective] as string) : null;\n\n const eager = props.eagerContent === true;\n const eagerRef = useRef(eager);\n eagerRef.current = eager;\n const realizer = useMemo(() => new G9TabRealizer(() => eagerRef.current), []);\n realizer.retain(keys);\n const [, setVersion] = useState(0);\n const [pending, setPending] = useState<string | null>(null);\n\n // Lazy-once realization of the active body; rapid switches realize only the latest.\n useEffect(() => {\n if (activeKey === null || props.headerOnly) {\n setPending(null);\n return;\n }\n const token = realizer.activate(activeKey);\n if (token === 'shown') {\n setPending(null);\n return;\n }\n setPending(activeKey);\n const t = startTimer(() => {\n if (realizer.realize(token)) {\n setPending(null);\n setVersion((v) => v + 1);\n }\n }, g9TabViewMetrics.lazyRealizeDelayMs);\n return () => stopTimer(t);\n }, [activeKey, props.headerOnly]);\n\n // Directional reveal of the newly active body (6 px slide + fade, 160 ms CubicOut).\n const previous = useRef(-1);\n const [reveal, setReveal] = useState<{ key: string | null; from: number; settled: boolean }>({\n key: activeKey,\n from: 0,\n settled: true,\n });\n useEffect(() => {\n const prev = previous.current;\n previous.current = effective;\n if (prev === effective || prev < 0) return;\n setReveal({ key: activeKey, from: tabContentSlideFrom(prev, effective, rtl), settled: false });\n const t = startTimer(() => setReveal((r) => ({ ...r, settled: true })), 16);\n return () => stopTimer(t);\n }, [effective]);\n\n const select = (index: number) => {\n if (index !== effective) setSelected(index);\n };\n\n const pager = useRef<ViewPagerRef | null>(null);\n const swipe = props.swipeEnabled === true && !props.headerOnly;\n const order = tabVisualOrder(count, rtl);\n const visualIndex = order.indexOf(effective);\n useEffect(() => {\n if (!swipe || visualIndex < 0) return;\n try {\n pager.current?.scrollToPage(visualIndex, true);\n } catch {\n // The pager is gone (unmounted mid-switch).\n }\n }, [visualIndex, swipe]);\n\n const bar = (\n <TabBarStrip\n items={items}\n order={order}\n effective={effective}\n tabStyle={tabStyle}\n mode={mode}\n tabHeight={tabHeight}\n background={props.tabBarBackground ?? null}\n indicatorColor={props.indicatorColor ?? null}\n numbering={culture.numberingSystem}\n onSelect={select}\n />\n );\n\n const bodyOf = (index: number): ReactNode => {\n const item = items[index] as G9TabItem;\n const key = keys[index] as string;\n const active = index === effective;\n if (!realizer.isRealized(key)) {\n return active && pending === key ? <Spinner /> : null;\n }\n const revealing = active && reveal.key === key && !reveal.settled;\n const style = {\n display: (active || swipe ? 'flex' : 'none') as 'flex' | 'none',\n transform: `translateX(${revealing ? reveal.from : 0}px)`,\n opacity: revealing ? 0 : 1,\n transition:\n revealing || swipe\n ? 'none'\n : `transform ${g9TabViewMetrics.contentTransitionMs}ms ease-out, opacity ${g9TabViewMetrics.contentTransitionMs}ms ease-out`,\n };\n const content = typeof item.content === 'function' ? (item.content as () => ReactNode)() : item.content;\n return (\n <view key={key} className={`g9-tabs__panel${active ? ' g9-tabs__panel--active' : ''}`} style={style}>\n {content}\n </view>\n );\n };\n\n const frame = props.showFrame\n ? {\n borderWidth: '1px',\n borderStyle: 'solid',\n borderColor: 'var(--g9-outline-variant)',\n borderRadius: `${props.frameCornerRadius ?? 0}px`,\n }\n : {};\n const content = props.headerOnly ? null : (\n <view\n className=\"g9-tabs__content\"\n style={{ padding: thicknessCss(props.contentPadding ?? g9TabViewMetrics.contentPadding), ...frame }}\n >\n {swipe ? (\n <ViewPager\n ref={pager}\n className=\"g9-tabs__pager\"\n data={order}\n getItemKey={(index: number) => keys[index] as string}\n initialSelectIndex={Math.max(visualIndex, 0)}\n onPageChange={(event) => {\n const next = order[Number(event.detail.index)];\n if (next !== undefined) select(next);\n }}\n >\n {(index: number) => <view className=\"g9-tabs__page\">{bodyOf(index)}</view>}\n </ViewPager>\n ) : (\n <>\n {items.map((_, i) => bodyOf(i))}\n {pending !== null && pending === activeKey ? <Spinner /> : null}\n </>\n )}\n </view>\n );\n\n return (\n <view\n className={`g9-tabs g9-tabs--${tabStyle} g9-tabs--${barPosition}${props.headerOnly ? ' g9-tabs--header-only' : ''} ${props.className ?? ''}`}\n >\n {barPosition === 'bottom' ? content : null}\n {bar}\n {barPosition === 'top' ? content : null}\n </view>\n );\n}\n\nfunction Spinner(): ReactNode {\n return (\n <view className=\"g9-tabs__spinner\">\n <G9ActivityIndicator running size={24} />\n </view>\n );\n}\n\ninterface StripProps {\n readonly items: readonly G9TabItem[];\n readonly order: readonly number[];\n readonly effective: number;\n readonly tabStyle: G9TabStyle;\n readonly mode: G9TabMode;\n readonly tabHeight: number;\n readonly background: string | null;\n readonly indicatorColor: string | null;\n readonly numbering: Parameters<typeof tabBadge>[1];\n readonly onSelect: (index: number) => void;\n}\n\nfunction TabBarStrip(props: StripProps): ReactNode {\n const { items, order, effective, tabStyle, mode } = props;\n const count = order.length;\n const pill = tabStyle === 'pill';\n const direction = useG9Direction();\n const indicator = props.indicatorColor ?? 'var(--g9-primary)';\n const fixed = mode === 'fixed';\n const cellRefs = useRef<Array<G9NodeRef | null>>([]);\n useEffect(() => {\n if (fixed || effective < 0) return;\n invokeNodeWhenReady({ current: cellRefs.current[effective] ?? null }, 'scrollIntoView', {\n scrollIntoViewOptions: { behavior: 'smooth', block: 'center', inline: 'center' },\n });\n }, [effective, fixed]);\n\n const cells = order.map((index, position) => {\n const item = items[index] as G9TabItem;\n const active = index === effective;\n const badge = tabBadge(item, props.numbering);\n const textColor = active\n ? pill\n ? (props.indicatorColor ?? 'var(--g9-on-primary-container)')\n : 'var(--g9-on-surface)'\n : 'var(--g9-on-surface-variant)';\n return (\n <view\n key={tabKey(item, index)}\n ref={(node: G9NodeRef | null) => {\n cellRefs.current[index] = node;\n }}\n className={`g9-tabs__cell${active ? ' g9-tabs__cell--active' : ''}`}\n style={fixed ? { flex: '1' } : {}}\n accessibility-element\n accessibility-traits=\"button\"\n accessibility-label={item.text ?? ''}\n bindtap={() => props.onSelect(index)}\n >\n {/* Sibling order is paint order in Lynx: the active pill goes first, behind the label. */}\n {!fixed && active ? (\n <view\n className={pill ? 'g9-tabs__cell-pill' : 'g9-tabs__cell-underline'}\n style={pill ? {} : { backgroundColor: indicator }}\n />\n ) : null}\n {position > 0 && !pill ? <view className=\"g9-tabs__separator\" /> : null}\n <view className=\"g9-tabs__cell-row\" style={g9RowStyle(direction)}>\n <G9IconSlot\n emoji={item.emoji}\n icon={item.icon}\n imagePath={item.imagePath}\n image={item.image}\n size={18}\n color={textColor}\n />\n {item.text ? (\n <text className=\"g9-tabs__label\" style={{ color: textColor, fontWeight: active ? '600' : '400' }}>\n {item.text}\n </text>\n ) : null}\n {badge === 'dot' ? (\n <view className=\"g9-tabs__badge-dot\" style={{ backgroundColor: item.badgeColor ?? 'var(--g9-error)' }} />\n ) : badge !== null ? (\n // MAUI CreateBadge: `badgeColor ?? palette.Primary` (only the dot defaults to Error).\n <view className=\"g9-tabs__badge\" style={{ backgroundColor: item.badgeColor ?? 'var(--g9-primary)' }}>\n <text className=\"g9-tabs__badge-text\">{badge}</text>\n </view>\n ) : null}\n </view>\n </view>\n );\n });\n\n const position = order.indexOf(effective);\n const slider =\n fixed && position >= 0 ? (\n <view\n className={pill ? 'g9-tabs__pill' : 'g9-tabs__underline'}\n style={{\n left: `${(position * 100) / count}%`,\n width: `${100 / count}%`,\n transition: `left ${g9TabViewMetrics.indicatorMoveMs}ms cubic-bezier(0.215, 0.61, 0.355, 1)`,\n }}\n >\n <view\n className={pill ? 'g9-tabs__pill-fill' : 'g9-tabs__underline-fill'}\n style={pill ? {} : { backgroundColor: indicator }}\n />\n </view>\n ) : null;\n\n const barStyle = {\n height: `${props.tabHeight}px`,\n backgroundColor: props.background ?? (pill ? 'var(--g9-surface-container-low)' : 'transparent'),\n };\n return (\n <view className=\"g9-tabs__bar\" style={barStyle}>\n {fixed ? (\n <view className=\"g9-tabs__cells\">\n {slider}\n {cells}\n </view>\n ) : (\n <scroll-view className=\"g9-tabs__scroller\" scroll-orientation=\"horizontal\">\n <view className=\"g9-tabs__cells g9-tabs__cells--scrollable\">{cells}</view>\n </scroll-view>\n )}\n </view>\n );\n}\n"],"names":["G9TabView","props","_props_tabStyle","_props_mode","_props_barPosition","_props_tabHeight","_props_defaultSelectedIndex","_props_tabBarBackground","_props_indicatorColor","_props_frameCornerRadius","_props_contentPadding","_props_className","items","tabStyle","mode","barPosition","tabHeight","g9TabViewMetrics","rtl","useG9Direction","culture","useG9Culture","selected","setSelected","useControllableState","count","effective","effectiveTabIndex","keys","item","i","tabKey","activeKey","eager","eagerRef","useRef","realizer","useMemo","G9TabRealizer","setVersion","useState","pending","setPending","useEffect","token","t","startTimer","v","stopTimer","previous","reveal","setReveal","prev","tabContentSlideFrom","r","select","index","pager","swipe","order","tabVisualOrder","visualIndex","_pager_current","bar","TabBarStrip","bodyOf","key","active","Spinner","revealing","style","content","view","frame","thicknessCss","ViewPager","Math","event","next","Number","undefined","_","G9ActivityIndicator","_props_background","pill","direction","indicator","fixed","cellRefs","_cellRefs_current_effective","invokeNodeWhenReady","cells","position","_item_text","_item_badgeColor","_item_badgeColor1","badge","tabBadge","textColor","node","g9RowStyle","G9IconSlot","text","slider","barStyle","scroll-view"],"mappings":"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"}
@@ -4,7 +4,7 @@ import { g9ChildId, g9IdProps, g9RowStyle, useG9Control } from "../base/control.
4
4
  import { useG9Press } from "../base/press.jsx";
5
5
  import { selectionKey } from "../base/selection-item.jsx";
6
6
  import { useControllableState } from "../base/use-controllable-state.jsx";
7
- import { G9IconSlot } from "../icon/slot.jsx";
7
+ import { G9IconSlot, hasIconSlot } from "../icon/slot.jsx";
8
8
  import { useG9Settings, useG9Strings, useG9Theme } from "../provider/hooks.jsx";
9
9
  const EMPTY = [];
10
10
  function G9ChipGroup(props) {
@@ -91,7 +91,13 @@ function Chip(props) {
91
91
  }} accessibility-element={true} accessibility-traits="button" accessibility-label={item.text} {...props.selected ? {
92
92
  'accessibility-value': props.selectedLabel
93
93
  } : {}} bindtap={press}>
94
- <G9IconSlot emoji={item.emoji} icon={item.icon} imagePath={item.iconPath} image={item.iconImage} size={props.iconSize} color={props.color}/>
94
+ {}
95
+ {hasIconSlot({
96
+ emoji: item.emoji,
97
+ icon: item.icon,
98
+ imagePath: item.iconPath,
99
+ image: item.iconImage
100
+ }) ? <G9IconSlot emoji={item.emoji} icon={item.icon} imagePath={item.iconPath} image={item.iconImage} size={props.iconSize} color={props.color}/> : null}
95
101
  <text className="g9-chip__text" style={{
96
102
  color: props.color
97
103
  }}>
@@ -1 +1 @@
1
- {"version":3,"file":"toggle/chip-group.jsx","sources":["../../src/toggle/chip-group.tsx"],"sourcesContent":["import './toggle.css';\nimport {\n buildSolidOrGradient,\n fillToCss,\n formatColor,\n type G9ChipGroupLayoutMode,\n type G9ChipGroupSelectionMode,\n G9Metrics,\n G9StringKey,\n tryParseColor,\n} from '@g9tm/lynxcontrols-foundation';\nimport type { ReactNode } from '@lynx-js/react';\nimport { type G9ControlBaseProps, g9ChildId, g9IdProps, g9RowStyle, useG9Control } from '../base/control';\nimport { useG9Press } from '../base/press';\nimport { type G9SelectionItem, selectionKey } from '../base/selection-item';\nimport { useControllableState } from '../base/use-controllable-state';\nimport { G9IconSlot } from '../icon/slot';\nimport { useG9Settings, useG9Strings, useG9Theme } from '../provider/hooks';\n\n/** Props of {@link G9ChipGroup} (MAUI `G9ChipGroup`). */\nexport interface G9ChipGroupProps extends G9ControlBaseProps {\n /** One chip per item (MAUI `ItemsSource`). Selection is by `selectionKey(item)`, so replaced objects keep it. */\n readonly items: ReadonlyArray<G9SelectionItem>;\n /** Default `'multiSelection'`. */\n readonly selectionMode?: G9ChipGroupSelectionMode;\n /** Multi mode: controlled selected keys (MAUI `SelectedItems`). */\n readonly selectedKeys?: ReadonlyArray<string>;\n readonly defaultSelectedKeys?: ReadonlyArray<string>;\n readonly onSelectedKeysChange?: (keys: ReadonlyArray<string>) => void;\n /** Single mode: controlled selected key (MAUI `SelectedItem`); `null` = none. */\n readonly selectedKey?: string | null;\n readonly defaultSelectedKey?: string | null;\n readonly onSelectedKeyChange?: (key: string | null, item: G9SelectionItem | null) => void;\n /** Single mode: tapping the selected chip clears the selection. Default false. */\n readonly allowNullSelection?: boolean;\n /** Gap between chips. Default 6. */\n readonly itemSpacing?: number;\n /** Default 36. */\n readonly chipHeight?: number;\n /** Leading icon size. Default 16. */\n readonly iconSize?: number;\n /** Group-wide selected fill (an item's `selectedColor` wins). Default primary. */\n readonly selectedBackground?: string | null;\n /** Group-wide selected text colour. Default on-primary. */\n readonly selectedTextColor?: string | null;\n /** `'wrap'` (default) or one horizontally scrolling line. */\n readonly layoutMode?: G9ChipGroupLayoutMode;\n /** Default 9 (`G9LayoutMetrics.ControlCornerRadius`). */\n readonly chipCornerRadius?: number;\n /** Check mark at the trailing edge of selected chips. Default true. */\n readonly showSelectionCheckmark?: boolean;\n /**\n * After every user tap (MAUI `SelectionChanged`): single mode `[item]` or `[]` (also when the tap changed nothing),\n * multi mode the selected items.\n */\n readonly onSelectionChange?: (items: ReadonlyArray<G9SelectionItem>) => void;\n}\n\nconst EMPTY: ReadonlyArray<string> = [];\n\n/**\n * Chip group (MAUI `G9ChipGroup`): single or multi selection by item identity, wrap or single-line scroll, colour\n * transition (140 ms) and check mark (120 ms) in CSS, press pulse 0.94 on the main thread. Disabled items are dimmed\n * and ignore taps.\n */\nexport function G9ChipGroup(props: G9ChipGroupProps): ReactNode {\n const control = useG9Control(props, 'g9-chip-group');\n const palette = useG9Theme().palette;\n const glyphs = useG9Settings().glyphs;\n const strings = useG9Strings();\n const single = props.selectionMode === 'singleSelection';\n const [keys, setKeys] = useControllableState<ReadonlyArray<string>>({\n value: props.selectedKeys,\n defaultValue: props.defaultSelectedKeys ?? EMPTY,\n onChange: props.onSelectedKeysChange,\n name: 'G9ChipGroup.selectedKeys',\n });\n const [key, setKey] = useControllableState<string | null, [G9SelectionItem | null]>({\n value: props.selectedKey,\n defaultValue: props.defaultSelectedKey ?? null,\n onChange: props.onSelectedKeyChange,\n name: 'G9ChipGroup.selectedKey',\n });\n const isSelected = (item: G9SelectionItem) =>\n single ? key === selectionKey(item) : keys.includes(selectionKey(item));\n\n const tap = (item: G9SelectionItem) => {\n if (control.disabled || item.isEnabled === false) return;\n const k = selectionKey(item);\n if (single) {\n const next = key === k ? (props.allowNullSelection ? null : k) : k;\n setKey(next, next === null ? null : item);\n props.onSelectionChange?.(next === null ? [] : [item]);\n return;\n }\n const next = keys.includes(k) ? keys.filter((x) => x !== k) : [...keys, k];\n setKeys(next);\n props.onSelectionChange?.(props.items.filter((i) => next.includes(selectionKey(i))));\n };\n\n const height = props.chipHeight ?? G9Metrics.ChipHeight;\n const spacing = props.itemSpacing ?? G9Metrics.ChipSpacing;\n const iconSize = props.iconSize ?? G9Metrics.ChipIconSize;\n const radius = props.chipCornerRadius ?? 9;\n const checkmark = props.showSelectionCheckmark !== false;\n const chips = props.items.map((item) => {\n const selected = isSelected(item);\n const bg = item.selectedColor ?? props.selectedBackground ?? palette.Primary;\n const fg = item.selectedTextColor ?? props.selectedTextColor ?? palette.OnPrimary;\n const parsed = tryParseColor(bg);\n const background = selected ? (parsed ? fillToCss(buildSolidOrGradient(parsed, true)) : bg) : palette.Surface;\n const color = selected ? fg : palette.TextSecondary;\n return (\n <Chip\n key={selectionKey(item)}\n direction={control.direction}\n item={item}\n selected={selected}\n background={background}\n stroke={selected ? (parsed ? formatColor(parsed) : bg) : palette.OutlineVariant}\n color={color}\n height={height}\n spacing={spacing}\n iconSize={iconSize}\n radius={radius}\n checkmark={checkmark}\n checkIcon={glyphs.check}\n groupDisabled={control.disabled}\n selectedLabel={strings.get(G9StringKey.Selected)}\n onTap={tap}\n id={g9ChildId(props.id, selectionKey(item))}\n />\n );\n });\n const scroll = props.layoutMode === 'singleLineScroll';\n return scroll ? (\n <scroll-view\n className={`${control.className} g9-chip-group--scroll`}\n {...control.idProps}\n scroll-orientation=\"horizontal\"\n >\n <view className=\"g9-chip-group__line\" style={g9RowStyle(control.direction)}>\n {chips}\n </view>\n </scroll-view>\n ) : (\n <view\n className={`${control.className} g9-chip-group--wrap`}\n {...control.idProps}\n style={g9RowStyle(control.direction)}\n >\n {chips}\n </view>\n );\n}\n\ninterface ChipProps {\n readonly item: G9SelectionItem;\n readonly selected: boolean;\n readonly background: string;\n readonly stroke: string;\n readonly color: string;\n readonly height: number;\n readonly spacing: number;\n readonly iconSize: number;\n readonly radius: number;\n readonly checkmark: boolean;\n readonly checkIcon: Parameters<typeof G9IconSlot>[0]['icon'];\n readonly groupDisabled: boolean;\n readonly selectedLabel: string;\n readonly onTap: (item: G9SelectionItem) => void;\n readonly direction: 'ltr' | 'rtl';\n /** The chip's automation id (`${groupId}_${key}`). */\n readonly id: string | undefined;\n}\n\nfunction Chip(props: ChipProps): ReactNode {\n const { item } = props;\n const disabled = props.groupDisabled || item.isEnabled === false;\n const press = useG9Press(() => props.onTap(item), { blocked: disabled, source: 'G9ChipGroup', guardMs: 0 });\n return (\n <view\n className={`g9-chip${props.selected ? ' g9-chip--selected' : ''}${item.isEnabled === false ? ' g9-chip--disabled' : ''}`}\n {...g9IdProps(props.id)}\n style={{\n ...g9RowStyle(props.direction),\n height: `${props.height}px`,\n minHeight: `${props.height}px`,\n [props.direction === 'rtl' ? 'marginLeft' : 'marginRight']: `${props.spacing}px`,\n marginBottom: `${props.spacing}px`,\n borderRadius: `${props.radius}px`,\n background: props.background,\n borderColor: props.stroke,\n }}\n accessibility-element={true}\n accessibility-traits=\"button\"\n accessibility-label={item.text}\n {...(props.selected ? { 'accessibility-value': props.selectedLabel } : {})}\n bindtap={press}\n >\n <G9IconSlot\n emoji={item.emoji}\n icon={item.icon}\n imagePath={item.iconPath}\n image={item.iconImage}\n size={props.iconSize}\n color={props.color}\n />\n <text className=\"g9-chip__text\" style={{ color: props.color }}>\n {item.text}\n </text>\n {props.checkmark ? (\n <view className={`g9-chip__check${props.selected ? ' g9-chip__check--on' : ''}`}>\n <G9IconSlot icon={props.checkIcon} size={G9Metrics.ChipIconSize} color={props.color} />\n </view>\n ) : null}\n </view>\n );\n}\n"],"names":["EMPTY","G9ChipGroup","props","_props_defaultSelectedKeys","_props_defaultSelectedKey","_props_chipHeight","_props_itemSpacing","_props_iconSize","_props_chipCornerRadius","control","useG9Control","palette","useG9Theme","glyphs","useG9Settings","strings","useG9Strings","single","keys","setKeys","useControllableState","key","setKey","isSelected","item","selectionKey","tap","k","next","x","i","height","G9Metrics","spacing","iconSize","radius","checkmark","chips","_ref","_ref1","selected","bg","fg","parsed","tryParseColor","background","fillToCss","buildSolidOrGradient","color","Chip","formatColor","G9StringKey","g9ChildId","scroll","scroll-view","view","g9RowStyle","disabled","press","useG9Press","g9IdProps","G9IconSlot","text"],"mappings":"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"}
1
+ {"version":3,"file":"toggle/chip-group.jsx","sources":["../../src/toggle/chip-group.tsx"],"sourcesContent":["import './toggle.css';\nimport {\n buildSolidOrGradient,\n fillToCss,\n formatColor,\n type G9ChipGroupLayoutMode,\n type G9ChipGroupSelectionMode,\n G9Metrics,\n G9StringKey,\n tryParseColor,\n} from '@g9tm/lynxcontrols-foundation';\nimport type { ReactNode } from '@lynx-js/react';\nimport { type G9ControlBaseProps, g9ChildId, g9IdProps, g9RowStyle, useG9Control } from '../base/control';\nimport { useG9Press } from '../base/press';\nimport { type G9SelectionItem, selectionKey } from '../base/selection-item';\nimport { useControllableState } from '../base/use-controllable-state';\nimport { G9IconSlot, hasIconSlot } from '../icon/slot';\nimport { useG9Settings, useG9Strings, useG9Theme } from '../provider/hooks';\n\n/** Props of {@link G9ChipGroup} (MAUI `G9ChipGroup`). */\nexport interface G9ChipGroupProps extends G9ControlBaseProps {\n /** One chip per item (MAUI `ItemsSource`). Selection is by `selectionKey(item)`, so replaced objects keep it. */\n readonly items: ReadonlyArray<G9SelectionItem>;\n /** Default `'multiSelection'`. */\n readonly selectionMode?: G9ChipGroupSelectionMode;\n /** Multi mode: controlled selected keys (MAUI `SelectedItems`). */\n readonly selectedKeys?: ReadonlyArray<string>;\n readonly defaultSelectedKeys?: ReadonlyArray<string>;\n readonly onSelectedKeysChange?: (keys: ReadonlyArray<string>) => void;\n /** Single mode: controlled selected key (MAUI `SelectedItem`); `null` = none. */\n readonly selectedKey?: string | null;\n readonly defaultSelectedKey?: string | null;\n readonly onSelectedKeyChange?: (key: string | null, item: G9SelectionItem | null) => void;\n /** Single mode: tapping the selected chip clears the selection. Default false. */\n readonly allowNullSelection?: boolean;\n /** Gap between chips. Default 6. */\n readonly itemSpacing?: number;\n /** Default 36. */\n readonly chipHeight?: number;\n /** Leading icon size. Default 16. */\n readonly iconSize?: number;\n /** Group-wide selected fill (an item's `selectedColor` wins). Default primary. */\n readonly selectedBackground?: string | null;\n /** Group-wide selected text colour. Default on-primary. */\n readonly selectedTextColor?: string | null;\n /** `'wrap'` (default) or one horizontally scrolling line. */\n readonly layoutMode?: G9ChipGroupLayoutMode;\n /** Default 9 (`G9LayoutMetrics.ControlCornerRadius`). */\n readonly chipCornerRadius?: number;\n /** Check mark at the trailing edge of selected chips. Default true. */\n readonly showSelectionCheckmark?: boolean;\n /**\n * After every user tap (MAUI `SelectionChanged`): single mode `[item]` or `[]` (also when the tap changed nothing),\n * multi mode the selected items.\n */\n readonly onSelectionChange?: (items: ReadonlyArray<G9SelectionItem>) => void;\n}\n\nconst EMPTY: ReadonlyArray<string> = [];\n\n/**\n * Chip group (MAUI `G9ChipGroup`): single or multi selection by item identity, wrap or single-line scroll, colour\n * transition (140 ms) and check mark (120 ms) in CSS, press pulse 0.94 on the main thread. Disabled items are dimmed\n * and ignore taps.\n */\nexport function G9ChipGroup(props: G9ChipGroupProps): ReactNode {\n const control = useG9Control(props, 'g9-chip-group');\n const palette = useG9Theme().palette;\n const glyphs = useG9Settings().glyphs;\n const strings = useG9Strings();\n const single = props.selectionMode === 'singleSelection';\n const [keys, setKeys] = useControllableState<ReadonlyArray<string>>({\n value: props.selectedKeys,\n defaultValue: props.defaultSelectedKeys ?? EMPTY,\n onChange: props.onSelectedKeysChange,\n name: 'G9ChipGroup.selectedKeys',\n });\n const [key, setKey] = useControllableState<string | null, [G9SelectionItem | null]>({\n value: props.selectedKey,\n defaultValue: props.defaultSelectedKey ?? null,\n onChange: props.onSelectedKeyChange,\n name: 'G9ChipGroup.selectedKey',\n });\n const isSelected = (item: G9SelectionItem) =>\n single ? key === selectionKey(item) : keys.includes(selectionKey(item));\n\n const tap = (item: G9SelectionItem) => {\n if (control.disabled || item.isEnabled === false) return;\n const k = selectionKey(item);\n if (single) {\n const next = key === k ? (props.allowNullSelection ? null : k) : k;\n setKey(next, next === null ? null : item);\n props.onSelectionChange?.(next === null ? [] : [item]);\n return;\n }\n const next = keys.includes(k) ? keys.filter((x) => x !== k) : [...keys, k];\n setKeys(next);\n props.onSelectionChange?.(props.items.filter((i) => next.includes(selectionKey(i))));\n };\n\n const height = props.chipHeight ?? G9Metrics.ChipHeight;\n const spacing = props.itemSpacing ?? G9Metrics.ChipSpacing;\n const iconSize = props.iconSize ?? G9Metrics.ChipIconSize;\n const radius = props.chipCornerRadius ?? 9;\n const checkmark = props.showSelectionCheckmark !== false;\n const chips = props.items.map((item) => {\n const selected = isSelected(item);\n const bg = item.selectedColor ?? props.selectedBackground ?? palette.Primary;\n const fg = item.selectedTextColor ?? props.selectedTextColor ?? palette.OnPrimary;\n const parsed = tryParseColor(bg);\n const background = selected ? (parsed ? fillToCss(buildSolidOrGradient(parsed, true)) : bg) : palette.Surface;\n const color = selected ? fg : palette.TextSecondary;\n return (\n <Chip\n key={selectionKey(item)}\n direction={control.direction}\n item={item}\n selected={selected}\n background={background}\n stroke={selected ? (parsed ? formatColor(parsed) : bg) : palette.OutlineVariant}\n color={color}\n height={height}\n spacing={spacing}\n iconSize={iconSize}\n radius={radius}\n checkmark={checkmark}\n checkIcon={glyphs.check}\n groupDisabled={control.disabled}\n selectedLabel={strings.get(G9StringKey.Selected)}\n onTap={tap}\n id={g9ChildId(props.id, selectionKey(item))}\n />\n );\n });\n const scroll = props.layoutMode === 'singleLineScroll';\n return scroll ? (\n <scroll-view\n className={`${control.className} g9-chip-group--scroll`}\n {...control.idProps}\n scroll-orientation=\"horizontal\"\n >\n <view className=\"g9-chip-group__line\" style={g9RowStyle(control.direction)}>\n {chips}\n </view>\n </scroll-view>\n ) : (\n <view\n className={`${control.className} g9-chip-group--wrap`}\n {...control.idProps}\n style={g9RowStyle(control.direction)}\n >\n {chips}\n </view>\n );\n}\n\ninterface ChipProps {\n readonly item: G9SelectionItem;\n readonly selected: boolean;\n readonly background: string;\n readonly stroke: string;\n readonly color: string;\n readonly height: number;\n readonly spacing: number;\n readonly iconSize: number;\n readonly radius: number;\n readonly checkmark: boolean;\n readonly checkIcon: Parameters<typeof G9IconSlot>[0]['icon'];\n readonly groupDisabled: boolean;\n readonly selectedLabel: string;\n readonly onTap: (item: G9SelectionItem) => void;\n readonly direction: 'ltr' | 'rtl';\n /** The chip's automation id (`${groupId}_${key}`). */\n readonly id: string | undefined;\n}\n\nfunction Chip(props: ChipProps): ReactNode {\n const { item } = props;\n const disabled = props.groupDisabled || item.isEnabled === false;\n const press = useG9Press(() => props.onTap(item), { blocked: disabled, source: 'G9ChipGroup', guardMs: 0 });\n return (\n <view\n className={`g9-chip${props.selected ? ' g9-chip--selected' : ''}${item.isEnabled === false ? ' g9-chip--disabled' : ''}`}\n {...g9IdProps(props.id)}\n style={{\n ...g9RowStyle(props.direction),\n height: `${props.height}px`,\n minHeight: `${props.height}px`,\n [props.direction === 'rtl' ? 'marginLeft' : 'marginRight']: `${props.spacing}px`,\n marginBottom: `${props.spacing}px`,\n borderRadius: `${props.radius}px`,\n background: props.background,\n borderColor: props.stroke,\n }}\n accessibility-element={true}\n accessibility-traits=\"button\"\n accessibility-label={item.text}\n {...(props.selected ? { 'accessibility-value': props.selectedLabel } : {})}\n bindtap={press}\n >\n {/* Only when the item has one (MAUI adds the icon view only then): an empty slot still took the row's 6 dp gap,\n and every icon-less chip came out 6 dp wider than MAUI's. */}\n {hasIconSlot({ emoji: item.emoji, icon: item.icon, imagePath: item.iconPath, image: item.iconImage }) ? (\n <G9IconSlot\n emoji={item.emoji}\n icon={item.icon}\n imagePath={item.iconPath}\n image={item.iconImage}\n size={props.iconSize}\n color={props.color}\n />\n ) : null}\n <text className=\"g9-chip__text\" style={{ color: props.color }}>\n {item.text}\n </text>\n {props.checkmark ? (\n <view className={`g9-chip__check${props.selected ? ' g9-chip__check--on' : ''}`}>\n <G9IconSlot icon={props.checkIcon} size={G9Metrics.ChipIconSize} color={props.color} />\n </view>\n ) : null}\n </view>\n );\n}\n"],"names":["EMPTY","G9ChipGroup","props","_props_defaultSelectedKeys","_props_defaultSelectedKey","_props_chipHeight","_props_itemSpacing","_props_iconSize","_props_chipCornerRadius","control","useG9Control","palette","useG9Theme","glyphs","useG9Settings","strings","useG9Strings","single","keys","setKeys","useControllableState","key","setKey","isSelected","item","selectionKey","tap","k","next","x","i","height","G9Metrics","spacing","iconSize","radius","checkmark","chips","_ref","_ref1","selected","bg","fg","parsed","tryParseColor","background","fillToCss","buildSolidOrGradient","color","Chip","formatColor","G9StringKey","g9ChildId","scroll","scroll-view","view","g9RowStyle","disabled","press","useG9Press","g9IdProps","hasIconSlot","G9IconSlot","text"],"mappings":"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"}
@@ -172,8 +172,9 @@ const switch_G9Switch = forwardRef(function(props, ref) {
172
172
  height: `${G9Metrics.SwitchHeight}px`,
173
173
  backgroundColor: track
174
174
  }}>
175
+ {}
175
176
  <view className="g9-switch__mark" style={{
176
- [rtl ? 'right' : 'left']: checked ? `${width - 15 - 6}px` : '12px'
177
+ [rtl ? 'right' : 'left']: checked ? '9px' : '12px'
177
178
  }}>
178
179
  {checked ? <text className="g9-switch__tick">✓</text> : <view className="g9-switch__dot"/>}
179
180
  </view>
@@ -1 +1 @@
1
- {"version":3,"file":"toggle/switch.jsx","sources":["../../src/toggle/switch.tsx"],"sourcesContent":["import './toggle.css';\nimport {\n formatColor,\n G9Metrics,\n G9StringKey,\n switchOffTrack,\n switchOnTrack,\n switchThumbOff,\n switchThumbOn,\n} from '@g9tm/lynxcontrols-foundation';\nimport {\n createContext,\n forwardRef,\n type ReactNode,\n useContext,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from '@lynx-js/react';\nimport { type G9ControlBaseProps, g9RowStyle, g9TextStart, useG9Control } from '../base/control';\nimport { useG9Press } from '../base/press';\nimport { useControllableState } from '../base/use-controllable-state';\nimport { G9CoreContext } from '../provider/contexts';\nimport { useG9Strings, useG9Theme } from '../provider/hooks';\n\n// ---- selection groups (MAUI `SelectionGroup`: radio-style, scoped to the provider) -------------------------------\n\ninterface GroupMember {\n turnOff(): void;\n}\n\nconst registries = new WeakMap<object, Map<string, Set<GroupMember>>>();\nconst globalScope = {};\n\nfunction groupSet(scope: object, group: string): Set<GroupMember> {\n let groups = registries.get(scope);\n if (!groups) {\n groups = new Map();\n registries.set(scope, groups);\n }\n let set = groups.get(group);\n if (!set) {\n set = new Set();\n groups.set(group, set);\n }\n return set;\n}\n\n// ---- G9SwitchGroup ---------------------------------------------------------------------------------------------\n\ninterface SwitchGroupContextValue {\n readonly minOneActive: boolean;\n activeCount(): number;\n report(id: number, on: boolean | null): void;\n}\n\nconst SwitchGroupContext = createContext<SwitchGroupContextValue | null>(null);\n\n/** Props of {@link G9SwitchGroup} (MAUI `G9SwitchGroup`). */\nexport interface G9SwitchGroupProps extends G9ControlBaseProps {\n /** A tap cannot turn off the last active switch of the group (it shakes). Default false. */\n readonly minOneActive?: boolean;\n /** Shows `active / total` under the switches (warning colour at ≤ 1 with `minOneActive`). Default false. */\n readonly showLiveStatus?: boolean;\n /** Told when the number of active switches changes (MAUI `ActiveSwitchCount`). */\n readonly onActiveCountChange?: (active: number, total: number) => void;\n readonly children?: ReactNode;\n}\n\n/**\n * A vertical group of switches (MAUI `G9SwitchGroup`): counts its switches (nearest group only, as MAUI's direct\n * children), optionally keeps one on and shows a live `active / total` status. It does not make switches exclusive:\n * use `selectionGroup` for radio behaviour.\n */\nexport function G9SwitchGroup(props: G9SwitchGroupProps): ReactNode {\n const control = useG9Control(props, 'g9-switch-group');\n const [states, setStates] = useState<ReadonlyMap<number, boolean>>(() => new Map());\n const latest = useRef(states);\n latest.current = states;\n const minOneActive = props.minOneActive === true;\n const minRef = useRef(minOneActive);\n minRef.current = minOneActive;\n // One context object for the group's life: switches key their effects on it.\n const value = useRef<SwitchGroupContextValue | null>(null);\n if (value.current === null) {\n value.current = {\n get minOneActive() {\n return minRef.current;\n },\n activeCount: () => {\n let n = 0;\n for (const on of latest.current.values()) if (on) n++;\n return n;\n },\n report: (id, on) =>\n setStates((current) => {\n if (on === null ? !current.has(id) : current.get(id) === on) return current;\n const next = new Map(current);\n if (on === null) next.delete(id);\n else next.set(id, on);\n latest.current = next;\n return next;\n }),\n };\n }\n let active = 0;\n for (const on of states.values()) if (on) active++;\n const total = states.size;\n const notify = useRef(props.onActiveCountChange);\n notify.current = props.onActiveCountChange;\n useEffect(() => {\n notify.current?.(active, total);\n }, [active, total]);\n const warn = minOneActive && active <= 1;\n return (\n <SwitchGroupContext.Provider value={value.current}>\n <view className={control.className} {...control.idProps}>\n {props.children}\n {props.showLiveStatus ? (\n <text\n className=\"g9-switch-group__status\"\n style={{ color: warn ? 'var(--g9-warning)' : 'var(--g9-text-tertiary)' }}\n >\n {`${active} / ${total}`}\n </text>\n ) : null}\n </view>\n </SwitchGroupContext.Provider>\n );\n}\n\n// ---- G9Switch ----------------------------------------------------------------------------------------------------\n\n/** Props of {@link G9Switch} (MAUI `G9Switch`). */\nexport interface G9SwitchProps extends G9ControlBaseProps {\n /** Controlled state (MAUI `IsOn`). */\n readonly checked?: boolean;\n readonly defaultChecked?: boolean;\n /**\n * Once per change made by the switch: a tap, `toggle()`, or being turned off by its selection group (MAUI\n * `Toggled`). Never for a value the parent pushed in.\n */\n readonly onCheckedChange?: (checked: boolean) => void;\n /** A tap cannot turn it off (it shakes); code still can. Default false. */\n readonly isRequired?: boolean;\n /** Radio group name: turning this on turns off the others with the same name under the same provider. */\n readonly selectionGroup?: string | null;\n /** Form row: title/description fill the start, switch at the end, padded. Default false. */\n readonly isInFormRow?: boolean;\n /** Bold 14 title (form row only); the accessibility name. */\n readonly title?: string | null;\n /** 12 px description under the title (form row only). */\n readonly description?: string | null;\n}\n\n/** Imperative handle of {@link G9Switch}. */\nexport interface G9SwitchHandle {\n /** Same as a tap. */\n toggle(): void;\n}\n\nlet switchIds = 0;\n\n/**\n * Switch (MAUI `G9Switch`): 56 × 32 track, 24 px thumb that stretches while travelling, 240 ms spring-like CSS\n * transition, tick/dot track mark; RTL moves the \"on\" side to the left. A blocked tap (required, or the last active\n * switch of a `minOneActive` group) shakes instead of toggling. Disabled: opacity 0.45.\n */\nexport const G9Switch = forwardRef<G9SwitchHandle, G9SwitchProps>(function G9Switch(props, ref): ReactNode {\n const control = useG9Control(props, 'g9-switch');\n const theme = useG9Theme();\n const strings = useG9Strings();\n const core = useContext(G9CoreContext);\n const group = useContext(SwitchGroupContext);\n const [checked, setChecked] = useControllableState<boolean>({\n value: props.checked,\n defaultValue: props.defaultChecked ?? false,\n onChange: props.onCheckedChange,\n name: 'G9Switch.checked',\n });\n const [shake, setShake] = useState(0);\n const id = useRef(0);\n if (id.current === 0) id.current = ++switchIds;\n const latestChecked = useRef(checked);\n latestChecked.current = checked;\n const setRef = useRef(setChecked);\n setRef.current = setChecked;\n\n // Report to the group.\n useEffect(() => {\n group?.report(id.current, checked);\n }, [group, checked]);\n useEffect(() => () => group?.report(id.current, null), [group]);\n\n // Selection group registration (while mounted and named).\n const groupName = props.selectionGroup && props.selectionGroup.trim() !== '' ? props.selectionGroup : null;\n const scope: object = core ?? globalScope;\n const memberRef = useRef<GroupMember | null>(null);\n useEffect(() => {\n if (!groupName) return;\n const member: GroupMember = { turnOff: () => setRef.current(false) };\n const set = groupSet(scope, groupName);\n set.add(member);\n memberRef.current = member;\n return () => {\n set.delete(member);\n memberRef.current = null;\n };\n }, [groupName, scope]);\n useEffect(() => {\n if (!checked || !groupName) return;\n for (const other of groupSet(scope, groupName)) if (other !== memberRef.current) other.turnOff();\n }, [checked, groupName, scope]);\n\n const toggle = () => {\n if (control.disabled) return;\n const on = latestChecked.current;\n if (on && (props.isRequired || (group?.minOneActive && group.activeCount() <= 1))) {\n setShake((n) => n + 1);\n return;\n }\n setChecked(!on);\n };\n useImperativeHandle(ref, () => ({ toggle }));\n const press = useG9Press(toggle, { blocked: control.disabled, source: 'G9Switch', guardMs: 0 });\n\n const rtl = control.direction === 'rtl';\n const width = G9Metrics.SwitchWidth;\n const thumb = G9Metrics.SwitchThumb;\n const travel = width - 4 - thumb - 4;\n const x = (checked ? travel : 0) * (rtl ? -1 : 1);\n const track = formatColor(checked ? switchOnTrack(theme) : switchOffTrack(theme));\n const thumbColor = formatColor(checked ? switchThumbOn(theme) : switchThumbOff(theme));\n const form = props.isInFormRow === true;\n const title = props.title && props.title.trim() !== '' ? props.title : null;\n const description = props.description && props.description.trim() !== '' ? props.description : null;\n return (\n <view\n className={`${control.className}${form ? ' g9-switch--row' : ''}`}\n {...control.idProps}\n style={g9RowStyle(control.direction)}\n accessibility-element={true}\n accessibility-traits=\"button\"\n {...(checked ? { 'accessibility-value': strings.get(G9StringKey.Selected) } : {})}\n {...((props.accessibilityLabel ?? title)\n ? { 'accessibility-label': props.accessibilityLabel ?? title ?? '' }\n : {})}\n bindtap={press}\n >\n {form ? (\n <view className=\"g9-switch__text\">\n {title ? (\n <text className=\"g9-switch__title\" style={g9TextStart(control.direction)}>\n {title}\n </text>\n ) : null}\n {description ? (\n <text className=\"g9-switch__description\" style={g9TextStart(control.direction)}>\n {description}\n </text>\n ) : null}\n </view>\n ) : null}\n <view\n key={`shake-${shake}`}\n className={`g9-switch__track${shake > 0 ? ' g9-switch__track--shake' : ''}`}\n style={{ width: `${width}px`, height: `${G9Metrics.SwitchHeight}px`, backgroundColor: track }}\n >\n <view\n className=\"g9-switch__mark\"\n style={{ [rtl ? 'right' : 'left']: checked ? `${width - 15 - 6}px` : '12px' }}\n >\n {checked ? <text className=\"g9-switch__tick\">✓</text> : <view className=\"g9-switch__dot\" />}\n </view>\n <view\n className=\"g9-switch__thumb\"\n style={{\n [rtl ? 'right' : 'left']: '4px',\n width: `${thumb}px`,\n height: `${thumb}px`,\n backgroundColor: thumbColor,\n transform: `translateX(${x}px)`,\n }}\n />\n </view>\n </view>\n );\n});\n"],"names":["registries","WeakMap","globalScope","groupSet","scope","group","groups","Map","set","Set","SwitchGroupContext","createContext","G9SwitchGroup","props","control","useG9Control","states","setStates","useState","latest","useRef","minOneActive","minRef","value","n","on","id","current","next","active","total","notify","useEffect","warn","view","text","switchIds","G9Switch","forwardRef","ref","_props_defaultChecked","_props_accessibilityLabel","_ref","theme","useG9Theme","strings","useG9Strings","core","useContext","G9CoreContext","checked","setChecked","useControllableState","shake","setShake","latestChecked","setRef","groupName","memberRef","member","other","toggle","useImperativeHandle","press","useG9Press","rtl","width","G9Metrics","thumb","travel","x","track","formatColor","switchOnTrack","switchOffTrack","thumbColor","switchThumbOn","switchThumbOff","form","title","description","g9RowStyle","G9StringKey","g9TextStart"],"mappings":"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"}
1
+ {"version":3,"file":"toggle/switch.jsx","sources":["../../src/toggle/switch.tsx"],"sourcesContent":["import './toggle.css';\nimport {\n formatColor,\n G9Metrics,\n G9StringKey,\n switchOffTrack,\n switchOnTrack,\n switchThumbOff,\n switchThumbOn,\n} from '@g9tm/lynxcontrols-foundation';\nimport {\n createContext,\n forwardRef,\n type ReactNode,\n useContext,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from '@lynx-js/react';\nimport { type G9ControlBaseProps, g9RowStyle, g9TextStart, useG9Control } from '../base/control';\nimport { useG9Press } from '../base/press';\nimport { useControllableState } from '../base/use-controllable-state';\nimport { G9CoreContext } from '../provider/contexts';\nimport { useG9Strings, useG9Theme } from '../provider/hooks';\n\n// ---- selection groups (MAUI `SelectionGroup`: radio-style, scoped to the provider) -------------------------------\n\ninterface GroupMember {\n turnOff(): void;\n}\n\nconst registries = new WeakMap<object, Map<string, Set<GroupMember>>>();\nconst globalScope = {};\n\nfunction groupSet(scope: object, group: string): Set<GroupMember> {\n let groups = registries.get(scope);\n if (!groups) {\n groups = new Map();\n registries.set(scope, groups);\n }\n let set = groups.get(group);\n if (!set) {\n set = new Set();\n groups.set(group, set);\n }\n return set;\n}\n\n// ---- G9SwitchGroup ---------------------------------------------------------------------------------------------\n\ninterface SwitchGroupContextValue {\n readonly minOneActive: boolean;\n activeCount(): number;\n report(id: number, on: boolean | null): void;\n}\n\nconst SwitchGroupContext = createContext<SwitchGroupContextValue | null>(null);\n\n/** Props of {@link G9SwitchGroup} (MAUI `G9SwitchGroup`). */\nexport interface G9SwitchGroupProps extends G9ControlBaseProps {\n /** A tap cannot turn off the last active switch of the group (it shakes). Default false. */\n readonly minOneActive?: boolean;\n /** Shows `active / total` under the switches (warning colour at ≤ 1 with `minOneActive`). Default false. */\n readonly showLiveStatus?: boolean;\n /** Told when the number of active switches changes (MAUI `ActiveSwitchCount`). */\n readonly onActiveCountChange?: (active: number, total: number) => void;\n readonly children?: ReactNode;\n}\n\n/**\n * A vertical group of switches (MAUI `G9SwitchGroup`): counts its switches (nearest group only, as MAUI's direct\n * children), optionally keeps one on and shows a live `active / total` status. It does not make switches exclusive:\n * use `selectionGroup` for radio behaviour.\n */\nexport function G9SwitchGroup(props: G9SwitchGroupProps): ReactNode {\n const control = useG9Control(props, 'g9-switch-group');\n const [states, setStates] = useState<ReadonlyMap<number, boolean>>(() => new Map());\n const latest = useRef(states);\n latest.current = states;\n const minOneActive = props.minOneActive === true;\n const minRef = useRef(minOneActive);\n minRef.current = minOneActive;\n // One context object for the group's life: switches key their effects on it.\n const value = useRef<SwitchGroupContextValue | null>(null);\n if (value.current === null) {\n value.current = {\n get minOneActive() {\n return minRef.current;\n },\n activeCount: () => {\n let n = 0;\n for (const on of latest.current.values()) if (on) n++;\n return n;\n },\n report: (id, on) =>\n setStates((current) => {\n if (on === null ? !current.has(id) : current.get(id) === on) return current;\n const next = new Map(current);\n if (on === null) next.delete(id);\n else next.set(id, on);\n latest.current = next;\n return next;\n }),\n };\n }\n let active = 0;\n for (const on of states.values()) if (on) active++;\n const total = states.size;\n const notify = useRef(props.onActiveCountChange);\n notify.current = props.onActiveCountChange;\n useEffect(() => {\n notify.current?.(active, total);\n }, [active, total]);\n const warn = minOneActive && active <= 1;\n return (\n <SwitchGroupContext.Provider value={value.current}>\n <view className={control.className} {...control.idProps}>\n {props.children}\n {props.showLiveStatus ? (\n <text\n className=\"g9-switch-group__status\"\n style={{ color: warn ? 'var(--g9-warning)' : 'var(--g9-text-tertiary)' }}\n >\n {`${active} / ${total}`}\n </text>\n ) : null}\n </view>\n </SwitchGroupContext.Provider>\n );\n}\n\n// ---- G9Switch ----------------------------------------------------------------------------------------------------\n\n/** Props of {@link G9Switch} (MAUI `G9Switch`). */\nexport interface G9SwitchProps extends G9ControlBaseProps {\n /** Controlled state (MAUI `IsOn`). */\n readonly checked?: boolean;\n readonly defaultChecked?: boolean;\n /**\n * Once per change made by the switch: a tap, `toggle()`, or being turned off by its selection group (MAUI\n * `Toggled`). Never for a value the parent pushed in.\n */\n readonly onCheckedChange?: (checked: boolean) => void;\n /** A tap cannot turn it off (it shakes); code still can. Default false. */\n readonly isRequired?: boolean;\n /** Radio group name: turning this on turns off the others with the same name under the same provider. */\n readonly selectionGroup?: string | null;\n /** Form row: title/description fill the start, switch at the end, padded. Default false. */\n readonly isInFormRow?: boolean;\n /** Bold 14 title (form row only); the accessibility name. */\n readonly title?: string | null;\n /** 12 px description under the title (form row only). */\n readonly description?: string | null;\n}\n\n/** Imperative handle of {@link G9Switch}. */\nexport interface G9SwitchHandle {\n /** Same as a tap. */\n toggle(): void;\n}\n\nlet switchIds = 0;\n\n/**\n * Switch (MAUI `G9Switch`): 56 × 32 track, 24 px thumb that stretches while travelling, 240 ms spring-like CSS\n * transition, tick/dot track mark; RTL moves the \"on\" side to the left. A blocked tap (required, or the last active\n * switch of a `minOneActive` group) shakes instead of toggling. Disabled: opacity 0.45.\n */\nexport const G9Switch = forwardRef<G9SwitchHandle, G9SwitchProps>(function G9Switch(props, ref): ReactNode {\n const control = useG9Control(props, 'g9-switch');\n const theme = useG9Theme();\n const strings = useG9Strings();\n const core = useContext(G9CoreContext);\n const group = useContext(SwitchGroupContext);\n const [checked, setChecked] = useControllableState<boolean>({\n value: props.checked,\n defaultValue: props.defaultChecked ?? false,\n onChange: props.onCheckedChange,\n name: 'G9Switch.checked',\n });\n const [shake, setShake] = useState(0);\n const id = useRef(0);\n if (id.current === 0) id.current = ++switchIds;\n const latestChecked = useRef(checked);\n latestChecked.current = checked;\n const setRef = useRef(setChecked);\n setRef.current = setChecked;\n\n // Report to the group.\n useEffect(() => {\n group?.report(id.current, checked);\n }, [group, checked]);\n useEffect(() => () => group?.report(id.current, null), [group]);\n\n // Selection group registration (while mounted and named).\n const groupName = props.selectionGroup && props.selectionGroup.trim() !== '' ? props.selectionGroup : null;\n const scope: object = core ?? globalScope;\n const memberRef = useRef<GroupMember | null>(null);\n useEffect(() => {\n if (!groupName) return;\n const member: GroupMember = { turnOff: () => setRef.current(false) };\n const set = groupSet(scope, groupName);\n set.add(member);\n memberRef.current = member;\n return () => {\n set.delete(member);\n memberRef.current = null;\n };\n }, [groupName, scope]);\n useEffect(() => {\n if (!checked || !groupName) return;\n for (const other of groupSet(scope, groupName)) if (other !== memberRef.current) other.turnOff();\n }, [checked, groupName, scope]);\n\n const toggle = () => {\n if (control.disabled) return;\n const on = latestChecked.current;\n if (on && (props.isRequired || (group?.minOneActive && group.activeCount() <= 1))) {\n setShake((n) => n + 1);\n return;\n }\n setChecked(!on);\n };\n useImperativeHandle(ref, () => ({ toggle }));\n const press = useG9Press(toggle, { blocked: control.disabled, source: 'G9Switch', guardMs: 0 });\n\n const rtl = control.direction === 'rtl';\n const width = G9Metrics.SwitchWidth;\n const thumb = G9Metrics.SwitchThumb;\n const travel = width - 4 - thumb - 4;\n const x = (checked ? travel : 0) * (rtl ? -1 : 1);\n const track = formatColor(checked ? switchOnTrack(theme) : switchOffTrack(theme));\n const thumbColor = formatColor(checked ? switchThumbOn(theme) : switchThumbOff(theme));\n const form = props.isInFormRow === true;\n const title = props.title && props.title.trim() !== '' ? props.title : null;\n const description = props.description && props.description.trim() !== '' ? props.description : null;\n return (\n <view\n className={`${control.className}${form ? ' g9-switch--row' : ''}`}\n {...control.idProps}\n style={g9RowStyle(control.direction)}\n accessibility-element={true}\n accessibility-traits=\"button\"\n {...(checked ? { 'accessibility-value': strings.get(G9StringKey.Selected) } : {})}\n {...((props.accessibilityLabel ?? title)\n ? { 'accessibility-label': props.accessibilityLabel ?? title ?? '' }\n : {})}\n bindtap={press}\n >\n {form ? (\n <view className=\"g9-switch__text\">\n {title ? (\n <text className=\"g9-switch__title\" style={g9TextStart(control.direction)}>\n {title}\n </text>\n ) : null}\n {description ? (\n <text className=\"g9-switch__description\" style={g9TextStart(control.direction)}>\n {description}\n </text>\n ) : null}\n </view>\n ) : null}\n <view\n key={`shake-${shake}`}\n className={`g9-switch__track${shake > 0 ? ' g9-switch__track--shake' : ''}`}\n style={{ width: `${width}px`, height: `${G9Metrics.SwitchHeight}px`, backgroundColor: track }}\n >\n {/* MAUI G9SwitchDrawable.DrawTrackMark: the mark is ALWAYS 15 dp from the track's start edge — on, the thumb has\n travelled to the end and the tick shows beside it; off, the thumb covers the dot. (Centring the tick at\n the end put it under the thumb: no tick was ever visible.) */}\n <view className=\"g9-switch__mark\" style={{ [rtl ? 'right' : 'left']: checked ? '9px' : '12px' }}>\n {checked ? <text className=\"g9-switch__tick\">✓</text> : <view className=\"g9-switch__dot\" />}\n </view>\n <view\n className=\"g9-switch__thumb\"\n style={{\n [rtl ? 'right' : 'left']: '4px',\n width: `${thumb}px`,\n height: `${thumb}px`,\n backgroundColor: thumbColor,\n transform: `translateX(${x}px)`,\n }}\n />\n </view>\n </view>\n );\n});\n"],"names":["registries","WeakMap","globalScope","groupSet","scope","group","groups","Map","set","Set","SwitchGroupContext","createContext","G9SwitchGroup","props","control","useG9Control","states","setStates","useState","latest","useRef","minOneActive","minRef","value","n","on","id","current","next","active","total","notify","useEffect","warn","view","text","switchIds","G9Switch","forwardRef","ref","_props_defaultChecked","_props_accessibilityLabel","_ref","theme","useG9Theme","strings","useG9Strings","core","useContext","G9CoreContext","checked","setChecked","useControllableState","shake","setShake","latestChecked","setRef","groupName","memberRef","member","other","toggle","useImperativeHandle","press","useG9Press","rtl","width","G9Metrics","thumb","travel","x","track","formatColor","switchOnTrack","switchOffTrack","thumbColor","switchThumbOn","switchThumbOff","form","title","description","g9RowStyle","G9StringKey","g9TextStart"],"mappings":"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"}
@@ -113,7 +113,10 @@
113
113
 
114
114
  .g9-switch__tick {
115
115
  color: #ffffffbf;
116
- font-size: 12px;
116
+ margin-top: -2px;
117
+ font-size: 15px;
118
+ font-weight: bold;
119
+ line-height: 15px;
117
120
  }
118
121
 
119
122
  .g9-switch__dot {
@@ -140,7 +143,7 @@
140
143
 
141
144
  .g9-chip-group--wrap {
142
145
  flex-flow: wrap;
143
- justify-content: center;
146
+ justify-content: flex-start;
144
147
  display: flex;
145
148
  }
146
149