@g9tm/lynxcontrols 1.4.5 → 1.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/base/pressable.d.ts.map +1 -1
- package/dist/base/pressable.jsx +2 -1
- package/dist/base/pressable.jsx.map +1 -1
- package/dist/cascade/cascade-panel.d.ts.map +1 -1
- package/dist/cascade/cascade-panel.jsx +2 -2
- package/dist/cascade/cascade-panel.jsx.map +1 -1
- package/dist/edge-panel/edge-panel.jsx +5 -5
- package/dist/edge-panel/edge-panel.jsx.map +1 -1
- package/dist/expander/expander.d.ts.map +1 -1
- package/dist/expander/expander.jsx +2 -1
- package/dist/expander/expander.jsx.map +1 -1
- package/dist/host/host.css.map +1 -1
- package/dist/host/host.d.ts +3 -2
- package/dist/host/host.d.ts.map +1 -1
- package/dist/host/host.jsx +2 -2
- package/dist/host/host.jsx.map +1 -1
- package/dist/plus-button/model.d.ts.map +1 -1
- package/dist/plus-button/model.jsx +5 -1
- package/dist/plus-button/model.jsx.map +1 -1
- package/dist/popup/frame.jsx +2 -2
- package/dist/popup/frame.jsx.map +1 -1
- package/dist/popup/service.d.ts.map +1 -1
- package/dist/popup/service.jsx +2 -2
- package/dist/popup/service.jsx.map +1 -1
- package/dist/provider/provider.css +4 -0
- package/dist/provider/provider.css.map +1 -1
- package/dist/provider/provider.d.ts.map +1 -1
- package/dist/provider/provider.jsx +4 -2
- package/dist/provider/provider.jsx.map +1 -1
- package/dist/selection/selection-sheet.d.ts +5 -0
- package/dist/selection/selection-sheet.d.ts.map +1 -1
- package/dist/selection/selection-sheet.jsx +7 -3
- package/dist/selection/selection-sheet.jsx.map +1 -1
- package/dist/sheet/frame.d.ts.map +1 -1
- package/dist/sheet/frame.jsx +30 -8
- package/dist/sheet/frame.jsx.map +1 -1
- package/dist/sheet/index.d.ts +1 -0
- package/dist/sheet/index.d.ts.map +1 -1
- package/dist/sheet/index.jsx +1 -0
- package/dist/sheet/motion.d.ts.map +1 -1
- package/dist/sheet/motion.jsx +14 -0
- package/dist/sheet/motion.jsx.map +1 -1
- package/dist/sheet/options.d.ts +6 -0
- package/dist/sheet/options.d.ts.map +1 -1
- package/dist/sheet/options.jsx.map +1 -1
- package/dist/sheet/scroll-indicator.d.ts +33 -0
- package/dist/sheet/scroll-indicator.d.ts.map +1 -0
- package/dist/sheet/scroll-indicator.jsx +84 -0
- package/dist/sheet/scroll-indicator.jsx.map +1 -0
- package/dist/sheet/sheet.css +14 -0
- package/dist/sheet/sheet.css.map +1 -1
- package/dist/tab-bar/tab-bar.d.ts.map +1 -1
- package/dist/tab-bar/tab-bar.jsx +11 -6
- package/dist/tab-bar/tab-bar.jsx.map +1 -1
- package/dist/toast/service.jsx +3 -3
- package/dist/toast/service.jsx.map +1 -1
- package/dist/toast/store.d.ts.map +1 -1
- package/dist/toast/store.jsx +3 -2
- package/dist/toast/store.jsx.map +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider/provider.jsx","sources":["../../src/provider/provider.tsx"],"sourcesContent":["import {\n assertRequiredCapabilities,\n createCultureStore,\n createDiagnostics,\n createIconFontRegistry,\n createPreferences,\n createSafeExecutor,\n createStrings,\n createThemeStore,\n type G9Capabilities,\n type G9ColorScheme,\n type G9CssVariables,\n type G9Culture,\n type G9CultureStore,\n type G9Diagnostics,\n type G9SafeInsets,\n type G9ThemeMode,\n type G9ThemeOptions,\n type G9ThemeStore,\n toCssVariables,\n toLayoutCssVariables,\n toMetricCssVariables,\n} from '@g9tm/lynxcontrols-foundation';\nimport { type ReactNode, useEffect, useMemo, useRef, useState, useSyncExternalStore } from '@lynx-js/react';\nimport { useStableCallback } from '../base/use-stable-callback';\nimport { g9Hosts } from '../host/registry';\nimport {\n G9CoreContext,\n G9CultureContext,\n G9DiagnosticsContext,\n G9InsetsContext,\n G9KeyboardContext,\n type G9KeyboardState,\n type G9ProviderCore,\n G9SettingsContext,\n G9WindowContext,\n type G9WindowState,\n} from './contexts';\nimport { addGlobalListener } from './global-events';\nimport { type G9Settings, g9DefaultErrorPresenter, resolveSettings } from './settings';\nimport { applyThemeVariables, changedVariables } from './theme-root';\nimport './provider.css';\nimport './theme.css';\n\n/** Props of {@link G9Provider}. */\nexport interface G9ProviderProps {\n /**\n * The app's platform services (plan/specs/01 §8). `clock`, `preferences` and `host` are required: a missing one\n * throws `G9CapabilityMissingError` naming it when the provider mounts. Pass a stable object.\n */\n readonly services: Partial<G9Capabilities>;\n /**\n * The culture: a store the app drives (`createCultureStore`), or a culture value / locale tag the provider keeps in\n * its own store (a new value switches it). Default `'en'`.\n */\n readonly culture?: G9CultureStore | G9Culture | string;\n /**\n * The theme: a store the app drives (`createThemeStore`), or options for the provider's own store (changes to\n * `mode`, `systemScheme`, `palettes`, `overrides` are applied to it). Default: system mode, stock palettes.\n */\n readonly theme?: G9ThemeStore | G9ThemeOptions;\n /** The platform's current colour scheme (MAUI `RequestedThemeChanged`); followed while the mode is `'system'`. */\n readonly systemScheme?: G9ColorScheme;\n /**\n * Save the theme mode in preferences and restore it at mount (MAUI `G9Theme.ChangeTheme` / `Init`, key\n * `g9controls.theme`, values 0 system, 1 light, 2 dark). Default `true`.\n */\n readonly persistTheme?: boolean;\n /** App-wide settings (plan/specs/03 §6). Validated at mount and on change. */\n readonly settings?: G9Settings;\n /** The diagnostics hub. Default: a hub the provider creates. */\n readonly diagnostics?: G9Diagnostics;\n readonly className?: string;\n readonly children?: ReactNode;\n}\n\nconst THEME_PREFERENCE_KEY = 'theme';\nconst THEME_MODES: readonly G9ThemeMode[] = ['system', 'light', 'dark'];\n\nlet providerSequence = 0;\n\nfunction isCultureStore(value: G9ProviderProps['culture']): value is G9CultureStore {\n return typeof value === 'object' && value !== null && typeof (value as G9CultureStore).getSnapshot === 'function';\n}\n\nfunction isThemeStore(value: G9ProviderProps['theme']): value is G9ThemeStore {\n return typeof value === 'object' && value !== null && typeof (value as G9ThemeStore).getSnapshot === 'function';\n}\n\n/** The logical window size the platform reports at startup (`SystemInfo` is in physical pixels). */\nfunction initialWindow(): G9WindowState {\n const info = (globalThis as { SystemInfo?: { pixelWidth?: number; pixelHeight?: number; pixelRatio?: number } })\n .SystemInfo;\n const ratio = info?.pixelRatio && info.pixelRatio > 0 ? info.pixelRatio : 1;\n return Object.freeze({ width: (info?.pixelWidth ?? 0) / ratio, height: (info?.pixelHeight ?? 0) / ratio });\n}\n\nconst HIDDEN_KEYBOARD: G9KeyboardState = Object.freeze({ visible: false, height: 0, compatHeight: 0 });\n\nfunction finite(value: unknown): number {\n return typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : 0;\n}\n\n/**\n * The root every G9 component lives in (plan/specs/01 §5, MAUI `G9Theme`/`G9Culture`/`G9Strings` statics,\n * `G9ServiceProvider`, `G9ScreenHelper`, `G9KeyboardHelper`): culture and strings, the theme as CSS variables on one\n * root `<view>`, settings, services and capabilities, the diagnostics hub, the shared safe executor, keyboard, window\n * and safe-area state, and the dispatch of the system back event to the active `G9Host`. Mount it once per LynxView.\n *\n * - A theme change updates the root's CSS variables with `setProperty`: nothing re-renders except `useG9Theme`\n * readers. A culture change re-renders culture readers.\n * - One `keyboardstatuschanged` and one `onWindowResize` listener for the whole tree.\n * - The back event (`settings.backEventName`, default `g9:back`) goes to the active host's back stack; when nothing\n * handles it, `services.host.exit()` runs (desktop hosts send the same event for Escape).\n * - Unmounting removes every listener, subscription and timer it created.\n */\nexport function G9Provider(props: G9ProviderProps): ReactNode {\n const { services, persistTheme = true } = props;\n assertRequiredCapabilities(services);\n\n const [ownDiagnostics] = useState(createDiagnostics);\n const diagnostics = props.diagnostics ?? ownDiagnostics;\n const [iconFonts] = useState(createIconFontRegistry);\n const settings = useMemo(() => resolveSettings(props.settings, iconFonts), [props.settings, iconFonts]);\n\n // Culture: the app's store, or one the provider keeps in step with the prop.\n const cultureProp = props.culture ?? 'en';\n const [ownCulture] = useState(() => createCultureStore(isCultureStore(cultureProp) ? 'en' : cultureProp));\n const cultureStore = isCultureStore(cultureProp) ? cultureProp : ownCulture;\n useEffect(() => {\n if (!isCultureStore(cultureProp)) ownCulture.setCulture(cultureProp);\n }, [cultureProp, ownCulture]);\n const culture = useSyncExternalStore(cultureStore.subscribe, cultureStore.getSnapshot);\n const strings = useMemo(() => createStrings({ ...settings.strings, culture }), [settings.strings, culture]);\n const cultureValue = useMemo(() => ({ culture, strings, store: cultureStore }), [culture, strings, cultureStore]);\n\n // Theme: the app's store, or one the provider keeps in step with the options.\n const themeProp = props.theme;\n const [ownTheme] = useState(() => createThemeStore(isThemeStore(themeProp) ? {} : (themeProp ?? {})));\n const themeStore = isThemeStore(themeProp) ? themeProp : ownTheme;\n const options = isThemeStore(themeProp) ? undefined : themeProp;\n useEffect(() => {\n if (!options) return;\n ownTheme.beginBatch();\n try {\n if (options.mode !== undefined) ownTheme.setMode(options.mode);\n if (options.systemScheme !== undefined) ownTheme.setSystemScheme(options.systemScheme);\n ownTheme.setPalettes(options.palettes);\n ownTheme.setOverrides(options.overrides);\n } finally {\n ownTheme.endBatch();\n }\n // Field-wise on purpose: an inline `theme={{ … }}` object must not re-apply (and undo a restored mode) per render.\n }, [ownTheme, options?.mode, options?.systemScheme, options?.palettes, options?.overrides]);\n useEffect(() => {\n if (props.systemScheme !== undefined) themeStore.setSystemScheme(props.systemScheme);\n }, [themeStore, props.systemScheme]);\n\n const executorStrings = useRef(strings);\n executorStrings.current = strings;\n const [rootId] = useState(() => {\n providerSequence += 1;\n return `g9-provider-${providerSequence}`;\n });\n\n const core = useMemo<G9ProviderCore>(() => {\n const preferences = createPreferences({\n store: services.preferences,\n onStoreError: (error, failure) =>\n diagnostics.log({\n level: 'warning',\n message: `Preference ${failure.operation} failed for '${failure.key}'.`,\n source: 'G9Provider',\n error,\n }),\n });\n const executorSettings = settings.executor;\n const executor = createSafeExecutor({\n clock: services.clock,\n diagnostics,\n strings: { get: (key) => executorStrings.current.get(key) },\n // No app presenter: the default one (the error popup, registered by `@g9tm/lynxcontrols/popup`) if loaded.\n showError:\n executorSettings.showError ??\n ((request) => {\n const presenter = g9DefaultErrorPresenter.current;\n if (presenter) return presenter(request);\n diagnostics.log({\n level: 'warning',\n message: `No error presenter is configured; not shown: ${request.message}`,\n source: request.source,\n });\n return undefined;\n }),\n ...(executorSettings.diagnosticsHandler ? { diagnosticsHandler: executorSettings.diagnosticsHandler } : {}),\n ...(executorSettings.diagnosticsAvailable ? { diagnosticsAvailable: executorSettings.diagnosticsAvailable } : {}),\n ...(executorSettings.defaults ? { defaults: executorSettings.defaults } : {}),\n });\n return Object.freeze({ services, themeStore, preferences, diagnostics, executor, rootId });\n }, [services, themeStore, diagnostics, settings.executor, rootId]);\n useEffect(() => () => core.executor.dispose(), [core]);\n\n // Theme root. The Lynx engine drops custom properties in an inline style object (page config\n // `enableCSSInlineVariables` is off and not exposed), so the first frame takes the stock palette of the initial\n // scheme and the default metrics from theme.css classes; the inline copy below only serves engines that honour it.\n // At mount every variable is then written with setProperty (custom palettes, overrides, layout), and each later\n // theme change sends only what changed (no re-render).\n const [initialVariables] = useState<G9CssVariables>(() => ({\n ...toMetricCssVariables(),\n ...toLayoutCssVariables(),\n ...toCssVariables(themeStore.getSnapshot()),\n }));\n const [initialScheme] = useState(() => themeStore.getSnapshot().scheme);\n const applied = useRef<G9CssVariables>({});\n const [fallbackVariables, setFallbackVariables] = useState<G9CssVariables | null>(null);\n useEffect(() => {\n const apply = () => {\n const next = { ...initialVariables, ...applied.current, ...toCssVariables(themeStore.getSnapshot()) };\n const diff = changedVariables(applied.current, next);\n applied.current = next;\n if (!diff) return;\n // Without the element API (a host that lacks it), re-render the root's style instead: still no child render.\n if (!applyThemeVariables(rootId, diff)) setFallbackVariables(next);\n };\n apply();\n return themeStore.subscribe(apply);\n }, [themeStore, rootId, initialVariables]);\n\n // Theme persistence (MAUI ChangeTheme wrote the AppTheme number, Init read it back).\n useEffect(() => {\n if (!persistTheme) return;\n let alive = true;\n const save = () => {\n const value = THEME_MODES.indexOf(themeStore.getSnapshot().mode);\n if (core.preferences.getInt(THEME_PREFERENCE_KEY, -1) !== value) {\n void core.preferences.setInt(THEME_PREFERENCE_KEY, value);\n }\n };\n let lastMode = themeStore.getSnapshot().mode;\n const unsubscribe = themeStore.subscribe((theme) => {\n if (theme.mode === lastMode) return;\n lastMode = theme.mode;\n save();\n });\n core.preferences.hydrate().then(\n () => {\n if (!alive) return;\n const stored = THEME_MODES[core.preferences.getInt(THEME_PREFERENCE_KEY, -1)];\n if (stored) themeStore.setMode(stored);\n },\n (error: unknown) =>\n diagnostics.log({ level: 'warning', message: 'Theme restore failed.', source: 'G9Provider', error }),\n );\n return () => {\n alive = false;\n unsubscribe();\n };\n }, [persistTheme, themeStore, core, diagnostics]);\n\n // Keyboard and window: one listener each for the whole tree.\n const [keyboard, setKeyboard] = useState<G9KeyboardState>(HIDDEN_KEYBOARD);\n useEffect(\n () =>\n addGlobalListener(\n 'keyboardstatuschanged',\n (status: unknown, height: unknown, compatHeight: unknown) => {\n setKeyboard(\n status === 'on'\n ? Object.freeze({ visible: true, height: finite(height), compatHeight: finite(compatHeight) })\n : HIDDEN_KEYBOARD,\n );\n },\n // A host without keyboard detection (windowless Lynxtron): the keyboard simply stays hidden.\n (error) =>\n diagnostics.log({\n level: 'warning',\n message: 'Keyboard events are unavailable on this host.',\n source: 'G9Provider',\n error,\n }),\n ),\n [diagnostics],\n );\n const [windowState, setWindowState] = useState<G9WindowState>(initialWindow);\n useEffect(\n () =>\n addGlobalListener('onWindowResize', (width: unknown, height: unknown) => {\n setWindowState((current) => {\n const next = {\n width: typeof width === 'number' && width >= 0 ? width : current.width,\n height: typeof height === 'number' && height >= 0 ? height : current.height,\n };\n return next.width === current.width && next.height === current.height ? current : Object.freeze(next);\n });\n }),\n [],\n );\n\n // Safe-area insets from the host service.\n const [insets, setInsets] = useState<G9SafeInsets>(() => services.host.getSafeInsets());\n useEffect(() => {\n setInsets(services.host.getSafeInsets());\n return services.host.subscribeSafeInsets((next) => setInsets(next));\n }, [services.host]);\n\n // System back: the active host of this provider first, then exit.\n const onBack = useStableCallback(() => {\n const host = g9Hosts.active();\n if (host && host.services === services && host.back.dispatch() === 'handled') return;\n services.host.exit();\n });\n useEffect(() => addGlobalListener(settings.backEventName, onBack), [settings.backEventName, onBack]);\n\n const rootStyle = fallbackVariables ?? initialVariables;\n const directionClass = culture.direction === 'rtl' ? 'g9-dir-rtl' : 'g9-dir-ltr';\n return (\n <G9CoreContext.Provider value={core}>\n <G9DiagnosticsContext.Provider value={diagnostics}>\n <G9SettingsContext.Provider value={settings}>\n <G9CultureContext.Provider value={cultureValue}>\n <G9KeyboardContext.Provider value={keyboard}>\n <G9WindowContext.Provider value={windowState}>\n <G9InsetsContext.Provider value={insets}>\n <view\n id={rootId}\n className={`g9-provider g9-theme-${initialScheme} ${directionClass}${props.className ? ` ${props.className}` : ''}`}\n style={rootStyle}\n >\n {props.children}\n </view>\n </G9InsetsContext.Provider>\n </G9WindowContext.Provider>\n </G9KeyboardContext.Provider>\n </G9CultureContext.Provider>\n </G9SettingsContext.Provider>\n </G9DiagnosticsContext.Provider>\n </G9CoreContext.Provider>\n );\n}\n"],"names":["THEME_PREFERENCE_KEY","THEME_MODES","providerSequence","isCultureStore","value","isThemeStore","initialWindow","info","globalThis","ratio","Object","HIDDEN_KEYBOARD","finite","Number","G9Provider","props","_props_diagnostics","_props_culture","services","persistTheme","assertRequiredCapabilities","ownDiagnostics","useState","createDiagnostics","diagnostics","iconFonts","createIconFontRegistry","settings","useMemo","resolveSettings","cultureProp","ownCulture","createCultureStore","cultureStore","useEffect","culture","useSyncExternalStore","strings","createStrings","cultureValue","themeProp","ownTheme","createThemeStore","themeStore","options","undefined","executorStrings","useRef","rootId","core","_executorSettings_showError","preferences","createPreferences","error","failure","executorSettings","executor","createSafeExecutor","key","request","presenter","g9DefaultErrorPresenter","initialVariables","toMetricCssVariables","toLayoutCssVariables","toCssVariables","initialScheme","applied","fallbackVariables","setFallbackVariables","apply","next","diff","changedVariables","applyThemeVariables","alive","save","lastMode","unsubscribe","theme","stored","keyboard","setKeyboard","addGlobalListener","status","height","compatHeight","windowState","setWindowState","width","current","insets","setInsets","onBack","useStableCallback","host","g9Hosts","rootStyle","directionClass","G9CoreContext","G9DiagnosticsContext","G9SettingsContext","G9CultureContext","G9KeyboardContext","G9WindowContext","G9InsetsContext","view"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"provider/provider.jsx","sources":["../../src/provider/provider.tsx"],"sourcesContent":["import {\n assertRequiredCapabilities,\n createCultureStore,\n createDiagnostics,\n createIconFontRegistry,\n createPreferences,\n createSafeExecutor,\n createStrings,\n createThemeStore,\n type G9Capabilities,\n type G9ColorScheme,\n type G9CssVariables,\n type G9Culture,\n type G9CultureStore,\n type G9Diagnostics,\n type G9SafeInsets,\n type G9ThemeMode,\n type G9ThemeOptions,\n type G9ThemeStore,\n g9MotionScale,\n subscribeG9Motion,\n toCssVariables,\n toLayoutCssVariables,\n toMetricCssVariables,\n} from '@g9tm/lynxcontrols-foundation';\nimport { type ReactNode, useEffect, useMemo, useRef, useState, useSyncExternalStore } from '@lynx-js/react';\nimport { useStableCallback } from '../base/use-stable-callback';\nimport { g9Hosts } from '../host/registry';\nimport {\n G9CoreContext,\n G9CultureContext,\n G9DiagnosticsContext,\n G9InsetsContext,\n G9KeyboardContext,\n type G9KeyboardState,\n type G9ProviderCore,\n G9SettingsContext,\n G9WindowContext,\n type G9WindowState,\n} from './contexts';\nimport { addGlobalListener } from './global-events';\nimport { type G9Settings, g9DefaultErrorPresenter, resolveSettings } from './settings';\nimport { applyThemeVariables, changedVariables } from './theme-root';\nimport './provider.css';\nimport './theme.css';\n\n/** Props of {@link G9Provider}. */\nexport interface G9ProviderProps {\n /**\n * The app's platform services (plan/specs/01 §8). `clock`, `preferences` and `host` are required: a missing one\n * throws `G9CapabilityMissingError` naming it when the provider mounts. Pass a stable object.\n */\n readonly services: Partial<G9Capabilities>;\n /**\n * The culture: a store the app drives (`createCultureStore`), or a culture value / locale tag the provider keeps in\n * its own store (a new value switches it). Default `'en'`.\n */\n readonly culture?: G9CultureStore | G9Culture | string;\n /**\n * The theme: a store the app drives (`createThemeStore`), or options for the provider's own store (changes to\n * `mode`, `systemScheme`, `palettes`, `overrides` are applied to it). Default: system mode, stock palettes.\n */\n readonly theme?: G9ThemeStore | G9ThemeOptions;\n /** The platform's current colour scheme (MAUI `RequestedThemeChanged`); followed while the mode is `'system'`. */\n readonly systemScheme?: G9ColorScheme;\n /**\n * Save the theme mode in preferences and restore it at mount (MAUI `G9Theme.ChangeTheme` / `Init`, key\n * `g9controls.theme`, values 0 system, 1 light, 2 dark). Default `true`.\n */\n readonly persistTheme?: boolean;\n /** App-wide settings (plan/specs/03 §6). Validated at mount and on change. */\n readonly settings?: G9Settings;\n /** The diagnostics hub. Default: a hub the provider creates. */\n readonly diagnostics?: G9Diagnostics;\n readonly className?: string;\n readonly children?: ReactNode;\n}\n\nconst THEME_PREFERENCE_KEY = 'theme';\nconst THEME_MODES: readonly G9ThemeMode[] = ['system', 'light', 'dark'];\n\nlet providerSequence = 0;\n\nfunction isCultureStore(value: G9ProviderProps['culture']): value is G9CultureStore {\n return typeof value === 'object' && value !== null && typeof (value as G9CultureStore).getSnapshot === 'function';\n}\n\nfunction isThemeStore(value: G9ProviderProps['theme']): value is G9ThemeStore {\n return typeof value === 'object' && value !== null && typeof (value as G9ThemeStore).getSnapshot === 'function';\n}\n\n/** The logical window size the platform reports at startup (`SystemInfo` is in physical pixels). */\nfunction initialWindow(): G9WindowState {\n const info = (globalThis as { SystemInfo?: { pixelWidth?: number; pixelHeight?: number; pixelRatio?: number } })\n .SystemInfo;\n const ratio = info?.pixelRatio && info.pixelRatio > 0 ? info.pixelRatio : 1;\n return Object.freeze({ width: (info?.pixelWidth ?? 0) / ratio, height: (info?.pixelHeight ?? 0) / ratio });\n}\n\nconst HIDDEN_KEYBOARD: G9KeyboardState = Object.freeze({ visible: false, height: 0, compatHeight: 0 });\n\nfunction finite(value: unknown): number {\n return typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : 0;\n}\n\n/**\n * The root every G9 component lives in (plan/specs/01 §5, MAUI `G9Theme`/`G9Culture`/`G9Strings` statics,\n * `G9ServiceProvider`, `G9ScreenHelper`, `G9KeyboardHelper`): culture and strings, the theme as CSS variables on one\n * root `<view>`, settings, services and capabilities, the diagnostics hub, the shared safe executor, keyboard, window\n * and safe-area state, and the dispatch of the system back event to the active `G9Host`. Mount it once per LynxView.\n *\n * - A theme change updates the root's CSS variables with `setProperty`: nothing re-renders except `useG9Theme`\n * readers. A culture change re-renders culture readers.\n * - One `keyboardstatuschanged` and one `onWindowResize` listener for the whole tree.\n * - The back event (`settings.backEventName`, default `g9:back`) goes to the active host's back stack; when nothing\n * handles it, `services.host.exit()` runs (desktop hosts send the same event for Escape).\n * - Unmounting removes every listener, subscription and timer it created.\n */\nexport function G9Provider(props: G9ProviderProps): ReactNode {\n const { services, persistTheme = true } = props;\n assertRequiredCapabilities(services);\n\n const [ownDiagnostics] = useState(createDiagnostics);\n const diagnostics = props.diagnostics ?? ownDiagnostics;\n const [iconFonts] = useState(createIconFontRegistry);\n const settings = useMemo(() => resolveSettings(props.settings, iconFonts), [props.settings, iconFonts]);\n\n // Culture: the app's store, or one the provider keeps in step with the prop.\n const cultureProp = props.culture ?? 'en';\n const [ownCulture] = useState(() => createCultureStore(isCultureStore(cultureProp) ? 'en' : cultureProp));\n const cultureStore = isCultureStore(cultureProp) ? cultureProp : ownCulture;\n useEffect(() => {\n if (!isCultureStore(cultureProp)) ownCulture.setCulture(cultureProp);\n }, [cultureProp, ownCulture]);\n const culture = useSyncExternalStore(cultureStore.subscribe, cultureStore.getSnapshot);\n const strings = useMemo(() => createStrings({ ...settings.strings, culture }), [settings.strings, culture]);\n const cultureValue = useMemo(() => ({ culture, strings, store: cultureStore }), [culture, strings, cultureStore]);\n\n // Theme: the app's store, or one the provider keeps in step with the options.\n const themeProp = props.theme;\n const [ownTheme] = useState(() => createThemeStore(isThemeStore(themeProp) ? {} : (themeProp ?? {})));\n const themeStore = isThemeStore(themeProp) ? themeProp : ownTheme;\n const options = isThemeStore(themeProp) ? undefined : themeProp;\n useEffect(() => {\n if (!options) return;\n ownTheme.beginBatch();\n try {\n if (options.mode !== undefined) ownTheme.setMode(options.mode);\n if (options.systemScheme !== undefined) ownTheme.setSystemScheme(options.systemScheme);\n ownTheme.setPalettes(options.palettes);\n ownTheme.setOverrides(options.overrides);\n } finally {\n ownTheme.endBatch();\n }\n // Field-wise on purpose: an inline `theme={{ … }}` object must not re-apply (and undo a restored mode) per render.\n }, [ownTheme, options?.mode, options?.systemScheme, options?.palettes, options?.overrides]);\n useEffect(() => {\n if (props.systemScheme !== undefined) themeStore.setSystemScheme(props.systemScheme);\n }, [themeStore, props.systemScheme]);\n\n const executorStrings = useRef(strings);\n executorStrings.current = strings;\n const [rootId] = useState(() => {\n providerSequence += 1;\n return `g9-provider-${providerSequence}`;\n });\n\n const core = useMemo<G9ProviderCore>(() => {\n const preferences = createPreferences({\n store: services.preferences,\n onStoreError: (error, failure) =>\n diagnostics.log({\n level: 'warning',\n message: `Preference ${failure.operation} failed for '${failure.key}'.`,\n source: 'G9Provider',\n error,\n }),\n });\n const executorSettings = settings.executor;\n const executor = createSafeExecutor({\n clock: services.clock,\n diagnostics,\n strings: { get: (key) => executorStrings.current.get(key) },\n // No app presenter: the default one (the error popup, registered by `@g9tm/lynxcontrols/popup`) if loaded.\n showError:\n executorSettings.showError ??\n ((request) => {\n const presenter = g9DefaultErrorPresenter.current;\n if (presenter) return presenter(request);\n diagnostics.log({\n level: 'warning',\n message: `No error presenter is configured; not shown: ${request.message}`,\n source: request.source,\n });\n return undefined;\n }),\n ...(executorSettings.diagnosticsHandler ? { diagnosticsHandler: executorSettings.diagnosticsHandler } : {}),\n ...(executorSettings.diagnosticsAvailable ? { diagnosticsAvailable: executorSettings.diagnosticsAvailable } : {}),\n ...(executorSettings.defaults ? { defaults: executorSettings.defaults } : {}),\n });\n return Object.freeze({ services, themeStore, preferences, diagnostics, executor, rootId });\n }, [services, themeStore, diagnostics, settings.executor, rootId]);\n useEffect(() => () => core.executor.dispose(), [core]);\n\n // Theme root. The Lynx engine drops custom properties in an inline style object (page config\n // `enableCSSInlineVariables` is off and not exposed), so the first frame takes the stock palette of the initial\n // scheme and the default metrics from theme.css classes; the inline copy below only serves engines that honour it.\n // At mount every variable is then written with setProperty (custom palettes, overrides, layout), and each later\n // theme change sends only what changed (no re-render).\n const [initialVariables] = useState<G9CssVariables>(() => ({\n ...toMetricCssVariables(),\n ...toLayoutCssVariables(),\n ...toCssVariables(themeStore.getSnapshot()),\n }));\n const [initialScheme] = useState(() => themeStore.getSnapshot().scheme);\n const applied = useRef<G9CssVariables>({});\n const [fallbackVariables, setFallbackVariables] = useState<G9CssVariables | null>(null);\n useEffect(() => {\n const apply = () => {\n const next = { ...initialVariables, ...applied.current, ...toCssVariables(themeStore.getSnapshot()) };\n const diff = changedVariables(applied.current, next);\n applied.current = next;\n if (!diff) return;\n // Without the element API (a host that lacks it), re-render the root's style instead: still no child render.\n if (!applyThemeVariables(rootId, diff)) setFallbackVariables(next);\n };\n apply();\n return themeStore.subscribe(apply);\n }, [themeStore, rootId, initialVariables]);\n\n // Theme persistence (MAUI ChangeTheme wrote the AppTheme number, Init read it back).\n useEffect(() => {\n if (!persistTheme) return;\n let alive = true;\n const save = () => {\n const value = THEME_MODES.indexOf(themeStore.getSnapshot().mode);\n if (core.preferences.getInt(THEME_PREFERENCE_KEY, -1) !== value) {\n void core.preferences.setInt(THEME_PREFERENCE_KEY, value);\n }\n };\n let lastMode = themeStore.getSnapshot().mode;\n const unsubscribe = themeStore.subscribe((theme) => {\n if (theme.mode === lastMode) return;\n lastMode = theme.mode;\n save();\n });\n core.preferences.hydrate().then(\n () => {\n if (!alive) return;\n const stored = THEME_MODES[core.preferences.getInt(THEME_PREFERENCE_KEY, -1)];\n if (stored) themeStore.setMode(stored);\n },\n (error: unknown) =>\n diagnostics.log({ level: 'warning', message: 'Theme restore failed.', source: 'G9Provider', error }),\n );\n return () => {\n alive = false;\n unsubscribe();\n };\n }, [persistTheme, themeStore, core, diagnostics]);\n\n // Keyboard and window: one listener each for the whole tree.\n const [keyboard, setKeyboard] = useState<G9KeyboardState>(HIDDEN_KEYBOARD);\n useEffect(\n () =>\n addGlobalListener(\n 'keyboardstatuschanged',\n (status: unknown, height: unknown, compatHeight: unknown) => {\n setKeyboard(\n status === 'on'\n ? Object.freeze({ visible: true, height: finite(height), compatHeight: finite(compatHeight) })\n : HIDDEN_KEYBOARD,\n );\n },\n // A host without keyboard detection (windowless Lynxtron): the keyboard simply stays hidden.\n (error) =>\n diagnostics.log({\n level: 'warning',\n message: 'Keyboard events are unavailable on this host.',\n source: 'G9Provider',\n error,\n }),\n ),\n [diagnostics],\n );\n const [windowState, setWindowState] = useState<G9WindowState>(initialWindow);\n useEffect(\n () =>\n addGlobalListener('onWindowResize', (width: unknown, height: unknown) => {\n setWindowState((current) => {\n const next = {\n width: typeof width === 'number' && width >= 0 ? width : current.width,\n height: typeof height === 'number' && height >= 0 ? height : current.height,\n };\n return next.width === current.width && next.height === current.height ? current : Object.freeze(next);\n });\n }),\n [],\n );\n\n // Safe-area insets from the host service.\n const [insets, setInsets] = useState<G9SafeInsets>(() => services.host.getSafeInsets());\n useEffect(() => {\n setInsets(services.host.getSafeInsets());\n return services.host.subscribeSafeInsets((next) => setInsets(next));\n }, [services.host]);\n\n // System back: the active host of this provider first, then exit.\n const onBack = useStableCallback(() => {\n const host = g9Hosts.active();\n if (host && host.services === services && host.back.dispatch() === 'handled') return;\n services.host.exit();\n });\n useEffect(() => addGlobalListener(settings.backEventName, onBack), [settings.backEventName, onBack]);\n\n // setG9MotionScale(0) (reduced motion; a screen that holds still for tests): the CSS transitions end at once too — the\n // root carries `g9-motion-off` and theme.css sets their durations to 0 (JS-timed motion already follows g9Ms).\n const [motionOff, setMotionOff] = useState(() => g9MotionScale() === 0);\n useEffect(() => subscribeG9Motion((s) => setMotionOff(s === 0)), []);\n\n const rootStyle = fallbackVariables ?? initialVariables;\n const directionClass = culture.direction === 'rtl' ? 'g9-dir-rtl' : 'g9-dir-ltr';\n return (\n <G9CoreContext.Provider value={core}>\n <G9DiagnosticsContext.Provider value={diagnostics}>\n <G9SettingsContext.Provider value={settings}>\n <G9CultureContext.Provider value={cultureValue}>\n <G9KeyboardContext.Provider value={keyboard}>\n <G9WindowContext.Provider value={windowState}>\n <G9InsetsContext.Provider value={insets}>\n <view\n id={rootId}\n className={`g9-provider g9-theme-${initialScheme} ${directionClass}${motionOff ? ' g9-motion-off' : ''}${props.className ? ` ${props.className}` : ''}`}\n style={rootStyle}\n >\n {props.children}\n </view>\n </G9InsetsContext.Provider>\n </G9WindowContext.Provider>\n </G9KeyboardContext.Provider>\n </G9CultureContext.Provider>\n </G9SettingsContext.Provider>\n </G9DiagnosticsContext.Provider>\n </G9CoreContext.Provider>\n );\n}\n"],"names":["THEME_PREFERENCE_KEY","THEME_MODES","providerSequence","isCultureStore","value","isThemeStore","initialWindow","info","globalThis","ratio","Object","HIDDEN_KEYBOARD","finite","Number","G9Provider","props","_props_diagnostics","_props_culture","services","persistTheme","assertRequiredCapabilities","ownDiagnostics","useState","createDiagnostics","diagnostics","iconFonts","createIconFontRegistry","settings","useMemo","resolveSettings","cultureProp","ownCulture","createCultureStore","cultureStore","useEffect","culture","useSyncExternalStore","strings","createStrings","cultureValue","themeProp","ownTheme","createThemeStore","themeStore","options","undefined","executorStrings","useRef","rootId","core","_executorSettings_showError","preferences","createPreferences","error","failure","executorSettings","executor","createSafeExecutor","key","request","presenter","g9DefaultErrorPresenter","initialVariables","toMetricCssVariables","toLayoutCssVariables","toCssVariables","initialScheme","applied","fallbackVariables","setFallbackVariables","apply","next","diff","changedVariables","applyThemeVariables","alive","save","lastMode","unsubscribe","theme","stored","keyboard","setKeyboard","addGlobalListener","status","height","compatHeight","windowState","setWindowState","width","current","insets","setInsets","onBack","useStableCallback","host","g9Hosts","motionOff","setMotionOff","g9MotionScale","subscribeG9Motion","s","rootStyle","directionClass","G9CoreContext","G9DiagnosticsContext","G9SettingsContext","G9CultureContext","G9KeyboardContext","G9WindowContext","G9InsetsContext","view"],"mappings":"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"}
|
|
@@ -45,6 +45,11 @@ export interface G9SelectionSheetRequest<T extends G9SelectionItem> {
|
|
|
45
45
|
readonly resetSelection?: ReadonlyArray<T> | null;
|
|
46
46
|
/** Count pill label (multi). Default `"{n} {selected}"`. */
|
|
47
47
|
readonly countLabel?: (count: number) => string;
|
|
48
|
+
/**
|
|
49
|
+
* Multi: Done stays disabled until at least this many items are ticked (MAUI `minimumSelected`, G9MAUIControls 1.4.7) —
|
|
50
|
+
* the sheet never closes on an answer the caller would refuse and reopen. Default 0.
|
|
51
|
+
*/
|
|
52
|
+
readonly minimumSelected?: number;
|
|
48
53
|
/** Extra sheet options (merged over the defaults). */
|
|
49
54
|
readonly options?: G9SheetOptions;
|
|
50
55
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selection-sheet.d.ts","sourceRoot":"","sources":["../../src/selection/selection-sheet.tsx"],"names":[],"mappings":"AAAA,OAAO,iBAAiB,CAAC;AACzB,OAAO,EAA4B,KAAK,oBAAoB,EAAa,MAAM,+BAA+B,CAAC;AAI/G,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAQ9D,OAAO,EAAiE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACtH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAIvD,+GAA+G;AAC/G,eAAO,MAAM,8BAA8B,KAAK,CAAC;AACjD,mDAAmD;AACnD,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,4DAA4D;AAC5D,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAC/C,sEAAsE;AACtE,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAE3C,wEAAwE;AACxE,MAAM,WAAW,sBAAsB,CAAC,CAAC,SAAS,eAAe,GAAG,eAAe;IACjF,sFAAsF;IACtF,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,kDAAkD;IAClD,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;IACnC,kGAAkG;IAClG,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;CAClC;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,aAAa,CAAC,eAAe,CAAC,GAAG,MAAM,EAAE,CAYnF;AAED,mHAAmH;AACnH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAG,MAAM,CAGlF;AAED,2EAA2E;AAC3E,MAAM,WAAW,uBAAuB,CAAC,CAAC,SAAS,eAAe;IAChE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IACjC,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IAC1C,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;IACjC,2EAA2E;IAC3E,QAAQ,CAAC,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAC1C,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAC9B,uDAAuD;IACvD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,mDAAmD;IACnD,QAAQ,CAAC,iBAAiB,CAAC,EAAE,oBAAoB,CAAC;IAClD,oFAAoF;IACpF,QAAQ,CAAC,cAAc,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAClD,4DAA4D;IAC5D,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAChD,sDAAsD;IACtD,QAAQ,CAAC,OAAO,CAAC,EAAE,cAAc,CAAC;CACnC;AAqJD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,eAAe,EAC1D,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,uBAAuB,CAAC,CAAC,CAAC,GAClC,OAAO,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"selection-sheet.d.ts","sourceRoot":"","sources":["../../src/selection/selection-sheet.tsx"],"names":[],"mappings":"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"}
|
|
@@ -152,6 +152,7 @@ function showSelectionSheet(sheets, request) {
|
|
|
152
152
|
isEnabled: (item)=>false !== item.isEnabled
|
|
153
153
|
});
|
|
154
154
|
return new Promise((resolve)=>{
|
|
155
|
+
var _request_minimumSelected;
|
|
155
156
|
let draft = initial;
|
|
156
157
|
const draftListeners = new Set();
|
|
157
158
|
const draftStore = {
|
|
@@ -211,9 +212,12 @@ function showSelectionSheet(sheets, request) {
|
|
|
211
212
|
title: request.title,
|
|
212
213
|
headerTitlePlacement: 'nearBack',
|
|
213
214
|
...multiple || reset ? {
|
|
214
|
-
headerTrailingView: <SelectionHeaderActions multiple={multiple} draft={draftStore} {...request.countLabel ? {
|
|
215
|
+
headerTrailingView: <SelectionHeaderActions multiple={multiple} draft={draftStore} minimum={Math.max(0, null != (_request_minimumSelected = request.minimumSelected) ? _request_minimumSelected : 0)} {...request.countLabel ? {
|
|
215
216
|
countLabel: request.countLabel
|
|
216
|
-
} : {}} onDone={()=>
|
|
217
|
+
} : {}} onDone={()=>{
|
|
218
|
+
var _request_minimumSelected;
|
|
219
|
+
if (draft.selectedKeys.length >= Math.max(0, null != (_request_minimumSelected = request.minimumSelected) ? _request_minimumSelected : 0)) complete(draft.selectedKeys, true);
|
|
220
|
+
}} {...reset ? {
|
|
217
221
|
onReset: ()=>{
|
|
218
222
|
settle([], true, reset);
|
|
219
223
|
null == handle || handle.close();
|
|
@@ -242,7 +246,7 @@ function SelectionHeaderActions(props) {
|
|
|
242
246
|
{props.countLabel ? props.countLabel(n) : `${n} ${strings.get(G9StringKey.Selected)}`}
|
|
243
247
|
</text>
|
|
244
248
|
</view> : null}
|
|
245
|
-
{props.multiple ? <G9Button text={strings.get(G9StringKey.Done)} variant="text" size="small" onPress={props.onDone}/> : null}
|
|
249
|
+
{props.multiple ? <G9Button text={strings.get(G9StringKey.Done)} variant="text" size="small" disabled={n < props.minimum} onPress={props.onDone}/> : null}
|
|
246
250
|
</view>;
|
|
247
251
|
}
|
|
248
252
|
function showListSheet(sheets, request) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selection/selection-sheet.jsx","sources":["../../src/selection/selection-sheet.tsx"],"sourcesContent":["import './selection.css';\nimport { formatColor, G9StringKey, type G9TextInputDirection, withAlpha } from '@g9tm/lynxcontrols-foundation';\nimport { type ReactNode, useEffect, useRef, useState, useSyncExternalStore } from '@lynx-js/react';\nimport { g9ChildId, g9RowStyle, g9TextStart } from '../base/control';\nimport { useG9Press } from '../base/press';\nimport type { G9SelectionItem } from '../base/selection-item';\nimport { type G9TimerHandle, startTimer, stopTimer } from '../base/timers';\nimport { G9Button } from '../button/button';\nimport { G9SearchEntry } from '../fields/search-entry';\nimport { G9TextEntry } from '../fields/text-entry';\nimport { G9IconSlot, hasIconSlot } from '../icon/slot';\nimport { G9Swatch } from '../icon/swatch';\nimport { useG9Direction, useG9Settings, useG9Strings, useG9Theme } from '../provider/hooks';\nimport { fitToContentSheetOptions, fullScreenWithoutHandleSheetOptions, type G9SheetOptions } from '../sheet/options';\nimport type { G9SheetService } from '../sheet/service';\nimport type { G9SheetHandle } from '../sheet/stack';\nimport { createSelectionModel, type G9SelectionModel } from './model';\n\n/** MAUI `G9SelectionSheet.EagerBuildItemLimit`: lists up to 40 items are built at once; longer lists defer. */\nexport const g9SelectionEagerBuildItemLimit = 40;\n/** Row height of the selection sheet (MAUI 52). */\nexport const g9SelectionRowHeight = 52;\n/** Search debounce of the selection sheet (MAUI 150 ms). */\nexport const g9SelectionSearchDebounceMs = 150;\n/** Wait after a close-on-select pick before closing (MAUI 120 ms). */\nexport const g9SelectionCloseDelayMs = 120;\n\n/** The outcome of a selection sheet (MAUI `G9SelectionSheetResult`). */\nexport interface G9SelectionSheetResult<T extends G9SelectionItem = G9SelectionItem> {\n /** A pick on a close-on-select sheet, Done, or Reset; `false` for every dismissal. */\n readonly wasAccepted: boolean;\n /** The selection differs from the opening one. */\n readonly selectionChanged: boolean;\n /** The final selection (opening items that are not listed or disabled stay unless deselected). */\n readonly items: ReadonlyArray<T>;\n}\n\n/**\n * The selection identity of an item inside one list (MAUI `G9SelectionSheet.KeyOf`): `k:<key>` when the key is set,\n * else `t:<text>` when the text is unique among key-less items, else the position (`i:<n>`). Keys and texts never\n * match each other.\n */\nexport function selectionIdentities(items: ReadonlyArray<G9SelectionItem>): string[] {\n const counts = new Map<string, number>();\n for (const item of items) {\n if (item.key == null || item.key.trim() === '') counts.set(item.text, (counts.get(item.text) ?? 0) + 1);\n }\n return items.map((item, i) =>\n item.key != null && item.key.trim() !== ''\n ? `k:${item.key}`\n : (counts.get(item.text) ?? 0) > 1\n ? `i:${i}`\n : `t:${item.text}`,\n );\n}\n\n/** Estimated body height (MAUI `EstimateContentHeight`): rows × 52 + chrome, clamped to [2 rows + chrome, 520]. */\nexport function estimateSelectionHeight(count: number, showSearch: boolean): number {\n const chrome = (showSearch ? 64 : 0) + 18;\n return Math.min(520, Math.max(2 * g9SelectionRowHeight + chrome, count * g9SelectionRowHeight + chrome));\n}\n\n/** Arguments of {@link showSelectionSheet} (MAUI `ShowForResultAsync`). */\nexport interface G9SelectionSheetRequest<T extends G9SelectionItem> {\n readonly title: string;\n readonly items: ReadonlyArray<T>;\n readonly selectedItems?: ReadonlyArray<T>;\n readonly allowMultiple?: boolean;\n /** Single mode: a pick completes and closes after 120 ms. Default true. */\n readonly closeOnSingleSelection?: boolean;\n readonly showSearch?: boolean;\n /** No-results text. Default the `noResults` string. */\n readonly emptyText?: string | null;\n /** Row text direction. Default `'matchParent'`. */\n readonly itemTextDirection?: G9TextInputDirection;\n /** A non-empty reset selection adds a Reset toolbar item that completes with it. */\n readonly resetSelection?: ReadonlyArray<T> | null;\n /** Count pill label (multi). Default `\"{n} {selected}\"`. */\n readonly countLabel?: (count: number) => string;\n /** Extra sheet options (merged over the defaults). */\n readonly options?: G9SheetOptions;\n}\n\ninterface BodyProps<T extends G9SelectionItem> {\n readonly request: G9SelectionSheetRequest<T>;\n readonly ids: ReadonlyArray<string>;\n readonly initial: G9SelectionModel<T>;\n readonly onComplete: (keys: ReadonlyArray<string>, accepted: boolean) => void;\n readonly onDraft: (model: G9SelectionModel<T>) => void;\n readonly done: { current: boolean };\n}\n\nfunction SelectionRow(props: {\n readonly item: G9SelectionItem;\n /** Element id (`${sheet automationId}_Item_${key}`), so automation finds the row like a person sees it. */\n readonly elementId?: string | undefined;\n readonly selected: boolean;\n readonly direction: 'ltr' | 'rtl';\n readonly onTap: () => void;\n}): ReactNode {\n const palette = useG9Theme().palette;\n const glyphs = useG9Settings().glyphs;\n const { item, selected } = props;\n const press = useG9Press(props.onTap, { blocked: item.isEnabled === false, source: 'G9SelectionSheet', guardMs: 0 });\n const icon = { emoji: item.emoji, icon: item.icon, imagePath: item.iconPath, image: item.iconImage };\n const iconColor = item.iconTintColor ?? (selected ? palette.Primary : palette.TextSecondary);\n return (\n <view\n {...(props.elementId ? { id: props.elementId } : {})}\n className={`g9-selection-row${selected ? ' g9-selection-row--selected' : ''}`}\n style={selected ? { backgroundColor: formatColor(withAlpha(palette.Primary, 0.1)) } : {}}\n accessibility-element={true}\n accessibility-traits=\"button\"\n accessibility-label={item.text}\n {...(selected ? { 'accessibility-value': 'selected' } : {})}\n bindtap={press}\n >\n <view\n className={`g9-selection-row__inner g9-dir-${props.direction}`}\n style={{ ...g9RowStyle(props.direction), ...(item.isEnabled === false ? { opacity: 0.45 } : {}) }}\n >\n {hasIconSlot(icon) ? (\n <G9IconSlot {...icon} size={18} color={iconColor} />\n ) : item.swatchFirstColor?.trim() || item.swatchSecondColor?.trim() ? (\n <G9Swatch first={item.swatchFirstColor} second={item.swatchSecondColor} direction={props.direction} />\n ) : (\n // MAUI's row is a Grid of ColumnSpacing 12 whose icon column stays when empty: its spacing still sits before\n // the text, so an icon-less row's text starts 16 + 12 dp in, level with nothing but the same in every picker.\n <view className=\"g9-selection-row__icon-gap\" />\n )}\n <text\n className=\"g9-selection-row__text\"\n text-maxline=\"1\"\n style={{\n color: selected ? palette.Primary : palette.TextPrimary,\n fontWeight: selected ? 'bold' : 'normal',\n ...g9TextStart(props.direction),\n }}\n >\n {item.text}\n </text>\n <view style={{ opacity: selected ? 1 : 0 }}>\n <G9IconSlot icon={glyphs.check} size={18} color={palette.Primary} />\n </view>\n </view>\n </view>\n );\n}\n\nfunction SelectionBody<T extends G9SelectionItem>(props: BodyProps<T>): ReactNode {\n const strings = useG9Strings();\n const parentDir = useG9Direction();\n const { request, ids } = props;\n const [model, setModel] = useState(props.initial);\n const [query, setQuery] = useState('');\n const [applied, setApplied] = useState('');\n const timer = useRef<G9TimerHandle | null>(null);\n const picking = useRef(false);\n useEffect(\n () => () => {\n if (timer.current !== null) stopTimer(timer.current);\n },\n [],\n );\n const visible = request.showSearch ? model.withItems(request.items).filter(applied) : request.items;\n const visibleIds = new Set(visible.map((item) => ids[request.items.indexOf(item)]));\n const dir =\n request.itemTextDirection === 'leftToRight'\n ? 'ltr'\n : request.itemTextDirection === 'rightToLeft'\n ? 'rtl'\n : parentDir;\n const multiple = request.allowMultiple === true;\n const onQuery = (value: string) => {\n setQuery(value);\n if (timer.current !== null) stopTimer(timer.current);\n if (value.trim() === '') {\n setApplied(value);\n return;\n }\n timer.current = startTimer(() => setApplied(value), g9SelectionSearchDebounceMs);\n };\n const tap = (id: string) => {\n if (props.done.current || picking.current) return;\n const next = model.toggle(id);\n if (next === model) return;\n setModel(next);\n props.onDraft(next);\n if (!multiple && request.closeOnSingleSelection !== false) {\n picking.current = true;\n startTimer(() => props.onComplete(next.selectedKeys, true), g9SelectionCloseDelayMs);\n }\n };\n return (\n <view className=\"g9-selection\">\n {request.showSearch ? (\n <view className=\"g9-selection__search\">\n <G9TextEntry\n placeholder={strings.get(G9StringKey.Search)}\n leadingIcon=\"search\"\n value={query}\n onValueChange={onQuery}\n />\n </view>\n ) : null}\n {visible.length > 0 ? (\n <view className=\"g9-selection__items\">\n {request.items.map((item, i) => {\n const id = ids[i] as string;\n if (!visibleIds.has(id)) return null;\n return (\n <SelectionRow\n key={id}\n item={item}\n elementId={g9ChildId(request.options?.automationId, `Item_${item.key ?? i}`)}\n selected={model.isSelected(id)}\n direction={dir}\n onTap={() => tap(id)}\n />\n );\n })}\n </view>\n ) : null}\n {visible.length === 0 ? (\n <text className=\"g9-selection__empty\">{request.emptyText ?? strings.get(G9StringKey.NoResults)}</text>\n ) : null}\n </view>\n );\n}\n\n/**\n * Shows the selection sheet (MAUI `G9SelectionSheet.ShowForResultAsync`): a fit sheet with a header (title near the\n * close button), optional search (150 ms debounce, text match, hidden selected rows stay selected), rows with icon or\n * swatch, check mark and selection tint. Single mode with `closeOnSingleSelection` completes on a pick; multi mode\n * shows a count pill and Done; a reset selection adds Reset. Any dismissal resolves `wasAccepted: false` with the\n * current draft (or the opening selection when the sheet never built).\n */\nexport function showSelectionSheet<T extends G9SelectionItem>(\n sheets: G9SheetService,\n request: G9SelectionSheetRequest<T>,\n): Promise<G9SelectionSheetResult<T>> {\n const items = [...request.items];\n const ids = selectionIdentities(items);\n const opening = request.selectedItems ?? [];\n // Opening items match listed items by identity; the rest are kept as \"not listed\".\n const openingIds = selectionIdentities([...items, ...opening]).slice(items.length);\n const listedIds = new Set(ids);\n const initialKeys = openingIds.filter((id) => listedIds.has(id));\n const unlisted = opening.filter((_, i) => !listedIds.has(openingIds[i] as string));\n const multiple = request.allowMultiple === true;\n const initial = createSelectionModel<T>({\n items,\n getKey: (item) => ids[items.indexOf(item)] as string,\n getText: (item) => item.text,\n mode: multiple ? 'multiple' : 'single',\n selectedKeys: initialKeys,\n isEnabled: (item) => item.isEnabled !== false,\n });\n return new Promise((resolve) => {\n let draft = initial;\n const draftListeners = new Set<() => void>();\n const draftStore = {\n subscribe: (l: () => void) => {\n draftListeners.add(l);\n return () => {\n draftListeners.delete(l);\n };\n },\n count: () => draft.selectedKeys.length,\n };\n let settled = false;\n const done = { current: false };\n const startKeys = new Set(initialKeys);\n const collect = (keys: ReadonlyArray<string>): T[] => {\n const set = new Set(keys);\n return [...items.filter((_, i) => set.has(ids[i] as string)), ...unlisted];\n };\n const settle = (keys: ReadonlyArray<string>, accepted: boolean, explicit?: ReadonlyArray<T>) => {\n if (settled) return;\n settled = true;\n done.current = true;\n const result = explicit ?? collect(keys);\n const changed = explicit !== undefined || keys.length !== startKeys.size || keys.some((k) => !startKeys.has(k));\n resolve({ wasAccepted: accepted, selectionChanged: changed, items: result });\n };\n let handle: G9SheetHandle | null = null;\n const complete = (keys: ReadonlyArray<string>, accepted: boolean) => {\n settle(keys, accepted);\n handle?.close();\n };\n const reset = request.resetSelection && request.resetSelection.length > 0 ? request.resetSelection : null;\n const body = () => (\n <SelectionBody\n request={request}\n ids={ids}\n initial={initial}\n done={done}\n onDraft={(m) => {\n draft = m;\n for (const l of [...draftListeners]) l();\n }}\n onComplete={complete}\n />\n );\n const options: G9SheetOptions = {\n ...fitToContentSheetOptions(),\n deferContent: items.length > g9SelectionEagerBuildItemLimit,\n deferredLoadingPlaceholderHeight: estimateSelectionHeight(items.length, request.showSearch === true),\n loadingSkeleton: 'listRows',\n loadingSkeletonRowCount: items.length,\n backgroundColor: 'var(--g9-surface)',\n showToolbar: true,\n showCloseButton: true,\n title: request.title,\n headerTitlePlacement: 'nearBack',\n ...(multiple || reset\n ? {\n headerTrailingView: (\n <SelectionHeaderActions\n multiple={multiple}\n draft={draftStore}\n {...(request.countLabel ? { countLabel: request.countLabel } : {})}\n onDone={() => complete(draft.selectedKeys, true)}\n {...(reset\n ? {\n onReset: () => {\n settle([], true, reset);\n handle?.close();\n },\n }\n : {})}\n />\n ),\n }\n : {}),\n ...request.options,\n onClosed: () => {\n request.options?.onClosed?.();\n settle(draft.selectedKeys, false);\n },\n };\n handle = sheets.show(items.length > g9SelectionEagerBuildItemLimit ? body : body(), options);\n if (!handle) settle(initialKeys, false);\n });\n}\n\nfunction SelectionHeaderActions(props: {\n readonly multiple: boolean;\n readonly draft: { subscribe(listener: () => void): () => void; count(): number };\n readonly countLabel?: (n: number) => string;\n readonly onDone: () => void;\n readonly onReset?: () => void;\n}): ReactNode {\n const strings = useG9Strings();\n const direction = useG9Direction();\n const n = useSyncExternalStore(props.draft.subscribe, props.draft.count);\n return (\n <view className=\"g9-selection__actions\" style={g9RowStyle(direction)}>\n {props.onReset ? (\n <G9Button text={strings.get(G9StringKey.Reset)} variant=\"text\" size=\"small\" onPress={props.onReset} />\n ) : null}\n {props.multiple && n > 0 ? (\n <view className=\"g9-selection__count\">\n <text className=\"g9-selection__count-text\">\n {props.countLabel ? props.countLabel(n) : `${n} ${strings.get(G9StringKey.Selected)}`}\n </text>\n </view>\n ) : null}\n {props.multiple ? (\n <G9Button text={strings.get(G9StringKey.Done)} variant=\"text\" size=\"small\" onPress={props.onDone} />\n ) : null}\n </view>\n );\n}\n\n/** Arguments of {@link showListSheet} (MAUI `ShowListG9BottomSheetAsync`). */\nexport interface G9ListSheetRequest<T extends G9SelectionItem> {\n readonly title: string;\n readonly items: ReadonlyArray<T>;\n readonly selected?: ReadonlyArray<T>;\n readonly multiple?: boolean;\n /** Default true. */\n readonly closeOnSingleSelection?: boolean;\n /** Shows the search field (250 ms debounce, trimmed) with this placeholder. */\n readonly searchPlaceholder?: string | null;\n /** Default full screen without a handle (MAUI). A fit-to-content `sizeMode` keeps the sheet compact. */\n readonly options?: G9SheetOptions;\n /** Apply button text (multi). Default the `save` string. */\n readonly applyButtonText?: string;\n}\n\n/**\n * The shared list picker (MAUI `ShowListG9BottomSheetAsync` / `G9BottomSheetListPickerModal`): search field with\n * placeholder, rows with a selection tint (no check mark), Apply in multi mode, close-on-select after 120 ms. Resolves\n * with the selection when it completes or closes (no accepted/dismissed distinction, as MAUI); `[]` when throttled.\n */\nexport function showListSheet<T extends G9SelectionItem>(\n sheets: G9SheetService,\n request: G9ListSheetRequest<T>,\n): Promise<T[]> {\n const items = [...request.items];\n const keyOf = (item: G9SelectionItem) => (item.key != null && item.key.trim() !== '' ? item.key : item.text);\n const multiple = request.multiple === true;\n const initial = createSelectionModel<T>({\n items,\n getKey: keyOf,\n getText: (item) => item.text,\n mode: multiple ? 'multiple' : 'single',\n selectedKeys: (request.selected ?? []).map(keyOf),\n isEnabled: (item) => item.isEnabled !== false,\n });\n return new Promise((resolve) => {\n let draft = initial;\n let settled = false;\n let handle: G9SheetHandle | null = null;\n const settle = () => {\n if (settled) return;\n settled = true;\n resolve(draft.selectedItems());\n };\n const complete = () => {\n settle();\n handle?.close();\n };\n const body = () => (\n <ListPickerBody\n request={request}\n initial={initial}\n keyOf={keyOf}\n onDraft={(m) => {\n draft = m;\n }}\n onComplete={complete}\n />\n );\n const base = request.options ?? fullScreenWithoutHandleSheetOptions();\n const fit = base.sizeMode === 'fitToContent';\n const options: G9SheetOptions = {\n ...base,\n ...(fit\n ? {}\n : {\n states: ['large'],\n currentState: 'large',\n hasHandle: false,\n isDraggable: true,\n useFullScreenLoadingPlaceholder: true,\n }),\n showToolbar: true,\n showCloseButton: true,\n title: request.title,\n headerTitlePlacement: 'nearBack',\n deferContent: true,\n onClosed: () => {\n base.onClosed?.();\n settle();\n },\n };\n handle = sheets.show(body, options);\n if (!handle) {\n settled = true;\n resolve([]);\n }\n });\n}\n\nfunction ListPickerBody<T extends G9SelectionItem>(props: {\n readonly request: G9ListSheetRequest<T>;\n readonly initial: G9SelectionModel<T>;\n readonly keyOf: (item: G9SelectionItem) => string;\n readonly onDraft: (model: G9SelectionModel<T>) => void;\n readonly onComplete: () => void;\n}): ReactNode {\n const strings = useG9Strings();\n const palette = useG9Theme().palette;\n const [model, setModel] = useState(props.initial);\n const [query, setQuery] = useState('');\n const picking = useRef(false);\n const { request } = props;\n const visible = model.filter(query, { trim: true });\n const tap = (item: T) => {\n if (picking.current || item.isEnabled === false) return;\n const next = model.toggle(props.keyOf(item));\n setModel(next);\n props.onDraft(next);\n if (!request.multiple && request.closeOnSingleSelection !== false) {\n picking.current = true;\n startTimer(props.onComplete, g9SelectionCloseDelayMs);\n }\n };\n return (\n <view className=\"g9-list-picker\">\n {request.searchPlaceholder ? (\n <view className=\"g9-selection__search\">\n <G9SearchEntry\n placeholder={request.searchPlaceholder}\n voiceEnabled={false}\n onDebouncedValueChange={setQuery}\n />\n </view>\n ) : null}\n {visible.map((item) => (\n <ListPickerRow\n key={props.keyOf(item)}\n item={item}\n selected={model.isSelected(props.keyOf(item))}\n tint={palette.PrimaryContainer}\n onTap={() => tap(item)}\n />\n ))}\n {request.multiple ? (\n <view className=\"g9-list-picker__apply\">\n <G9Button\n text={request.applyButtonText ?? strings.get(G9StringKey.Save)}\n size=\"hero\"\n onPress={props.onComplete}\n />\n </view>\n ) : null}\n </view>\n );\n}\n\nfunction ListPickerRow(props: {\n readonly item: G9SelectionItem;\n readonly selected: boolean;\n readonly tint: string;\n readonly onTap: () => void;\n}): ReactNode {\n const press = useG9Press(props.onTap, {\n source: 'G9ListPicker',\n guardMs: 0,\n blocked: props.item.isEnabled === false,\n });\n const icon = { icon: props.item.icon, imagePath: props.item.iconPath, image: props.item.iconImage };\n const direction = useG9Direction();\n return (\n <view\n className={`g9-list-picker__row${props.selected ? ' g9-list-picker__row--selected' : ''}`}\n style={{ ...g9RowStyle(direction), ...(props.selected ? { backgroundColor: props.tint } : {}) }}\n accessibility-element={true}\n accessibility-traits=\"button\"\n accessibility-label={props.item.text}\n bindtap={press}\n >\n {hasIconSlot(icon) ? <G9IconSlot {...icon} size={20} color=\"var(--g9-on-surface-variant)\" /> : null}\n <text className=\"g9-list-picker__text\" text-maxline=\"1\" style={g9TextStart(direction)}>\n {props.item.text}\n </text>\n </view>\n );\n}\n"],"names":["g9SelectionEagerBuildItemLimit","g9SelectionRowHeight","g9SelectionSearchDebounceMs","g9SelectionCloseDelayMs","selectionIdentities","items","counts","Map","item","_counts_get","i","estimateSelectionHeight","count","showSearch","chrome","Math","SelectionRow","props","_item_iconTintColor","_item_swatchFirstColor","_item_swatchSecondColor","palette","useG9Theme","glyphs","useG9Settings","selected","press","useG9Press","icon","iconColor","view","formatColor","withAlpha","g9RowStyle","hasIconSlot","G9IconSlot","G9Swatch","text","g9TextStart","SelectionBody","_request_emptyText","strings","useG9Strings","parentDir","useG9Direction","request","ids","model","setModel","useState","query","setQuery","applied","setApplied","timer","useRef","picking","useEffect","stopTimer","visible","visibleIds","Set","dir","multiple","onQuery","value","startTimer","tap","id","next","G9TextEntry","G9StringKey","_item_key","_request_options","g9ChildId","showSelectionSheet","sheets","_request_selectedItems","opening","openingIds","listedIds","initialKeys","unlisted","_","initial","createSelectionModel","Promise","resolve","draft","draftListeners","draftStore","l","settled","done","startKeys","collect","keys","set","settle","accepted","explicit","result","changed","undefined","k","handle","complete","reset","body","m","options","fitToContentSheetOptions","SelectionHeaderActions","direction","n","useSyncExternalStore","G9Button","showListSheet","_request_selected","keyOf","ListPickerBody","base","fullScreenWithoutHandleSheetOptions","fit","_request_applyButtonText","G9SearchEntry","ListPickerRow"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"selection/selection-sheet.jsx","sources":["../../src/selection/selection-sheet.tsx"],"sourcesContent":["import './selection.css';\nimport { formatColor, G9StringKey, type G9TextInputDirection, withAlpha } from '@g9tm/lynxcontrols-foundation';\nimport { type ReactNode, useEffect, useRef, useState, useSyncExternalStore } from '@lynx-js/react';\nimport { g9ChildId, g9RowStyle, g9TextStart } from '../base/control';\nimport { useG9Press } from '../base/press';\nimport type { G9SelectionItem } from '../base/selection-item';\nimport { type G9TimerHandle, startTimer, stopTimer } from '../base/timers';\nimport { G9Button } from '../button/button';\nimport { G9SearchEntry } from '../fields/search-entry';\nimport { G9TextEntry } from '../fields/text-entry';\nimport { G9IconSlot, hasIconSlot } from '../icon/slot';\nimport { G9Swatch } from '../icon/swatch';\nimport { useG9Direction, useG9Settings, useG9Strings, useG9Theme } from '../provider/hooks';\nimport { fitToContentSheetOptions, fullScreenWithoutHandleSheetOptions, type G9SheetOptions } from '../sheet/options';\nimport type { G9SheetService } from '../sheet/service';\nimport type { G9SheetHandle } from '../sheet/stack';\nimport { createSelectionModel, type G9SelectionModel } from './model';\n\n/** MAUI `G9SelectionSheet.EagerBuildItemLimit`: lists up to 40 items are built at once; longer lists defer. */\nexport const g9SelectionEagerBuildItemLimit = 40;\n/** Row height of the selection sheet (MAUI 52). */\nexport const g9SelectionRowHeight = 52;\n/** Search debounce of the selection sheet (MAUI 150 ms). */\nexport const g9SelectionSearchDebounceMs = 150;\n/** Wait after a close-on-select pick before closing (MAUI 120 ms). */\nexport const g9SelectionCloseDelayMs = 120;\n\n/** The outcome of a selection sheet (MAUI `G9SelectionSheetResult`). */\nexport interface G9SelectionSheetResult<T extends G9SelectionItem = G9SelectionItem> {\n /** A pick on a close-on-select sheet, Done, or Reset; `false` for every dismissal. */\n readonly wasAccepted: boolean;\n /** The selection differs from the opening one. */\n readonly selectionChanged: boolean;\n /** The final selection (opening items that are not listed or disabled stay unless deselected). */\n readonly items: ReadonlyArray<T>;\n}\n\n/**\n * The selection identity of an item inside one list (MAUI `G9SelectionSheet.KeyOf`): `k:<key>` when the key is set,\n * else `t:<text>` when the text is unique among key-less items, else the position (`i:<n>`). Keys and texts never\n * match each other.\n */\nexport function selectionIdentities(items: ReadonlyArray<G9SelectionItem>): string[] {\n const counts = new Map<string, number>();\n for (const item of items) {\n if (item.key == null || item.key.trim() === '') counts.set(item.text, (counts.get(item.text) ?? 0) + 1);\n }\n return items.map((item, i) =>\n item.key != null && item.key.trim() !== ''\n ? `k:${item.key}`\n : (counts.get(item.text) ?? 0) > 1\n ? `i:${i}`\n : `t:${item.text}`,\n );\n}\n\n/** Estimated body height (MAUI `EstimateContentHeight`): rows × 52 + chrome, clamped to [2 rows + chrome, 520]. */\nexport function estimateSelectionHeight(count: number, showSearch: boolean): number {\n const chrome = (showSearch ? 64 : 0) + 18;\n return Math.min(520, Math.max(2 * g9SelectionRowHeight + chrome, count * g9SelectionRowHeight + chrome));\n}\n\n/** Arguments of {@link showSelectionSheet} (MAUI `ShowForResultAsync`). */\nexport interface G9SelectionSheetRequest<T extends G9SelectionItem> {\n readonly title: string;\n readonly items: ReadonlyArray<T>;\n readonly selectedItems?: ReadonlyArray<T>;\n readonly allowMultiple?: boolean;\n /** Single mode: a pick completes and closes after 120 ms. Default true. */\n readonly closeOnSingleSelection?: boolean;\n readonly showSearch?: boolean;\n /** No-results text. Default the `noResults` string. */\n readonly emptyText?: string | null;\n /** Row text direction. Default `'matchParent'`. */\n readonly itemTextDirection?: G9TextInputDirection;\n /** A non-empty reset selection adds a Reset toolbar item that completes with it. */\n readonly resetSelection?: ReadonlyArray<T> | null;\n /** Count pill label (multi). Default `\"{n} {selected}\"`. */\n readonly countLabel?: (count: number) => string;\n /**\n * Multi: Done stays disabled until at least this many items are ticked (MAUI `minimumSelected`, G9MAUIControls 1.4.7) —\n * the sheet never closes on an answer the caller would refuse and reopen. Default 0.\n */\n readonly minimumSelected?: number;\n /** Extra sheet options (merged over the defaults). */\n readonly options?: G9SheetOptions;\n}\n\ninterface BodyProps<T extends G9SelectionItem> {\n readonly request: G9SelectionSheetRequest<T>;\n readonly ids: ReadonlyArray<string>;\n readonly initial: G9SelectionModel<T>;\n readonly onComplete: (keys: ReadonlyArray<string>, accepted: boolean) => void;\n readonly onDraft: (model: G9SelectionModel<T>) => void;\n readonly done: { current: boolean };\n}\n\nfunction SelectionRow(props: {\n readonly item: G9SelectionItem;\n /** Element id (`${sheet automationId}_Item_${key}`), so automation finds the row like a person sees it. */\n readonly elementId?: string | undefined;\n readonly selected: boolean;\n readonly direction: 'ltr' | 'rtl';\n readonly onTap: () => void;\n}): ReactNode {\n const palette = useG9Theme().palette;\n const glyphs = useG9Settings().glyphs;\n const { item, selected } = props;\n const press = useG9Press(props.onTap, { blocked: item.isEnabled === false, source: 'G9SelectionSheet', guardMs: 0 });\n const icon = { emoji: item.emoji, icon: item.icon, imagePath: item.iconPath, image: item.iconImage };\n const iconColor = item.iconTintColor ?? (selected ? palette.Primary : palette.TextSecondary);\n return (\n <view\n {...(props.elementId ? { id: props.elementId } : {})}\n className={`g9-selection-row${selected ? ' g9-selection-row--selected' : ''}`}\n style={selected ? { backgroundColor: formatColor(withAlpha(palette.Primary, 0.1)) } : {}}\n accessibility-element={true}\n accessibility-traits=\"button\"\n accessibility-label={item.text}\n {...(selected ? { 'accessibility-value': 'selected' } : {})}\n bindtap={press}\n >\n <view\n className={`g9-selection-row__inner g9-dir-${props.direction}`}\n style={{ ...g9RowStyle(props.direction), ...(item.isEnabled === false ? { opacity: 0.45 } : {}) }}\n >\n {hasIconSlot(icon) ? (\n <G9IconSlot {...icon} size={18} color={iconColor} />\n ) : item.swatchFirstColor?.trim() || item.swatchSecondColor?.trim() ? (\n <G9Swatch first={item.swatchFirstColor} second={item.swatchSecondColor} direction={props.direction} />\n ) : (\n // MAUI's row is a Grid of ColumnSpacing 12 whose icon column stays when empty: its spacing still sits before\n // the text, so an icon-less row's text starts 16 + 12 dp in, level with nothing but the same in every picker.\n <view className=\"g9-selection-row__icon-gap\" />\n )}\n <text\n className=\"g9-selection-row__text\"\n text-maxline=\"1\"\n style={{\n color: selected ? palette.Primary : palette.TextPrimary,\n fontWeight: selected ? 'bold' : 'normal',\n ...g9TextStart(props.direction),\n }}\n >\n {item.text}\n </text>\n <view style={{ opacity: selected ? 1 : 0 }}>\n <G9IconSlot icon={glyphs.check} size={18} color={palette.Primary} />\n </view>\n </view>\n </view>\n );\n}\n\nfunction SelectionBody<T extends G9SelectionItem>(props: BodyProps<T>): ReactNode {\n const strings = useG9Strings();\n const parentDir = useG9Direction();\n const { request, ids } = props;\n const [model, setModel] = useState(props.initial);\n const [query, setQuery] = useState('');\n const [applied, setApplied] = useState('');\n const timer = useRef<G9TimerHandle | null>(null);\n const picking = useRef(false);\n useEffect(\n () => () => {\n if (timer.current !== null) stopTimer(timer.current);\n },\n [],\n );\n const visible = request.showSearch ? model.withItems(request.items).filter(applied) : request.items;\n const visibleIds = new Set(visible.map((item) => ids[request.items.indexOf(item)]));\n const dir =\n request.itemTextDirection === 'leftToRight'\n ? 'ltr'\n : request.itemTextDirection === 'rightToLeft'\n ? 'rtl'\n : parentDir;\n const multiple = request.allowMultiple === true;\n const onQuery = (value: string) => {\n setQuery(value);\n if (timer.current !== null) stopTimer(timer.current);\n if (value.trim() === '') {\n setApplied(value);\n return;\n }\n timer.current = startTimer(() => setApplied(value), g9SelectionSearchDebounceMs);\n };\n const tap = (id: string) => {\n if (props.done.current || picking.current) return;\n const next = model.toggle(id);\n if (next === model) return;\n setModel(next);\n props.onDraft(next);\n if (!multiple && request.closeOnSingleSelection !== false) {\n picking.current = true;\n startTimer(() => props.onComplete(next.selectedKeys, true), g9SelectionCloseDelayMs);\n }\n };\n return (\n <view className=\"g9-selection\">\n {request.showSearch ? (\n <view className=\"g9-selection__search\">\n <G9TextEntry\n placeholder={strings.get(G9StringKey.Search)}\n leadingIcon=\"search\"\n value={query}\n onValueChange={onQuery}\n />\n </view>\n ) : null}\n {visible.length > 0 ? (\n <view className=\"g9-selection__items\">\n {request.items.map((item, i) => {\n const id = ids[i] as string;\n if (!visibleIds.has(id)) return null;\n return (\n <SelectionRow\n key={id}\n item={item}\n elementId={g9ChildId(request.options?.automationId, `Item_${item.key ?? i}`)}\n selected={model.isSelected(id)}\n direction={dir}\n onTap={() => tap(id)}\n />\n );\n })}\n </view>\n ) : null}\n {visible.length === 0 ? (\n <text className=\"g9-selection__empty\">{request.emptyText ?? strings.get(G9StringKey.NoResults)}</text>\n ) : null}\n </view>\n );\n}\n\n/**\n * Shows the selection sheet (MAUI `G9SelectionSheet.ShowForResultAsync`): a fit sheet with a header (title near the\n * close button), optional search (150 ms debounce, text match, hidden selected rows stay selected), rows with icon or\n * swatch, check mark and selection tint. Single mode with `closeOnSingleSelection` completes on a pick; multi mode\n * shows a count pill and Done; a reset selection adds Reset. Any dismissal resolves `wasAccepted: false` with the\n * current draft (or the opening selection when the sheet never built).\n */\nexport function showSelectionSheet<T extends G9SelectionItem>(\n sheets: G9SheetService,\n request: G9SelectionSheetRequest<T>,\n): Promise<G9SelectionSheetResult<T>> {\n const items = [...request.items];\n const ids = selectionIdentities(items);\n const opening = request.selectedItems ?? [];\n // Opening items match listed items by identity; the rest are kept as \"not listed\".\n const openingIds = selectionIdentities([...items, ...opening]).slice(items.length);\n const listedIds = new Set(ids);\n const initialKeys = openingIds.filter((id) => listedIds.has(id));\n const unlisted = opening.filter((_, i) => !listedIds.has(openingIds[i] as string));\n const multiple = request.allowMultiple === true;\n const initial = createSelectionModel<T>({\n items,\n getKey: (item) => ids[items.indexOf(item)] as string,\n getText: (item) => item.text,\n mode: multiple ? 'multiple' : 'single',\n selectedKeys: initialKeys,\n isEnabled: (item) => item.isEnabled !== false,\n });\n return new Promise((resolve) => {\n let draft = initial;\n const draftListeners = new Set<() => void>();\n const draftStore = {\n subscribe: (l: () => void) => {\n draftListeners.add(l);\n return () => {\n draftListeners.delete(l);\n };\n },\n count: () => draft.selectedKeys.length,\n };\n let settled = false;\n const done = { current: false };\n const startKeys = new Set(initialKeys);\n const collect = (keys: ReadonlyArray<string>): T[] => {\n const set = new Set(keys);\n return [...items.filter((_, i) => set.has(ids[i] as string)), ...unlisted];\n };\n const settle = (keys: ReadonlyArray<string>, accepted: boolean, explicit?: ReadonlyArray<T>) => {\n if (settled) return;\n settled = true;\n done.current = true;\n const result = explicit ?? collect(keys);\n const changed = explicit !== undefined || keys.length !== startKeys.size || keys.some((k) => !startKeys.has(k));\n resolve({ wasAccepted: accepted, selectionChanged: changed, items: result });\n };\n let handle: G9SheetHandle | null = null;\n const complete = (keys: ReadonlyArray<string>, accepted: boolean) => {\n settle(keys, accepted);\n handle?.close();\n };\n const reset = request.resetSelection && request.resetSelection.length > 0 ? request.resetSelection : null;\n const body = () => (\n <SelectionBody\n request={request}\n ids={ids}\n initial={initial}\n done={done}\n onDraft={(m) => {\n draft = m;\n for (const l of [...draftListeners]) l();\n }}\n onComplete={complete}\n />\n );\n const options: G9SheetOptions = {\n ...fitToContentSheetOptions(),\n deferContent: items.length > g9SelectionEagerBuildItemLimit,\n deferredLoadingPlaceholderHeight: estimateSelectionHeight(items.length, request.showSearch === true),\n loadingSkeleton: 'listRows',\n loadingSkeletonRowCount: items.length,\n backgroundColor: 'var(--g9-surface)',\n showToolbar: true,\n showCloseButton: true,\n title: request.title,\n headerTitlePlacement: 'nearBack',\n ...(multiple || reset\n ? {\n headerTrailingView: (\n <SelectionHeaderActions\n multiple={multiple}\n draft={draftStore}\n minimum={Math.max(0, request.minimumSelected ?? 0)}\n {...(request.countLabel ? { countLabel: request.countLabel } : {})}\n onDone={() => {\n if (draft.selectedKeys.length >= Math.max(0, request.minimumSelected ?? 0))\n complete(draft.selectedKeys, true);\n }}\n {...(reset\n ? {\n onReset: () => {\n settle([], true, reset);\n handle?.close();\n },\n }\n : {})}\n />\n ),\n }\n : {}),\n ...request.options,\n onClosed: () => {\n request.options?.onClosed?.();\n settle(draft.selectedKeys, false);\n },\n };\n handle = sheets.show(items.length > g9SelectionEagerBuildItemLimit ? body : body(), options);\n if (!handle) settle(initialKeys, false);\n });\n}\n\nfunction SelectionHeaderActions(props: {\n readonly multiple: boolean;\n readonly draft: { subscribe(listener: () => void): () => void; count(): number };\n readonly minimum: number;\n readonly countLabel?: (n: number) => string;\n readonly onDone: () => void;\n readonly onReset?: () => void;\n}): ReactNode {\n const strings = useG9Strings();\n const direction = useG9Direction();\n const n = useSyncExternalStore(props.draft.subscribe, props.draft.count);\n return (\n <view className=\"g9-selection__actions\" style={g9RowStyle(direction)}>\n {props.onReset ? (\n <G9Button text={strings.get(G9StringKey.Reset)} variant=\"text\" size=\"small\" onPress={props.onReset} />\n ) : null}\n {props.multiple && n > 0 ? (\n <view className=\"g9-selection__count\">\n <text className=\"g9-selection__count-text\">\n {props.countLabel ? props.countLabel(n) : `${n} ${strings.get(G9StringKey.Selected)}`}\n </text>\n </view>\n ) : null}\n {props.multiple ? (\n <G9Button\n text={strings.get(G9StringKey.Done)}\n variant=\"text\"\n size=\"small\"\n disabled={n < props.minimum}\n onPress={props.onDone}\n />\n ) : null}\n </view>\n );\n}\n\n/** Arguments of {@link showListSheet} (MAUI `ShowListG9BottomSheetAsync`). */\nexport interface G9ListSheetRequest<T extends G9SelectionItem> {\n readonly title: string;\n readonly items: ReadonlyArray<T>;\n readonly selected?: ReadonlyArray<T>;\n readonly multiple?: boolean;\n /** Default true. */\n readonly closeOnSingleSelection?: boolean;\n /** Shows the search field (250 ms debounce, trimmed) with this placeholder. */\n readonly searchPlaceholder?: string | null;\n /** Default full screen without a handle (MAUI). A fit-to-content `sizeMode` keeps the sheet compact. */\n readonly options?: G9SheetOptions;\n /** Apply button text (multi). Default the `save` string. */\n readonly applyButtonText?: string;\n}\n\n/**\n * The shared list picker (MAUI `ShowListG9BottomSheetAsync` / `G9BottomSheetListPickerModal`): search field with\n * placeholder, rows with a selection tint (no check mark), Apply in multi mode, close-on-select after 120 ms. Resolves\n * with the selection when it completes or closes (no accepted/dismissed distinction, as MAUI); `[]` when throttled.\n */\nexport function showListSheet<T extends G9SelectionItem>(\n sheets: G9SheetService,\n request: G9ListSheetRequest<T>,\n): Promise<T[]> {\n const items = [...request.items];\n const keyOf = (item: G9SelectionItem) => (item.key != null && item.key.trim() !== '' ? item.key : item.text);\n const multiple = request.multiple === true;\n const initial = createSelectionModel<T>({\n items,\n getKey: keyOf,\n getText: (item) => item.text,\n mode: multiple ? 'multiple' : 'single',\n selectedKeys: (request.selected ?? []).map(keyOf),\n isEnabled: (item) => item.isEnabled !== false,\n });\n return new Promise((resolve) => {\n let draft = initial;\n let settled = false;\n let handle: G9SheetHandle | null = null;\n const settle = () => {\n if (settled) return;\n settled = true;\n resolve(draft.selectedItems());\n };\n const complete = () => {\n settle();\n handle?.close();\n };\n const body = () => (\n <ListPickerBody\n request={request}\n initial={initial}\n keyOf={keyOf}\n onDraft={(m) => {\n draft = m;\n }}\n onComplete={complete}\n />\n );\n const base = request.options ?? fullScreenWithoutHandleSheetOptions();\n const fit = base.sizeMode === 'fitToContent';\n const options: G9SheetOptions = {\n ...base,\n ...(fit\n ? {}\n : {\n states: ['large'],\n currentState: 'large',\n hasHandle: false,\n isDraggable: true,\n useFullScreenLoadingPlaceholder: true,\n }),\n showToolbar: true,\n showCloseButton: true,\n title: request.title,\n headerTitlePlacement: 'nearBack',\n deferContent: true,\n onClosed: () => {\n base.onClosed?.();\n settle();\n },\n };\n handle = sheets.show(body, options);\n if (!handle) {\n settled = true;\n resolve([]);\n }\n });\n}\n\nfunction ListPickerBody<T extends G9SelectionItem>(props: {\n readonly request: G9ListSheetRequest<T>;\n readonly initial: G9SelectionModel<T>;\n readonly keyOf: (item: G9SelectionItem) => string;\n readonly onDraft: (model: G9SelectionModel<T>) => void;\n readonly onComplete: () => void;\n}): ReactNode {\n const strings = useG9Strings();\n const palette = useG9Theme().palette;\n const [model, setModel] = useState(props.initial);\n const [query, setQuery] = useState('');\n const picking = useRef(false);\n const { request } = props;\n const visible = model.filter(query, { trim: true });\n const tap = (item: T) => {\n if (picking.current || item.isEnabled === false) return;\n const next = model.toggle(props.keyOf(item));\n setModel(next);\n props.onDraft(next);\n if (!request.multiple && request.closeOnSingleSelection !== false) {\n picking.current = true;\n startTimer(props.onComplete, g9SelectionCloseDelayMs);\n }\n };\n return (\n <view className=\"g9-list-picker\">\n {request.searchPlaceholder ? (\n <view className=\"g9-selection__search\">\n <G9SearchEntry\n placeholder={request.searchPlaceholder}\n voiceEnabled={false}\n onDebouncedValueChange={setQuery}\n />\n </view>\n ) : null}\n {visible.map((item) => (\n <ListPickerRow\n key={props.keyOf(item)}\n item={item}\n selected={model.isSelected(props.keyOf(item))}\n tint={palette.PrimaryContainer}\n onTap={() => tap(item)}\n />\n ))}\n {request.multiple ? (\n <view className=\"g9-list-picker__apply\">\n <G9Button\n text={request.applyButtonText ?? strings.get(G9StringKey.Save)}\n size=\"hero\"\n onPress={props.onComplete}\n />\n </view>\n ) : null}\n </view>\n );\n}\n\nfunction ListPickerRow(props: {\n readonly item: G9SelectionItem;\n readonly selected: boolean;\n readonly tint: string;\n readonly onTap: () => void;\n}): ReactNode {\n const press = useG9Press(props.onTap, {\n source: 'G9ListPicker',\n guardMs: 0,\n blocked: props.item.isEnabled === false,\n });\n const icon = { icon: props.item.icon, imagePath: props.item.iconPath, image: props.item.iconImage };\n const direction = useG9Direction();\n return (\n <view\n className={`g9-list-picker__row${props.selected ? ' g9-list-picker__row--selected' : ''}`}\n style={{ ...g9RowStyle(direction), ...(props.selected ? { backgroundColor: props.tint } : {}) }}\n accessibility-element={true}\n accessibility-traits=\"button\"\n accessibility-label={props.item.text}\n bindtap={press}\n >\n {hasIconSlot(icon) ? <G9IconSlot {...icon} size={20} color=\"var(--g9-on-surface-variant)\" /> : null}\n <text className=\"g9-list-picker__text\" text-maxline=\"1\" style={g9TextStart(direction)}>\n {props.item.text}\n </text>\n </view>\n );\n}\n"],"names":["g9SelectionEagerBuildItemLimit","g9SelectionRowHeight","g9SelectionSearchDebounceMs","g9SelectionCloseDelayMs","selectionIdentities","items","counts","Map","item","_counts_get","i","estimateSelectionHeight","count","showSearch","chrome","Math","SelectionRow","props","_item_iconTintColor","_item_swatchFirstColor","_item_swatchSecondColor","palette","useG9Theme","glyphs","useG9Settings","selected","press","useG9Press","icon","iconColor","view","formatColor","withAlpha","g9RowStyle","hasIconSlot","G9IconSlot","G9Swatch","text","g9TextStart","SelectionBody","_request_emptyText","strings","useG9Strings","parentDir","useG9Direction","request","ids","model","setModel","useState","query","setQuery","applied","setApplied","timer","useRef","picking","useEffect","stopTimer","visible","visibleIds","Set","dir","multiple","onQuery","value","startTimer","tap","id","next","G9TextEntry","G9StringKey","_item_key","_request_options","g9ChildId","showSelectionSheet","sheets","_request_selectedItems","opening","openingIds","listedIds","initialKeys","unlisted","_","initial","createSelectionModel","Promise","resolve","_request_minimumSelected","draft","draftListeners","draftStore","l","settled","done","startKeys","collect","keys","set","settle","accepted","explicit","result","changed","undefined","k","handle","complete","reset","body","m","options","fitToContentSheetOptions","SelectionHeaderActions","direction","n","useSyncExternalStore","G9Button","showListSheet","_request_selected","keyOf","ListPickerBody","base","fullScreenWithoutHandleSheetOptions","fit","_request_applyButtonText","G9SearchEntry","ListPickerRow"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frame.d.ts","sourceRoot":"","sources":["../../src/sheet/frame.tsx"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAC;AACrB,OAAO,EAIL,KAAK,aAAa,
|
|
1
|
+
{"version":3,"file":"frame.d.ts","sourceRoot":"","sources":["../../src/sheet/frame.tsx"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAC;AACrB,OAAO,EAIL,KAAK,aAAa,EAKnB,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EAAiB,KAAK,SAAS,EAA2C,MAAM,gBAAgB,CAAC;AASxG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEvD,OAAO,KAAK,EAAiB,eAAe,EAAE,MAAM,WAAW,CAAC;AAehE,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEzE,qGAAqG;AACrG,MAAM,WAAW,mBAAmB;IAClC,iEAAiE;IACjE,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,oEAAoE;IACpE,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,oCAAoC;IACpC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,4BAA4B;IAC5B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,yDAAyD;IACzD,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC;AAID,kDAAkD;AAClD,wBAAgB,iBAAiB,IAAI,mBAAmB,GAAG,IAAI,CAE9D;AAED,qCAAqC;AACrC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,wEAAwE;IACxE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,kGAAkG;IAClG,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC;IAC7C,QAAQ,CAAC,IAAI,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACxC,mCAAmC;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,gEAAgE;IAChE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,qFAAqF;IACrF,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C;AA4BD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,SAAS,CA6WhE"}
|
package/dist/sheet/frame.jsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "./sheet.css";
|
|
2
|
-
import { createAbortController, formatColor, resolveThemeColor, tryParseColor, withAlpha } from "@g9tm/lynxcontrols-foundation";
|
|
2
|
+
import { createAbortController, formatColor, g9Ms, resolveThemeColor, tryParseColor, withAlpha } from "@g9tm/lynxcontrols-foundation";
|
|
3
3
|
import { SheetContent, SheetRoot, SheetView } from "@lynx-js/lynx-ui-sheet";
|
|
4
4
|
import { createContext, useContext, useEffect, useRef, useState } from "@lynx-js/react";
|
|
5
5
|
import { g9IdProps } from "../base/control.jsx";
|
|
@@ -11,6 +11,7 @@ import { useG9Theme } from "../provider/hooks.jsx";
|
|
|
11
11
|
import { G9SheetGestureFrame, useSheetBodyGesture, useSheetGestureRefs } from "./gesture.jsx";
|
|
12
12
|
import { G9SheetFooter, G9SheetHeader } from "./header.jsx";
|
|
13
13
|
import { resolveSheetMotion } from "./motion.jsx";
|
|
14
|
+
import { G9SheetScrollIndicator, G9SheetScrollModel } from "./scroll-indicator.jsx";
|
|
14
15
|
import { clampFitHeight, currentPlatform, g9SheetDragCloseThreshold, g9SheetFitLoadingMinHeight, g9SheetFitMinHeight, parentRecedeOffset, platformSheetConstraints, resolveSheetLayout, sheetOverlayOpacity, sheetSurfaceBottomPadding, sheetSurfaceTopPadding } from "./sizing.jsx";
|
|
15
16
|
const SheetContext = createContext(null);
|
|
16
17
|
function useG9SheetContext() {
|
|
@@ -26,7 +27,7 @@ const sheetInnerStyle = {
|
|
|
26
27
|
display: 'flex',
|
|
27
28
|
flexDirection: 'column'
|
|
28
29
|
};
|
|
29
|
-
const g9SheetRecedeTransition =
|
|
30
|
+
const g9SheetRecedeTransition = ()=>`transform ${g9Ms(220)}ms ease-out, opacity ${g9Ms(220)}ms ease-out`;
|
|
30
31
|
function orderedDetents(o) {
|
|
31
32
|
const requested = o.states && o.states.length > 0 ? o.states : [
|
|
32
33
|
'medium',
|
|
@@ -46,7 +47,7 @@ function initialDetent(o, fit) {
|
|
|
46
47
|
return ordered.includes(current) ? current : null != (_ordered_ = ordered[0]) ? _ordered_ : 'medium';
|
|
47
48
|
}
|
|
48
49
|
function G9SheetFrame(props) {
|
|
49
|
-
var _ref, _o_deferredLoadingPlaceholderHeight, _o_loadingSkeletonRowCount, _ratios_layout_initialSnap, _props_bottomInset, _o_openAnimationDurationMs, _o_closeAnimationDurationMs, _o_cornerRadius, _o_backgroundColor, _ratios_restIndex, _o_padding, _o_padding1;
|
|
50
|
+
var _ref, _o_deferredLoadingPlaceholderHeight, _o_loadingSkeletonRowCount, _ratios_layout_initialSnap, _props_bottomInset, _o_openAnimationDurationMs, _o_closeAnimationDurationMs, _o_cornerRadius, _o_backgroundColor, _ratios_restIndex, _o_padding, _o_padding1, _o_padding2;
|
|
50
51
|
var _props_memo;
|
|
51
52
|
const { entry, stack, settings } = props;
|
|
52
53
|
const palette = useG9Theme().palette;
|
|
@@ -87,7 +88,7 @@ function G9SheetFrame(props) {
|
|
|
87
88
|
setRevealed(true);
|
|
88
89
|
if (false !== o.fadeDeferredContentIn) {
|
|
89
90
|
setCovering(true);
|
|
90
|
-
coverTimer.current = startTimer(()=>setCovering(false), 220);
|
|
91
|
+
coverTimer.current = startTimer(()=>setCovering(false), g9Ms(220));
|
|
91
92
|
}
|
|
92
93
|
}, delay);
|
|
93
94
|
return ()=>{
|
|
@@ -113,10 +114,13 @@ function G9SheetFrame(props) {
|
|
|
113
114
|
var _e_detail;
|
|
114
115
|
return setFooterHeight(Math.max(0, null != (_ref = null == (_e_detail = e.detail) ? void 0 : _e_detail.height) ? _ref : 0));
|
|
115
116
|
};
|
|
117
|
+
const indicator = useRef(null);
|
|
118
|
+
if ('always' === o.scrollIndicator && false !== o.scrollBody && !indicator.current) indicator.current = new G9SheetScrollModel();
|
|
116
119
|
const onBodyLayout = (e)=>{
|
|
117
|
-
var _ref;
|
|
118
|
-
var _e_detail, _props_memo;
|
|
120
|
+
var _ref, _o_padding;
|
|
121
|
+
var _e_detail, _indicator_current, _props_memo;
|
|
119
122
|
const h = null != (_ref = null == (_e_detail = e.detail) ? void 0 : _e_detail.height) ? _ref : 0;
|
|
123
|
+
null == (_indicator_current = indicator.current) || _indicator_current.setContent(h > 0 ? h + 2 * (null != (_o_padding = o.padding) ? _o_padding : 0) : 0);
|
|
120
124
|
if (h <= 0) return;
|
|
121
125
|
setBodyHeight(h);
|
|
122
126
|
if (memoKey && revealed && !covering) null == (_props_memo = props.memo) || _props_memo.record(memoKey, h);
|
|
@@ -228,7 +232,7 @@ function G9SheetFrame(props) {
|
|
|
228
232
|
<SheetContent className={`g9-sheet g9-sheet--${entry.status}${layout.fit ? ' g9-sheet--fit' : ''}`} style={platformSheetConstraints(o)} innerStyle={sheetInnerStyle} {...motion}>
|
|
229
233
|
<G9SheetGestureFrame refs={gesture} canDrag={!layout.dragDisabled} bodyDrags={false !== o.scrollingExpandsSheet} className="g9-sheet__frame" style={{
|
|
230
234
|
...recede,
|
|
231
|
-
transition: g9SheetRecedeTransition
|
|
235
|
+
transition: g9SheetRecedeTransition()
|
|
232
236
|
}}>
|
|
233
237
|
<view className="g9-sheet__surface" {...g9IdProps(o.automationId)} style={{
|
|
234
238
|
backgroundColor: surface,
|
|
@@ -250,11 +254,29 @@ function G9SheetFrame(props) {
|
|
|
250
254
|
padding: `${null != (_o_padding = o.padding) ? _o_padding : 0}px`
|
|
251
255
|
}}>
|
|
252
256
|
{content}
|
|
253
|
-
</view> : <
|
|
257
|
+
</view> : indicator.current ? <view className={`g9-sheet__body-frame${layout.fillBody ? ' g9-sheet__body--fill' : ''}`}>
|
|
258
|
+
<scroll-view className={`g9-sheet__body${layout.fillBody ? ' g9-sheet__body--fill' : ''}`} scroll-orientation="vertical" main-thread:ref={gesture.body} main-thread:bindscroll={phone ? bodyGesture.onScrollMT : void 0} main-thread:bindtouchstart={bodyGesture.onTouchStartMT} scroll-event-throttle={16} bindscroll={(e)=>{
|
|
259
|
+
var _ref;
|
|
260
|
+
var _e_detail, _indicator_current;
|
|
261
|
+
return null == (_indicator_current = indicator.current) ? void 0 : _indicator_current.setTop(null != (_ref = null == (_e_detail = e.detail) ? void 0 : _e_detail.scrollTop) ? _ref : 0);
|
|
262
|
+
}} bindlayoutchange={(e)=>{
|
|
263
|
+
var _ref;
|
|
264
|
+
var _e_detail, _indicator_current;
|
|
265
|
+
return null == (_indicator_current = indicator.current) ? void 0 : _indicator_current.setViewport(null != (_ref = null == (_e_detail = e.detail) ? void 0 : _e_detail.height) ? _ref : 0);
|
|
266
|
+
}} style={{
|
|
254
267
|
padding: `${null != (_o_padding1 = o.padding) ? _o_padding1 : 0}px`,
|
|
255
268
|
...null !== maxBody ? {
|
|
256
269
|
maxHeight: `${maxBody}px`
|
|
257
270
|
} : {}
|
|
271
|
+
}}>
|
|
272
|
+
{content}
|
|
273
|
+
</scroll-view>
|
|
274
|
+
<G9SheetScrollIndicator model={indicator.current}/>
|
|
275
|
+
</view> : <scroll-view className={`g9-sheet__body${layout.fillBody ? ' g9-sheet__body--fill' : ''}`} scroll-orientation="vertical" main-thread:ref={gesture.body} main-thread:bindscroll={phone ? bodyGesture.onScrollMT : void 0} main-thread:bindtouchstart={bodyGesture.onTouchStartMT} style={{
|
|
276
|
+
padding: `${null != (_o_padding2 = o.padding) ? _o_padding2 : 0}px`,
|
|
277
|
+
...null !== maxBody ? {
|
|
278
|
+
maxHeight: `${maxBody}px`
|
|
279
|
+
} : {}
|
|
258
280
|
}}>
|
|
259
281
|
{content}
|
|
260
282
|
</scroll-view>}
|