@elurjs/ionic 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +287 -0
- package/MIGRATION.md +221 -0
- package/README.md +695 -0
- package/dist/lib/IonRouterOutlet.d.ts +154 -0
- package/dist/lib/__tests__/IonRouterOutlet.test.d.ts +1 -0
- package/dist/lib/__tests__/bundles.test.d.ts +1 -0
- package/dist/lib/__tests__/cache-policy.test.d.ts +1 -0
- package/dist/lib/__tests__/capacitor.test.d.ts +1 -0
- package/dist/lib/__tests__/components.test.d.ts +1 -0
- package/dist/lib/__tests__/devtools.test.d.ts +1 -0
- package/dist/lib/__tests__/lifecycle.test.d.ts +1 -0
- package/dist/lib/__tests__/mocks/ionic.d.ts +61 -0
- package/dist/lib/__tests__/navigation.test.d.ts +1 -0
- package/dist/lib/__tests__/overlays.test.d.ts +1 -0
- package/dist/lib/__tests__/page-state.test.d.ts +1 -0
- package/dist/lib/__tests__/phase0-bugs.test.d.ts +1 -0
- package/dist/lib/__tests__/setup.d.ts +1 -0
- package/dist/lib/__tests__/setup.test.d.ts +1 -0
- package/dist/lib/__tests__/tabs.test.d.ts +1 -0
- package/dist/lib/__tests__/vite-plugin.test.d.ts +1 -0
- package/dist/lib/bundles/all.cjs +2 -0
- package/dist/lib/bundles/all.cjs.map +1 -0
- package/dist/lib/bundles/all.d.ts +16 -0
- package/dist/lib/bundles/all.js +21 -0
- package/dist/lib/bundles/all.js.map +1 -0
- package/dist/lib/bundles/buttons.cjs +2 -0
- package/dist/lib/bundles/buttons.cjs.map +1 -0
- package/dist/lib/bundles/buttons.d.ts +7 -0
- package/dist/lib/bundles/buttons.js +17 -0
- package/dist/lib/bundles/buttons.js.map +1 -0
- package/dist/lib/bundles/feedback.cjs +2 -0
- package/dist/lib/bundles/feedback.cjs.map +1 -0
- package/dist/lib/bundles/feedback.d.ts +8 -0
- package/dist/lib/bundles/feedback.js +19 -0
- package/dist/lib/bundles/feedback.js.map +1 -0
- package/dist/lib/bundles/forms.cjs +2 -0
- package/dist/lib/bundles/forms.cjs.map +1 -0
- package/dist/lib/bundles/forms.d.ts +11 -0
- package/dist/lib/bundles/forms.js +47 -0
- package/dist/lib/bundles/forms.js.map +1 -0
- package/dist/lib/bundles/layout.cjs +2 -0
- package/dist/lib/bundles/layout.cjs.map +1 -0
- package/dist/lib/bundles/layout.d.ts +9 -0
- package/dist/lib/bundles/layout.js +33 -0
- package/dist/lib/bundles/layout.js.map +1 -0
- package/dist/lib/bundles/lists.cjs +2 -0
- package/dist/lib/bundles/lists.cjs.map +1 -0
- package/dist/lib/bundles/lists.d.ts +15 -0
- package/dist/lib/bundles/lists.js +65 -0
- package/dist/lib/bundles/lists.js.map +1 -0
- package/dist/lib/bundles/navigation.cjs +2 -0
- package/dist/lib/bundles/navigation.cjs.map +1 -0
- package/dist/lib/bundles/navigation.d.ts +9 -0
- package/dist/lib/bundles/navigation.js +29 -0
- package/dist/lib/bundles/navigation.js.map +1 -0
- package/dist/lib/bundles/overlays.cjs +2 -0
- package/dist/lib/bundles/overlays.cjs.map +1 -0
- package/dist/lib/bundles/overlays.d.ts +9 -0
- package/dist/lib/bundles/overlays.js +25 -0
- package/dist/lib/bundles/overlays.js.map +1 -0
- package/dist/lib/capacitor.cjs +2 -0
- package/dist/lib/capacitor.cjs.map +1 -0
- package/dist/lib/capacitor.d.ts +217 -0
- package/dist/lib/capacitor.js +223 -0
- package/dist/lib/capacitor.js.map +1 -0
- package/dist/lib/components/accordion-group.cjs +1 -0
- package/dist/lib/components/accordion-group.d.ts +11 -0
- package/dist/lib/components/accordion-group.js +2 -0
- package/dist/lib/components/accordion.cjs +1 -0
- package/dist/lib/components/accordion.d.ts +11 -0
- package/dist/lib/components/accordion.js +2 -0
- package/dist/lib/components/action-sheet.cjs +1 -0
- package/dist/lib/components/action-sheet.d.ts +11 -0
- package/dist/lib/components/action-sheet.js +2 -0
- package/dist/lib/components/alert.cjs +1 -0
- package/dist/lib/components/alert.d.ts +11 -0
- package/dist/lib/components/alert.js +2 -0
- package/dist/lib/components/avatar.cjs +1 -0
- package/dist/lib/components/avatar.d.ts +11 -0
- package/dist/lib/components/avatar.js +2 -0
- package/dist/lib/components/backdrop.cjs +1 -0
- package/dist/lib/components/backdrop.d.ts +11 -0
- package/dist/lib/components/backdrop.js +2 -0
- package/dist/lib/components/badge.cjs +1 -0
- package/dist/lib/components/badge.d.ts +11 -0
- package/dist/lib/components/badge.js +2 -0
- package/dist/lib/components/breadcrumb.cjs +1 -0
- package/dist/lib/components/breadcrumb.d.ts +11 -0
- package/dist/lib/components/breadcrumb.js +2 -0
- package/dist/lib/components/breadcrumbs.cjs +1 -0
- package/dist/lib/components/breadcrumbs.d.ts +11 -0
- package/dist/lib/components/breadcrumbs.js +2 -0
- package/dist/lib/components/button.cjs +1 -0
- package/dist/lib/components/button.d.ts +11 -0
- package/dist/lib/components/button.js +2 -0
- package/dist/lib/components/buttons.cjs +1 -0
- package/dist/lib/components/buttons.d.ts +11 -0
- package/dist/lib/components/buttons.js +2 -0
- package/dist/lib/components/card-content.cjs +1 -0
- package/dist/lib/components/card-content.d.ts +11 -0
- package/dist/lib/components/card-content.js +2 -0
- package/dist/lib/components/card-header.cjs +1 -0
- package/dist/lib/components/card-header.d.ts +11 -0
- package/dist/lib/components/card-header.js +2 -0
- package/dist/lib/components/card-subtitle.cjs +1 -0
- package/dist/lib/components/card-subtitle.d.ts +11 -0
- package/dist/lib/components/card-subtitle.js +2 -0
- package/dist/lib/components/card-title.cjs +1 -0
- package/dist/lib/components/card-title.d.ts +11 -0
- package/dist/lib/components/card-title.js +2 -0
- package/dist/lib/components/card.cjs +1 -0
- package/dist/lib/components/card.d.ts +11 -0
- package/dist/lib/components/card.js +2 -0
- package/dist/lib/components/checkbox.cjs +1 -0
- package/dist/lib/components/checkbox.d.ts +11 -0
- package/dist/lib/components/checkbox.js +2 -0
- package/dist/lib/components/chip.cjs +1 -0
- package/dist/lib/components/chip.d.ts +11 -0
- package/dist/lib/components/chip.js +2 -0
- package/dist/lib/components/col.cjs +1 -0
- package/dist/lib/components/col.d.ts +11 -0
- package/dist/lib/components/col.js +2 -0
- package/dist/lib/components/content.cjs +1 -0
- package/dist/lib/components/content.d.ts +11 -0
- package/dist/lib/components/content.js +2 -0
- package/dist/lib/components/datetime-button.cjs +1 -0
- package/dist/lib/components/datetime-button.d.ts +11 -0
- package/dist/lib/components/datetime-button.js +2 -0
- package/dist/lib/components/datetime.cjs +1 -0
- package/dist/lib/components/datetime.d.ts +11 -0
- package/dist/lib/components/datetime.js +2 -0
- package/dist/lib/components/fab-button.cjs +1 -0
- package/dist/lib/components/fab-button.d.ts +11 -0
- package/dist/lib/components/fab-button.js +2 -0
- package/dist/lib/components/fab-list.cjs +1 -0
- package/dist/lib/components/fab-list.d.ts +11 -0
- package/dist/lib/components/fab-list.js +2 -0
- package/dist/lib/components/fab.cjs +1 -0
- package/dist/lib/components/fab.d.ts +11 -0
- package/dist/lib/components/fab.js +2 -0
- package/dist/lib/components/footer.cjs +1 -0
- package/dist/lib/components/footer.d.ts +11 -0
- package/dist/lib/components/footer.js +2 -0
- package/dist/lib/components/grid.cjs +1 -0
- package/dist/lib/components/grid.d.ts +11 -0
- package/dist/lib/components/grid.js +2 -0
- package/dist/lib/components/header.cjs +1 -0
- package/dist/lib/components/header.d.ts +11 -0
- package/dist/lib/components/header.js +2 -0
- package/dist/lib/components/img.cjs +1 -0
- package/dist/lib/components/img.d.ts +11 -0
- package/dist/lib/components/img.js +2 -0
- package/dist/lib/components/infinite-scroll-content.cjs +1 -0
- package/dist/lib/components/infinite-scroll-content.d.ts +11 -0
- package/dist/lib/components/infinite-scroll-content.js +2 -0
- package/dist/lib/components/infinite-scroll.cjs +1 -0
- package/dist/lib/components/infinite-scroll.d.ts +11 -0
- package/dist/lib/components/infinite-scroll.js +2 -0
- package/dist/lib/components/input-otp.cjs +1 -0
- package/dist/lib/components/input-otp.d.ts +11 -0
- package/dist/lib/components/input-otp.js +2 -0
- package/dist/lib/components/input-password-toggle.cjs +1 -0
- package/dist/lib/components/input-password-toggle.d.ts +11 -0
- package/dist/lib/components/input-password-toggle.js +2 -0
- package/dist/lib/components/input.cjs +1 -0
- package/dist/lib/components/input.d.ts +11 -0
- package/dist/lib/components/input.js +2 -0
- package/dist/lib/components/item-divider.cjs +1 -0
- package/dist/lib/components/item-divider.d.ts +11 -0
- package/dist/lib/components/item-divider.js +2 -0
- package/dist/lib/components/item-group.cjs +1 -0
- package/dist/lib/components/item-group.d.ts +11 -0
- package/dist/lib/components/item-group.js +2 -0
- package/dist/lib/components/item-option.cjs +1 -0
- package/dist/lib/components/item-option.d.ts +11 -0
- package/dist/lib/components/item-option.js +2 -0
- package/dist/lib/components/item-options.cjs +1 -0
- package/dist/lib/components/item-options.d.ts +11 -0
- package/dist/lib/components/item-options.js +2 -0
- package/dist/lib/components/item-sliding.cjs +1 -0
- package/dist/lib/components/item-sliding.d.ts +11 -0
- package/dist/lib/components/item-sliding.js +2 -0
- package/dist/lib/components/item.cjs +1 -0
- package/dist/lib/components/item.d.ts +11 -0
- package/dist/lib/components/item.js +2 -0
- package/dist/lib/components/label.cjs +1 -0
- package/dist/lib/components/label.d.ts +11 -0
- package/dist/lib/components/label.js +2 -0
- package/dist/lib/components/list-header.cjs +1 -0
- package/dist/lib/components/list-header.d.ts +11 -0
- package/dist/lib/components/list-header.js +2 -0
- package/dist/lib/components/list.cjs +1 -0
- package/dist/lib/components/list.d.ts +11 -0
- package/dist/lib/components/list.js +2 -0
- package/dist/lib/components/loading.cjs +1 -0
- package/dist/lib/components/loading.d.ts +11 -0
- package/dist/lib/components/loading.js +2 -0
- package/dist/lib/components/manifest.cjs +2 -0
- package/dist/lib/components/manifest.cjs.map +1 -0
- package/dist/lib/components/manifest.d.ts +43 -0
- package/dist/lib/components/manifest.js +647 -0
- package/dist/lib/components/manifest.js.map +1 -0
- package/dist/lib/components/menu-button.cjs +1 -0
- package/dist/lib/components/menu-button.d.ts +11 -0
- package/dist/lib/components/menu-button.js +2 -0
- package/dist/lib/components/menu-toggle.cjs +1 -0
- package/dist/lib/components/menu-toggle.d.ts +11 -0
- package/dist/lib/components/menu-toggle.js +2 -0
- package/dist/lib/components/menu.cjs +1 -0
- package/dist/lib/components/menu.d.ts +11 -0
- package/dist/lib/components/menu.js +2 -0
- package/dist/lib/components/modal.cjs +1 -0
- package/dist/lib/components/modal.d.ts +11 -0
- package/dist/lib/components/modal.js +2 -0
- package/dist/lib/components/nav-link.cjs +1 -0
- package/dist/lib/components/nav-link.d.ts +11 -0
- package/dist/lib/components/nav-link.js +2 -0
- package/dist/lib/components/nav.cjs +1 -0
- package/dist/lib/components/nav.d.ts +11 -0
- package/dist/lib/components/nav.js +2 -0
- package/dist/lib/components/note.cjs +1 -0
- package/dist/lib/components/note.d.ts +11 -0
- package/dist/lib/components/note.js +2 -0
- package/dist/lib/components/picker-column-option.cjs +1 -0
- package/dist/lib/components/picker-column-option.d.ts +11 -0
- package/dist/lib/components/picker-column-option.js +2 -0
- package/dist/lib/components/picker-column.cjs +1 -0
- package/dist/lib/components/picker-column.d.ts +11 -0
- package/dist/lib/components/picker-column.js +2 -0
- package/dist/lib/components/picker.cjs +1 -0
- package/dist/lib/components/picker.d.ts +11 -0
- package/dist/lib/components/picker.js +2 -0
- package/dist/lib/components/popover.cjs +1 -0
- package/dist/lib/components/popover.d.ts +11 -0
- package/dist/lib/components/popover.js +2 -0
- package/dist/lib/components/progress-bar.cjs +1 -0
- package/dist/lib/components/progress-bar.d.ts +11 -0
- package/dist/lib/components/progress-bar.js +2 -0
- package/dist/lib/components/radio-group.cjs +1 -0
- package/dist/lib/components/radio-group.d.ts +11 -0
- package/dist/lib/components/radio-group.js +2 -0
- package/dist/lib/components/radio.cjs +1 -0
- package/dist/lib/components/radio.d.ts +11 -0
- package/dist/lib/components/radio.js +2 -0
- package/dist/lib/components/range.cjs +1 -0
- package/dist/lib/components/range.d.ts +11 -0
- package/dist/lib/components/range.js +2 -0
- package/dist/lib/components/refresher-content.cjs +1 -0
- package/dist/lib/components/refresher-content.d.ts +11 -0
- package/dist/lib/components/refresher-content.js +2 -0
- package/dist/lib/components/refresher.cjs +1 -0
- package/dist/lib/components/refresher.d.ts +11 -0
- package/dist/lib/components/refresher.js +2 -0
- package/dist/lib/components/reorder-group.cjs +1 -0
- package/dist/lib/components/reorder-group.d.ts +11 -0
- package/dist/lib/components/reorder-group.js +2 -0
- package/dist/lib/components/reorder.cjs +1 -0
- package/dist/lib/components/reorder.d.ts +11 -0
- package/dist/lib/components/reorder.js +2 -0
- package/dist/lib/components/ripple-effect.cjs +1 -0
- package/dist/lib/components/ripple-effect.d.ts +11 -0
- package/dist/lib/components/ripple-effect.js +2 -0
- package/dist/lib/components/router-link.cjs +1 -0
- package/dist/lib/components/router-link.d.ts +11 -0
- package/dist/lib/components/router-link.js +2 -0
- package/dist/lib/components/row.cjs +1 -0
- package/dist/lib/components/row.d.ts +11 -0
- package/dist/lib/components/row.js +2 -0
- package/dist/lib/components/searchbar.cjs +1 -0
- package/dist/lib/components/searchbar.d.ts +11 -0
- package/dist/lib/components/searchbar.js +2 -0
- package/dist/lib/components/segment-button.cjs +1 -0
- package/dist/lib/components/segment-button.d.ts +11 -0
- package/dist/lib/components/segment-button.js +2 -0
- package/dist/lib/components/segment-content.cjs +1 -0
- package/dist/lib/components/segment-content.d.ts +11 -0
- package/dist/lib/components/segment-content.js +2 -0
- package/dist/lib/components/segment-view.cjs +1 -0
- package/dist/lib/components/segment-view.d.ts +11 -0
- package/dist/lib/components/segment-view.js +2 -0
- package/dist/lib/components/segment.cjs +1 -0
- package/dist/lib/components/segment.d.ts +11 -0
- package/dist/lib/components/segment.js +2 -0
- package/dist/lib/components/select-modal.cjs +1 -0
- package/dist/lib/components/select-modal.d.ts +11 -0
- package/dist/lib/components/select-modal.js +2 -0
- package/dist/lib/components/select-option.cjs +1 -0
- package/dist/lib/components/select-option.d.ts +11 -0
- package/dist/lib/components/select-option.js +2 -0
- package/dist/lib/components/select-popover.cjs +1 -0
- package/dist/lib/components/select-popover.d.ts +11 -0
- package/dist/lib/components/select-popover.js +2 -0
- package/dist/lib/components/select.cjs +1 -0
- package/dist/lib/components/select.d.ts +11 -0
- package/dist/lib/components/select.js +2 -0
- package/dist/lib/components/skeleton-text.cjs +1 -0
- package/dist/lib/components/skeleton-text.d.ts +11 -0
- package/dist/lib/components/skeleton-text.js +2 -0
- package/dist/lib/components/spinner.cjs +1 -0
- package/dist/lib/components/spinner.d.ts +11 -0
- package/dist/lib/components/spinner.js +2 -0
- package/dist/lib/components/split-pane.cjs +1 -0
- package/dist/lib/components/split-pane.d.ts +11 -0
- package/dist/lib/components/split-pane.js +2 -0
- package/dist/lib/components/tab-bar.cjs +1 -0
- package/dist/lib/components/tab-bar.d.ts +11 -0
- package/dist/lib/components/tab-bar.js +2 -0
- package/dist/lib/components/tab-button.cjs +1 -0
- package/dist/lib/components/tab-button.d.ts +11 -0
- package/dist/lib/components/tab-button.js +2 -0
- package/dist/lib/components/tab.cjs +1 -0
- package/dist/lib/components/tab.d.ts +11 -0
- package/dist/lib/components/tab.js +2 -0
- package/dist/lib/components/tabs.cjs +1 -0
- package/dist/lib/components/tabs.d.ts +11 -0
- package/dist/lib/components/tabs.js +2 -0
- package/dist/lib/components/text.cjs +1 -0
- package/dist/lib/components/text.d.ts +11 -0
- package/dist/lib/components/text.js +2 -0
- package/dist/lib/components/textarea.cjs +1 -0
- package/dist/lib/components/textarea.d.ts +11 -0
- package/dist/lib/components/textarea.js +2 -0
- package/dist/lib/components/thumbnail.cjs +1 -0
- package/dist/lib/components/thumbnail.d.ts +11 -0
- package/dist/lib/components/thumbnail.js +2 -0
- package/dist/lib/components/title.cjs +1 -0
- package/dist/lib/components/title.d.ts +11 -0
- package/dist/lib/components/title.js +2 -0
- package/dist/lib/components/toast.cjs +1 -0
- package/dist/lib/components/toast.d.ts +11 -0
- package/dist/lib/components/toast.js +2 -0
- package/dist/lib/components/toggle.cjs +1 -0
- package/dist/lib/components/toggle.d.ts +11 -0
- package/dist/lib/components/toggle.js +2 -0
- package/dist/lib/components/toolbar.cjs +1 -0
- package/dist/lib/components/toolbar.d.ts +11 -0
- package/dist/lib/components/toolbar.js +2 -0
- package/dist/lib/components.cjs +1 -0
- package/dist/lib/components.d.ts +110 -0
- package/dist/lib/components.js +94 -0
- package/dist/lib/definitions-CmPIrLhr.js +282 -0
- package/dist/lib/definitions-CmPIrLhr.js.map +1 -0
- package/dist/lib/definitions-CrcjkKEd.cjs +2 -0
- package/dist/lib/definitions-CrcjkKEd.cjs.map +1 -0
- package/dist/lib/devtools.cjs +2 -0
- package/dist/lib/devtools.cjs.map +1 -0
- package/dist/lib/devtools.d.ts +54 -0
- package/dist/lib/devtools.js +46 -0
- package/dist/lib/devtools.js.map +1 -0
- package/dist/lib/elur-ionic.cjs +1 -0
- package/dist/lib/elur-ionic.cjs.map +1 -0
- package/dist/lib/elur-ionic.js +1 -0
- package/dist/lib/elur-ionic.js.map +1 -0
- package/dist/lib/index.d.ts +47 -0
- package/dist/lib/lifecycle.d.ts +62 -0
- package/dist/lib/navigation.cjs +2 -0
- package/dist/lib/navigation.cjs.map +1 -0
- package/dist/lib/navigation.d.ts +214 -0
- package/dist/lib/navigation.js +205 -0
- package/dist/lib/navigation.js.map +1 -0
- package/dist/lib/overlays.cjs +2 -0
- package/dist/lib/overlays.cjs.map +1 -0
- package/dist/lib/overlays.d.ts +400 -0
- package/dist/lib/overlays.js +161 -0
- package/dist/lib/overlays.js.map +1 -0
- package/dist/lib/page-state.cjs +2 -0
- package/dist/lib/page-state.cjs.map +1 -0
- package/dist/lib/page-state.d.ts +160 -0
- package/dist/lib/page-state.js +91 -0
- package/dist/lib/page-state.js.map +1 -0
- package/dist/lib/setup.d.ts +105 -0
- package/dist/lib/tabs-ByPLrzax.cjs +50 -0
- package/dist/lib/tabs-ByPLrzax.cjs.map +1 -0
- package/dist/lib/tabs-DjD1_PTM.js +178 -0
- package/dist/lib/tabs-DjD1_PTM.js.map +1 -0
- package/dist/lib/tabs.cjs +1 -0
- package/dist/lib/tabs.d.ts +83 -0
- package/dist/lib/tabs.js +2 -0
- package/dist/lib/vite-plugin.cjs +38 -0
- package/dist/lib/vite-plugin.cjs.map +1 -0
- package/dist/lib/vite-plugin.d.ts +61 -0
- package/dist/lib/vite-plugin.js +25411 -0
- package/dist/lib/vite-plugin.js.map +1 -0
- package/dist/lib/web-Bnz2Zs_W.cjs +2 -0
- package/dist/lib/web-Bnz2Zs_W.cjs.map +1 -0
- package/dist/lib/web-Ce5OM4dv.js +56 -0
- package/dist/lib/web-Ce5OM4dv.js.map +1 -0
- package/package.json +216 -0
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @elurjs/ionic / overlays.ts
|
|
3
|
+
*
|
|
4
|
+
* Reactive overlay controllers for Elur following the `create*` pattern
|
|
5
|
+
* (`createStore`, `createRouter`, `createForm` → `createToast`, `createAlert`, etc.).
|
|
6
|
+
*
|
|
7
|
+
* Each controller provides:
|
|
8
|
+
* - `presented: Signal<boolean>` — reactive presentation state
|
|
9
|
+
* - `result: Signal<OverlayEventDetail | null>` — dismiss event detail
|
|
10
|
+
* - `present(opts)` — creates + presents the overlay
|
|
11
|
+
* - `dismiss(data?, role?)` — dismisses the active overlay
|
|
12
|
+
* - `dispose()` — transactional cleanup, dismisses if still presented
|
|
13
|
+
*
|
|
14
|
+
* Stale-result protection: only the most recent `present()` call's dismiss
|
|
15
|
+
* event updates the result signal. Latest-wins: presenting a new overlay
|
|
16
|
+
* dismisses the previous one.
|
|
17
|
+
*
|
|
18
|
+
* @example Function component (preferred)
|
|
19
|
+
* ```ts
|
|
20
|
+
* import { signal, html } from "@elurjs/core";
|
|
21
|
+
* import { createToast, createAlert } from "@elurjs/ionic";
|
|
22
|
+
*
|
|
23
|
+
* function SettingsPage() {
|
|
24
|
+
* // Create overlay controllers — signals close over function scope
|
|
25
|
+
* const toast = createToast();
|
|
26
|
+
* const alert = createAlert();
|
|
27
|
+
*
|
|
28
|
+
* const save = async () => {
|
|
29
|
+
* // Use withLoading for async tasks
|
|
30
|
+
* const ok = await withLoading({ message: "Saving..." }, async () => {
|
|
31
|
+
* await fetch("/api/settings", { method: "POST" });
|
|
32
|
+
* });
|
|
33
|
+
* toast.present({ message: "Saved!", duration: 1500 });
|
|
34
|
+
* };
|
|
35
|
+
*
|
|
36
|
+
* const deleteAccount = () => {
|
|
37
|
+
* // confirm() returns a Promise<boolean>
|
|
38
|
+
* confirm({
|
|
39
|
+
* header: "Delete account",
|
|
40
|
+
* message: "This cannot be undone.",
|
|
41
|
+
* confirmText: "Delete",
|
|
42
|
+
* }).then((yes) => {
|
|
43
|
+
* if (yes) alert.present({ header: "Done", message: "Account deleted" });
|
|
44
|
+
* });
|
|
45
|
+
* };
|
|
46
|
+
*
|
|
47
|
+
* return html`
|
|
48
|
+
* <ion-content>
|
|
49
|
+
* <ion-button @click=${save}>Save</ion-button>
|
|
50
|
+
* <ion-button color="danger" @click=${deleteAccount}>Delete</ion-button>
|
|
51
|
+
* </ion-content>
|
|
52
|
+
* `;
|
|
53
|
+
* }
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* @example Class component with lifecycle cleanup
|
|
57
|
+
* ```ts
|
|
58
|
+
* import { ElurComponent, html, signal } from "@elurjs/core";
|
|
59
|
+
* import { createLoading, IonPage } from "@elurjs/ionic";
|
|
60
|
+
*
|
|
61
|
+
* class ProfilePage extends IonPage {
|
|
62
|
+
* private loading = createLoading();
|
|
63
|
+
* private data = signal<unknown>(null);
|
|
64
|
+
*
|
|
65
|
+
* override async onMount() {
|
|
66
|
+
* // withLoading auto-dismisses on settle or error
|
|
67
|
+
* const result = await withLoading(
|
|
68
|
+
* { message: "Loading profile..." },
|
|
69
|
+
* () => fetch("/api/profile").then(r => r.json()),
|
|
70
|
+
* );
|
|
71
|
+
* this.data.value = result;
|
|
72
|
+
* }
|
|
73
|
+
*
|
|
74
|
+
* override onUnmount() {
|
|
75
|
+
* // Dispose all overlay controllers to prevent leaks
|
|
76
|
+
* this.loading.dispose();
|
|
77
|
+
* }
|
|
78
|
+
*
|
|
79
|
+
* override render() {
|
|
80
|
+
* return html`
|
|
81
|
+
* <ion-content>
|
|
82
|
+
* <p>${() => JSON.stringify(this.data.value)}</p>
|
|
83
|
+
* </ion-content>
|
|
84
|
+
* `;
|
|
85
|
+
* }
|
|
86
|
+
* }
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
89
|
+
* @example Reactive UI driven by overlay signals
|
|
90
|
+
* ```ts
|
|
91
|
+
* import { signal, html } from "@elurjs/core";
|
|
92
|
+
* import { createModal } from "@elurjs/ionic";
|
|
93
|
+
*
|
|
94
|
+
* function ProductList() {
|
|
95
|
+
* const modal = createModal();
|
|
96
|
+
* const selected = signal<string | null>(null);
|
|
97
|
+
*
|
|
98
|
+
* const openDetail = (id: string) => {
|
|
99
|
+
* selected.value = id;
|
|
100
|
+
* modal.present({ component: "product-detail" });
|
|
101
|
+
* };
|
|
102
|
+
*
|
|
103
|
+
* return html`
|
|
104
|
+
* <ion-content>
|
|
105
|
+
* <ion-list>
|
|
106
|
+
* <ion-button @click=${() => openDetail("1")}>Product 1</ion-button>
|
|
107
|
+
* </ion-list>
|
|
108
|
+
*
|
|
109
|
+
* <!-- Reactively show dismiss result -->
|
|
110
|
+
* ${() => modal.result.value
|
|
111
|
+
* ? html`<p>Modal closed with role: ${modal.result.value.role}</p>`
|
|
112
|
+
* : null}
|
|
113
|
+
*
|
|
114
|
+
* <!-- Reactively disable button while modal is open -->
|
|
115
|
+
* <ion-button
|
|
116
|
+
* disabled=${() => modal.presented.value}
|
|
117
|
+
* @click=${() => openDetail("2")}
|
|
118
|
+
* >Product 2</ion-button>
|
|
119
|
+
* </ion-content>
|
|
120
|
+
* `;
|
|
121
|
+
* }
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
import { type Signal, type ElurTemplate, type ElurComponent } from "@elurjs/core";
|
|
125
|
+
import { toastController, alertController, loadingController, actionSheetController, popoverController, modalController, pickerController } from "@ionic/core";
|
|
126
|
+
export interface OverlayHandle<TDetail = unknown> {
|
|
127
|
+
/** True while the overlay is presented (visible). */
|
|
128
|
+
readonly presented: Signal<boolean>;
|
|
129
|
+
/** The dismiss event detail (role, data) — null until dismissed. */
|
|
130
|
+
readonly result: Signal<TDetail | null>;
|
|
131
|
+
/** Present the overlay with the given options. */
|
|
132
|
+
present(options: Record<string, unknown>): Promise<void>;
|
|
133
|
+
/** Dismiss the active overlay with optional data and role. */
|
|
134
|
+
dismiss(data?: unknown, role?: string): Promise<boolean>;
|
|
135
|
+
/** Cleanup: dismisses any active overlay. Safe to call multiple times. */
|
|
136
|
+
dispose(): void;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Create a reactive toast overlay controller.
|
|
140
|
+
* Call `dispose()` in `onUnmount()` when used in a class component.
|
|
141
|
+
*/
|
|
142
|
+
export declare function createToast(): OverlayHandle;
|
|
143
|
+
/**
|
|
144
|
+
* Create a reactive alert overlay controller.
|
|
145
|
+
* Call `dispose()` in `onUnmount()` when used in a class component.
|
|
146
|
+
*/
|
|
147
|
+
export declare function createAlert(): OverlayHandle;
|
|
148
|
+
/**
|
|
149
|
+
* Create a reactive loading overlay controller.
|
|
150
|
+
* Call `dispose()` in `onUnmount()` when used in a class component.
|
|
151
|
+
*/
|
|
152
|
+
export declare function createLoading(): OverlayHandle;
|
|
153
|
+
/**
|
|
154
|
+
* Create a reactive action-sheet overlay controller.
|
|
155
|
+
* Call `dispose()` in `onUnmount()` when used in a class component.
|
|
156
|
+
*/
|
|
157
|
+
export declare function createActionSheet(): OverlayHandle;
|
|
158
|
+
/**
|
|
159
|
+
* Create a reactive popover overlay controller.
|
|
160
|
+
* Call `dispose()` in `onUnmount()` when used in a class component.
|
|
161
|
+
*/
|
|
162
|
+
export declare function createPopover(): OverlayHandle;
|
|
163
|
+
/**
|
|
164
|
+
* Create a reactive modal overlay controller.
|
|
165
|
+
* Call `dispose()` in `onUnmount()` when used in a class component.
|
|
166
|
+
*/
|
|
167
|
+
export declare function createModal(): OverlayHandle;
|
|
168
|
+
/**
|
|
169
|
+
* Present a toast and return immediately (fire-and-forget).
|
|
170
|
+
* The toast auto-dismisses after its duration.
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* ```ts
|
|
174
|
+
* import { showToast } from "@elurjs/ionic";
|
|
175
|
+
*
|
|
176
|
+
* showToast({ message: "Saved!", duration: 1500 });
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
export declare function showToast(options: Record<string, unknown>): Promise<void>;
|
|
180
|
+
/**
|
|
181
|
+
* Present a loading overlay, run an async task, then dismiss.
|
|
182
|
+
* Returns the task result. If the task throws, the loading is still dismissed.
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* ```ts
|
|
186
|
+
* import { withLoading } from "@elurjs/ionic";
|
|
187
|
+
*
|
|
188
|
+
* const data = await withLoading(
|
|
189
|
+
* { message: "Fetching..." },
|
|
190
|
+
* () => fetch("/api/data").then(r => r.json()),
|
|
191
|
+
* );
|
|
192
|
+
* ```
|
|
193
|
+
*/
|
|
194
|
+
export declare function withLoading<T>(options: Record<string, unknown>, task: () => Promise<T>): Promise<T>;
|
|
195
|
+
/**
|
|
196
|
+
* Present a confirm alert and return true if "OK" was clicked, false otherwise.
|
|
197
|
+
*
|
|
198
|
+
* @example
|
|
199
|
+
* ```ts
|
|
200
|
+
* import { confirm } from "@elurjs/ionic";
|
|
201
|
+
*
|
|
202
|
+
* const yes = await confirm({
|
|
203
|
+
* header: "Delete",
|
|
204
|
+
* message: "Are you sure?",
|
|
205
|
+
* confirmText: "Delete",
|
|
206
|
+
* });
|
|
207
|
+
* if (yes) deleteItem();
|
|
208
|
+
* ```
|
|
209
|
+
*/
|
|
210
|
+
export declare function confirm(options: {
|
|
211
|
+
header?: string;
|
|
212
|
+
message?: string;
|
|
213
|
+
confirmText?: string;
|
|
214
|
+
cancelText?: string;
|
|
215
|
+
}): Promise<boolean>;
|
|
216
|
+
export { toastController, alertController, loadingController, actionSheetController, popoverController, modalController, pickerController, };
|
|
217
|
+
/**
|
|
218
|
+
* A Elur FrameworkDelegate that can mount ElurTemplate or ElurComponent
|
|
219
|
+
* instances inside Ionic overlays (modal, popover).
|
|
220
|
+
*
|
|
221
|
+
* Ionic's `FrameworkDelegate` interface has two methods:
|
|
222
|
+
* - `attachViewToDom(container, component, props, cssClasses)` → HTMLElement
|
|
223
|
+
* - `removeViewFromDom(container, component)` → void
|
|
224
|
+
*
|
|
225
|
+
* For Elur, "component" is a function that returns a ElurTemplate or
|
|
226
|
+
* ElurComponent. The delegate creates a wrapper div, mounts the Elur
|
|
227
|
+
* content into it, appends it to the overlay, and tracks the unmount
|
|
228
|
+
* handle for cleanup on dismiss.
|
|
229
|
+
*/
|
|
230
|
+
export interface ElurOverlayDelegate {
|
|
231
|
+
attachViewToDom(container: HTMLElement, component: () => ElurTemplate | ElurComponent, propsOrData?: Record<string, unknown>, cssClasses?: string[]): Promise<HTMLElement>;
|
|
232
|
+
removeViewFromDom(container: HTMLElement, component: unknown): Promise<void>;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Create a Elur delegate for use with Ionic modal/popover controllers.
|
|
236
|
+
* The delegate mounts Elur templates inside overlays and cleans up on
|
|
237
|
+
* removal.
|
|
238
|
+
*
|
|
239
|
+
* @example
|
|
240
|
+
* ```ts
|
|
241
|
+
* import { createElurDelegate, createModal } from "@elurjs/ionic";
|
|
242
|
+
* import { html, signal } from "@elurjs/core";
|
|
243
|
+
*
|
|
244
|
+
* const delegate = createElurDelegate();
|
|
245
|
+
* const modal = createModal();
|
|
246
|
+
*
|
|
247
|
+
* // The delegate is passed via `delegate` option
|
|
248
|
+
* modal.present({
|
|
249
|
+
* component: () => html`<ion-content><h1>Hello from modal!</h1></ion-content>`,
|
|
250
|
+
* delegate,
|
|
251
|
+
* });
|
|
252
|
+
* ```
|
|
253
|
+
*/
|
|
254
|
+
export declare function createElurDelegate(): ElurOverlayDelegate;
|
|
255
|
+
/**
|
|
256
|
+
* Options for presenting a modal with Elur content.
|
|
257
|
+
* The `component` function returns a ElurTemplate or ElurComponent that will
|
|
258
|
+
* be mounted inside the modal via the Elur delegate.
|
|
259
|
+
*/
|
|
260
|
+
export interface ModalOptions {
|
|
261
|
+
/** Function returning the Elur content to mount inside the modal. */
|
|
262
|
+
component: () => ElurTemplate | ElurComponent;
|
|
263
|
+
/** Component props/data passed to the delegate. */
|
|
264
|
+
componentProps?: Record<string, unknown>;
|
|
265
|
+
/** CSS classes to add to the mounted content wrapper. */
|
|
266
|
+
cssClasses?: string[];
|
|
267
|
+
/** Modal-specific options. */
|
|
268
|
+
backdropDismiss?: boolean;
|
|
269
|
+
showBackdrop?: boolean;
|
|
270
|
+
animated?: boolean;
|
|
271
|
+
canDismiss?: boolean | (() => Promise<boolean>);
|
|
272
|
+
/** Custom delegate (if not provided, a default Elur delegate is used). */
|
|
273
|
+
delegate?: ElurOverlayDelegate;
|
|
274
|
+
[key: string]: unknown;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Options for presenting a popover with Elur content.
|
|
278
|
+
*/
|
|
279
|
+
export interface PopoverOptions {
|
|
280
|
+
/** Function returning the Elur content to mount inside the popover. */
|
|
281
|
+
component: () => ElurTemplate | ElurComponent;
|
|
282
|
+
/** Component props/data passed to the delegate. */
|
|
283
|
+
componentProps?: Record<string, unknown>;
|
|
284
|
+
/** CSS classes to add to the mounted content wrapper. */
|
|
285
|
+
cssClasses?: string[];
|
|
286
|
+
/** The element that the popover should be anchored to. */
|
|
287
|
+
event?: Event | {
|
|
288
|
+
target: HTMLElement;
|
|
289
|
+
};
|
|
290
|
+
/** Popover-specific options. */
|
|
291
|
+
backdropDismiss?: boolean;
|
|
292
|
+
showBackdrop?: boolean;
|
|
293
|
+
animated?: boolean;
|
|
294
|
+
/** Custom delegate (if not provided, a default Elur delegate is used). */
|
|
295
|
+
delegate?: ElurOverlayDelegate;
|
|
296
|
+
[key: string]: unknown;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Enhanced modal controller with Elur delegate support.
|
|
300
|
+
* Unlike the basic `createModal()`, this automatically creates and uses
|
|
301
|
+
* a Elur delegate so `component` can be a ElurTemplate/ElurComponent.
|
|
302
|
+
*
|
|
303
|
+
* @example
|
|
304
|
+
* ```ts
|
|
305
|
+
* import { createModalController, createElurDelegate } from "@elurjs/ionic";
|
|
306
|
+
* import { html, signal } from "@elurjs/core";
|
|
307
|
+
*
|
|
308
|
+
* const modal = createModalController();
|
|
309
|
+
*
|
|
310
|
+
* modal.present({
|
|
311
|
+
* component: () => html`
|
|
312
|
+
* <ion-header>
|
|
313
|
+
* <ion-toolbar><ion-title>Settings</ion-title></ion-toolbar>
|
|
314
|
+
* </ion-header>
|
|
315
|
+
* <ion-content><p>Modal content here</p></ion-content>
|
|
316
|
+
* `,
|
|
317
|
+
* });
|
|
318
|
+
*
|
|
319
|
+
* // React to dismiss
|
|
320
|
+
* effect(() => {
|
|
321
|
+
* if (modal.result.value) {
|
|
322
|
+
* console.log("Modal dismissed:", modal.result.value);
|
|
323
|
+
* }
|
|
324
|
+
* });
|
|
325
|
+
* ```
|
|
326
|
+
*/
|
|
327
|
+
export declare function createModalController(delegate?: ElurOverlayDelegate): OverlayHandle;
|
|
328
|
+
/**
|
|
329
|
+
* Enhanced popover controller with Elur delegate support.
|
|
330
|
+
* Automatically creates and uses a Elur delegate so `component` can be
|
|
331
|
+
* a ElurTemplate/ElurComponent.
|
|
332
|
+
*
|
|
333
|
+
* @example
|
|
334
|
+
* ```ts
|
|
335
|
+
* import { createPopoverController } from "@elurjs/ionic";
|
|
336
|
+
* import { html } from "@elurjs/core";
|
|
337
|
+
*
|
|
338
|
+
* const popover = createPopoverController();
|
|
339
|
+
*
|
|
340
|
+
* // In an event handler:
|
|
341
|
+
* popover.present({
|
|
342
|
+
* component: () => html`<ion-content><p>Popover content</p></ion-content>`,
|
|
343
|
+
* event, // the click event for anchoring
|
|
344
|
+
* });
|
|
345
|
+
* ```
|
|
346
|
+
*/
|
|
347
|
+
export declare function createPopoverController(delegate?: ElurOverlayDelegate): OverlayHandle;
|
|
348
|
+
/**
|
|
349
|
+
* Options for a picker column.
|
|
350
|
+
*/
|
|
351
|
+
export interface PickerColumnOption {
|
|
352
|
+
text: string;
|
|
353
|
+
value: string | number;
|
|
354
|
+
disabled?: boolean;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Options for presenting a picker overlay.
|
|
358
|
+
*/
|
|
359
|
+
export interface PickerOptions {
|
|
360
|
+
columns: Array<{
|
|
361
|
+
name: string;
|
|
362
|
+
options: PickerColumnOption[];
|
|
363
|
+
selectedIndex?: number;
|
|
364
|
+
}>;
|
|
365
|
+
buttons?: Array<{
|
|
366
|
+
text: string;
|
|
367
|
+
role?: string;
|
|
368
|
+
handler?: (selected: Record<string, PickerColumnOption>) => void;
|
|
369
|
+
}>;
|
|
370
|
+
cssClass?: string;
|
|
371
|
+
animated?: boolean;
|
|
372
|
+
backdropDismiss?: boolean;
|
|
373
|
+
[key: string]: unknown;
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Create a reactive picker overlay controller.
|
|
377
|
+
* Pickers are column-based selection overlays (like iOS date pickers).
|
|
378
|
+
*
|
|
379
|
+
* @example
|
|
380
|
+
* ```ts
|
|
381
|
+
* import { createPicker } from "@elurjs/ionic";
|
|
382
|
+
*
|
|
383
|
+
* const picker = createPicker();
|
|
384
|
+
* await picker.present({
|
|
385
|
+
* columns: [{
|
|
386
|
+
* name: "size",
|
|
387
|
+
* options: [
|
|
388
|
+
* { text: "Small", value: "sm" },
|
|
389
|
+
* { text: "Medium", value: "md" },
|
|
390
|
+
* { text: "Large", value: "lg" },
|
|
391
|
+
* ],
|
|
392
|
+
* }],
|
|
393
|
+
* buttons: [
|
|
394
|
+
* { text: "Cancel", role: "cancel" },
|
|
395
|
+
* { text: "Done", role: "confirm" },
|
|
396
|
+
* ],
|
|
397
|
+
* });
|
|
398
|
+
* ```
|
|
399
|
+
*/
|
|
400
|
+
export declare function createPicker(): OverlayHandle;
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { mount as e, signal as t } from "@elurjs/core";
|
|
2
|
+
import { actionSheetController as n, alertController as r, loadingController as i, modalController as a, pickerController as o, popoverController as s, toastController as c } from "@ionic/core";
|
|
3
|
+
//#region src/overlays.ts
|
|
4
|
+
var l = null, u = {
|
|
5
|
+
async attachViewToDom(t, n, r, i) {
|
|
6
|
+
if (typeof n == "function") {
|
|
7
|
+
let r = n();
|
|
8
|
+
return l = e(r, t), t;
|
|
9
|
+
}
|
|
10
|
+
if (typeof n == "string") {
|
|
11
|
+
let e = document.createElement(n);
|
|
12
|
+
return i && i.forEach((t) => e.classList.add(t)), t.appendChild(e), e;
|
|
13
|
+
}
|
|
14
|
+
return n instanceof HTMLElement ? (i && i.forEach((e) => n.classList.add(e)), t.appendChild(n), n) : t;
|
|
15
|
+
},
|
|
16
|
+
async removeViewFromDom(e, t) {
|
|
17
|
+
l &&= (l.unmount(), null);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
function d(e) {
|
|
21
|
+
let n = t(!1), r = t(null), i = null, a = !1;
|
|
22
|
+
async function o(t) {
|
|
23
|
+
if (a) return;
|
|
24
|
+
if (i) try {
|
|
25
|
+
await i.el.dismiss();
|
|
26
|
+
} catch {}
|
|
27
|
+
let o = (i?.token ?? 0) + 1, s = { ...t };
|
|
28
|
+
typeof s.component == "function" && !s.delegate && (s.delegate = u);
|
|
29
|
+
let c = await e.create(s);
|
|
30
|
+
await c.present(), i = {
|
|
31
|
+
el: c,
|
|
32
|
+
dismissPromise: c.onDidDismiss?.() ?? Promise.resolve(null),
|
|
33
|
+
token: o
|
|
34
|
+
}, n.value = !0, i.dismissPromise.then((e) => {
|
|
35
|
+
i?.token === o && !a && (r.value = e, n.value = !1, i = null);
|
|
36
|
+
}).catch(() => {
|
|
37
|
+
i?.token === o && !a && (n.value = !1, i = null);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
async function s(e, t) {
|
|
41
|
+
if (!i || a) return !1;
|
|
42
|
+
try {
|
|
43
|
+
return await i.el.dismiss(e, t);
|
|
44
|
+
} catch {
|
|
45
|
+
return !1;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function c() {
|
|
49
|
+
if (!a) {
|
|
50
|
+
if (a = !0, i) {
|
|
51
|
+
try {
|
|
52
|
+
i.el.dismiss();
|
|
53
|
+
} catch {}
|
|
54
|
+
i = null;
|
|
55
|
+
}
|
|
56
|
+
n.value = !1;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
presented: n,
|
|
61
|
+
result: r,
|
|
62
|
+
present: o,
|
|
63
|
+
dismiss: s,
|
|
64
|
+
dispose: c
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function f() {
|
|
68
|
+
return d(c);
|
|
69
|
+
}
|
|
70
|
+
function p() {
|
|
71
|
+
return d(r);
|
|
72
|
+
}
|
|
73
|
+
function m() {
|
|
74
|
+
return d(i);
|
|
75
|
+
}
|
|
76
|
+
function h() {
|
|
77
|
+
return d(n);
|
|
78
|
+
}
|
|
79
|
+
function g() {
|
|
80
|
+
return d(s);
|
|
81
|
+
}
|
|
82
|
+
function _() {
|
|
83
|
+
return d(a);
|
|
84
|
+
}
|
|
85
|
+
async function v(e) {
|
|
86
|
+
await (await c.create(e)).present();
|
|
87
|
+
}
|
|
88
|
+
async function y(e, t) {
|
|
89
|
+
let n = await i.create(e);
|
|
90
|
+
await n.present();
|
|
91
|
+
try {
|
|
92
|
+
return await t();
|
|
93
|
+
} finally {
|
|
94
|
+
await n.dismiss();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
async function b(e) {
|
|
98
|
+
return new Promise((t) => {
|
|
99
|
+
r.create({
|
|
100
|
+
header: e.header,
|
|
101
|
+
message: e.message,
|
|
102
|
+
buttons: [{
|
|
103
|
+
text: e.cancelText ?? "Cancel",
|
|
104
|
+
role: "cancel",
|
|
105
|
+
handler: () => t(!1)
|
|
106
|
+
}, {
|
|
107
|
+
text: e.confirmText ?? "OK",
|
|
108
|
+
role: "confirm",
|
|
109
|
+
handler: () => t(!0)
|
|
110
|
+
}]
|
|
111
|
+
}).then((e) => e.present());
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
function x() {
|
|
115
|
+
let t = /* @__PURE__ */ new Map();
|
|
116
|
+
return {
|
|
117
|
+
async attachViewToDom(n, r, i, a) {
|
|
118
|
+
let o = document.createElement("div");
|
|
119
|
+
if (a) for (let e of a) o.classList.add(e);
|
|
120
|
+
let s = e(r(), o);
|
|
121
|
+
return t.set(o, s.unmount), n.appendChild(o), o;
|
|
122
|
+
},
|
|
123
|
+
async removeViewFromDom(e, n) {
|
|
124
|
+
let r = n, i = t.get(r);
|
|
125
|
+
i && (i(), t.delete(r)), r.parentElement && r.remove();
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function S(e) {
|
|
130
|
+
let t = e ?? x(), n = d(a), r = n.present;
|
|
131
|
+
async function i(e) {
|
|
132
|
+
await r({
|
|
133
|
+
...e,
|
|
134
|
+
delegate: t
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
...n,
|
|
139
|
+
present: i
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function C(e) {
|
|
143
|
+
let t = e ?? x(), n = d(s), r = n.present;
|
|
144
|
+
async function i(e) {
|
|
145
|
+
await r({
|
|
146
|
+
...e,
|
|
147
|
+
delegate: t
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
...n,
|
|
152
|
+
present: i
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function w() {
|
|
156
|
+
return typeof customElements < "u" && (customElements.get("ion-picker-legacy") || import("@ionic/core/components/ion-picker-legacy.js").then((e) => e.defineCustomElement()).catch(() => {}), customElements.get("ion-picker-legacy-column") || import("@ionic/core/components/ion-picker-legacy-column.js").then((e) => e.defineCustomElement()).catch(() => {}), customElements.get("ion-backdrop") || import("@ionic/core/components/ion-backdrop.js").then((e) => e.defineCustomElement()).catch(() => {})), d(o);
|
|
157
|
+
}
|
|
158
|
+
//#endregion
|
|
159
|
+
export { n as actionSheetController, r as alertController, b as confirm, h as createActionSheet, p as createAlert, x as createElurDelegate, m as createLoading, _ as createModal, S as createModalController, w as createPicker, g as createPopover, C as createPopoverController, f as createToast, i as loadingController, a as modalController, o as pickerController, s as popoverController, v as showToast, c as toastController, y as withLoading };
|
|
160
|
+
|
|
161
|
+
//# sourceMappingURL=overlays.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlays.js","names":[],"sources":["../../src/overlays.ts"],"sourcesContent":["/**\n * @elurjs/ionic / overlays.ts\n *\n * Reactive overlay controllers for Elur following the `create*` pattern\n * (`createStore`, `createRouter`, `createForm` → `createToast`, `createAlert`, etc.).\n *\n * Each controller provides:\n * - `presented: Signal<boolean>` — reactive presentation state\n * - `result: Signal<OverlayEventDetail | null>` — dismiss event detail\n * - `present(opts)` — creates + presents the overlay\n * - `dismiss(data?, role?)` — dismisses the active overlay\n * - `dispose()` — transactional cleanup, dismisses if still presented\n *\n * Stale-result protection: only the most recent `present()` call's dismiss\n * event updates the result signal. Latest-wins: presenting a new overlay\n * dismisses the previous one.\n *\n * @example Function component (preferred)\n * ```ts\n * import { signal, html } from \"@elurjs/core\";\n * import { createToast, createAlert } from \"@elurjs/ionic\";\n *\n * function SettingsPage() {\n * // Create overlay controllers — signals close over function scope\n * const toast = createToast();\n * const alert = createAlert();\n *\n * const save = async () => {\n * // Use withLoading for async tasks\n * const ok = await withLoading({ message: \"Saving...\" }, async () => {\n * await fetch(\"/api/settings\", { method: \"POST\" });\n * });\n * toast.present({ message: \"Saved!\", duration: 1500 });\n * };\n *\n * const deleteAccount = () => {\n * // confirm() returns a Promise<boolean>\n * confirm({\n * header: \"Delete account\",\n * message: \"This cannot be undone.\",\n * confirmText: \"Delete\",\n * }).then((yes) => {\n * if (yes) alert.present({ header: \"Done\", message: \"Account deleted\" });\n * });\n * };\n *\n * return html`\n * <ion-content>\n * <ion-button @click=${save}>Save</ion-button>\n * <ion-button color=\"danger\" @click=${deleteAccount}>Delete</ion-button>\n * </ion-content>\n * `;\n * }\n * ```\n *\n * @example Class component with lifecycle cleanup\n * ```ts\n * import { ElurComponent, html, signal } from \"@elurjs/core\";\n * import { createLoading, IonPage } from \"@elurjs/ionic\";\n *\n * class ProfilePage extends IonPage {\n * private loading = createLoading();\n * private data = signal<unknown>(null);\n *\n * override async onMount() {\n * // withLoading auto-dismisses on settle or error\n * const result = await withLoading(\n * { message: \"Loading profile...\" },\n * () => fetch(\"/api/profile\").then(r => r.json()),\n * );\n * this.data.value = result;\n * }\n *\n * override onUnmount() {\n * // Dispose all overlay controllers to prevent leaks\n * this.loading.dispose();\n * }\n *\n * override render() {\n * return html`\n * <ion-content>\n * <p>${() => JSON.stringify(this.data.value)}</p>\n * </ion-content>\n * `;\n * }\n * }\n * ```\n *\n * @example Reactive UI driven by overlay signals\n * ```ts\n * import { signal, html } from \"@elurjs/core\";\n * import { createModal } from \"@elurjs/ionic\";\n *\n * function ProductList() {\n * const modal = createModal();\n * const selected = signal<string | null>(null);\n *\n * const openDetail = (id: string) => {\n * selected.value = id;\n * modal.present({ component: \"product-detail\" });\n * };\n *\n * return html`\n * <ion-content>\n * <ion-list>\n * <ion-button @click=${() => openDetail(\"1\")}>Product 1</ion-button>\n * </ion-list>\n *\n * <!-- Reactively show dismiss result -->\n * ${() => modal.result.value\n * ? html`<p>Modal closed with role: ${modal.result.value.role}</p>`\n * : null}\n *\n * <!-- Reactively disable button while modal is open -->\n * <ion-button\n * disabled=${() => modal.presented.value}\n * @click=${() => openDetail(\"2\")}\n * >Product 2</ion-button>\n * </ion-content>\n * `;\n * }\n * ```\n */\n\nimport { signal, type Signal, mount, type ElurTemplate, type ElurComponent } from \"@elurjs/core\";\nimport {\n toastController,\n alertController,\n loadingController,\n actionSheetController,\n popoverController,\n modalController,\n pickerController,\n} from \"@ionic/core\";\n\n// --- Elur framework delegate for overlays ---\n// Ionic overlays (popover, modal) need a \"framework delegate\" to render\n// content when `component` is not a string tag name or HTMLElement.\n// We provide a delegate that uses Elur `mount()` to render templates.\n\ninterface FrameworkDelegate {\n attachViewToDom: (\n parentElement: HTMLElement,\n userComponent: unknown,\n userComponentProps?: Record<string, unknown>,\n cssClasses?: string[],\n ) => Promise<HTMLElement>;\n removeViewFromDom: (\n parentElement: HTMLElement,\n childElement: HTMLElement,\n ) => Promise<void>;\n}\n\nlet _delegateHandle: { unmount: () => void } | null = null;\n\nconst elurDelegate: FrameworkDelegate = {\n async attachViewToDom(parentElement, userComponent, _props, cssClasses) {\n // If userComponent is a function (ElurTemplate factory), call it and mount\n if (typeof userComponent === \"function\") {\n const template = (userComponent as () => ElurTemplate)();\n const handle = mount(template, parentElement);\n _delegateHandle = handle;\n return parentElement;\n }\n // If userComponent is a string, create the element\n if (typeof userComponent === \"string\") {\n const el = document.createElement(userComponent);\n if (cssClasses) cssClasses.forEach((c) => el.classList.add(c));\n parentElement.appendChild(el);\n return el;\n }\n // If userComponent is an HTMLElement, append it\n if (userComponent instanceof HTMLElement) {\n if (cssClasses) cssClasses.forEach((c) => userComponent.classList.add(c));\n parentElement.appendChild(userComponent);\n return userComponent;\n }\n // Fallback: just return the parent\n return parentElement;\n },\n async removeViewFromDom(_parent, _child) {\n if (_delegateHandle) {\n _delegateHandle.unmount();\n _delegateHandle = null;\n }\n },\n};\n\n// --- Types ---\n\nexport interface OverlayHandle<TDetail = unknown> {\n /** True while the overlay is presented (visible). */\n readonly presented: Signal<boolean>;\n /** The dismiss event detail (role, data) — null until dismissed. */\n readonly result: Signal<TDetail | null>;\n /** Present the overlay with the given options. */\n present(options: Record<string, unknown>): Promise<void>;\n /** Dismiss the active overlay with optional data and role. */\n dismiss(data?: unknown, role?: string): Promise<boolean>;\n /** Cleanup: dismisses any active overlay. Safe to call multiple times. */\n dispose(): void;\n}\n\ninterface ActiveOverlay {\n el: { dismiss: (data?: unknown, role?: string) => Promise<boolean> };\n dismissPromise: Promise<unknown>;\n token: number; // stale-result protection\n}\n\n// --- Internal factory ---\n\nfunction createOverlayHandle<TDetail>(\n controller: {\n create: (opts: Record<string, unknown>) => Promise<any>;\n },\n): OverlayHandle<TDetail> {\n const presented = signal(false);\n const result = signal<TDetail | null>(null);\n let active: ActiveOverlay | null = null;\n let disposed = false;\n\n async function present(options: Record<string, unknown>): Promise<void> {\n if (disposed) return;\n\n // If an overlay is already active, dismiss it first (latest-wins).\n if (active) {\n try {\n await active.el.dismiss();\n } catch { /* ignore */ }\n }\n\n const token = (active?.token ?? 0) + 1;\n\n // If `component` is a function (ElurTemplate factory), inject our\n // framework delegate so Ionic can render Elur templates inside\n // popover/modal overlays.\n const opts = { ...options };\n if (typeof opts.component === \"function\" && !opts.delegate) {\n opts.delegate = elurDelegate;\n }\n\n const el = await controller.create(opts);\n await el.present();\n active = { el, dismissPromise: el.onDidDismiss?.() ?? Promise.resolve(null), token };\n presented.value = true;\n\n // Fire-and-forget the dismiss wait — updates state when the overlay\n // is dismissed by the user or by dismiss(). This avoids blocking the\n // caller; they can use the `presented` and `result` signals to react.\n active.dismissPromise\n .then((detail) => {\n if (active?.token === token && !disposed) {\n result.value = detail as TDetail;\n presented.value = false;\n active = null;\n }\n })\n .catch(() => {\n if (active?.token === token && !disposed) {\n presented.value = false;\n active = null;\n }\n });\n }\n\n async function dismiss(data?: unknown, role?: string): Promise<boolean> {\n if (!active || disposed) return false;\n try {\n return await active.el.dismiss(data, role);\n } catch {\n return false;\n }\n }\n\n function dispose(): void {\n if (disposed) return;\n disposed = true;\n if (active) {\n try {\n active.el.dismiss();\n } catch { /* ignore */ }\n active = null;\n }\n presented.value = false;\n }\n\n return { presented, result, present, dismiss, dispose };\n}\n\n// --- Public factories (create* pattern) ---\n\n/**\n * Create a reactive toast overlay controller.\n * Call `dispose()` in `onUnmount()` when used in a class component.\n */\nexport function createToast(): OverlayHandle {\n return createOverlayHandle(toastController as any);\n}\n\n/**\n * Create a reactive alert overlay controller.\n * Call `dispose()` in `onUnmount()` when used in a class component.\n */\nexport function createAlert(): OverlayHandle {\n return createOverlayHandle(alertController as any);\n}\n\n/**\n * Create a reactive loading overlay controller.\n * Call `dispose()` in `onUnmount()` when used in a class component.\n */\nexport function createLoading(): OverlayHandle {\n return createOverlayHandle(loadingController as any);\n}\n\n/**\n * Create a reactive action-sheet overlay controller.\n * Call `dispose()` in `onUnmount()` when used in a class component.\n */\nexport function createActionSheet(): OverlayHandle {\n return createOverlayHandle(actionSheetController as any);\n}\n\n/**\n * Create a reactive popover overlay controller.\n * Call `dispose()` in `onUnmount()` when used in a class component.\n */\nexport function createPopover(): OverlayHandle {\n return createOverlayHandle(popoverController as any);\n}\n\n/**\n * Create a reactive modal overlay controller.\n * Call `dispose()` in `onUnmount()` when used in a class component.\n */\nexport function createModal(): OverlayHandle {\n return createOverlayHandle(modalController as any);\n}\n\n// --- Convenience: one-shot helpers (no controller needed) ---\n\n/**\n * Present a toast and return immediately (fire-and-forget).\n * The toast auto-dismisses after its duration.\n *\n * @example\n * ```ts\n * import { showToast } from \"@elurjs/ionic\";\n *\n * showToast({ message: \"Saved!\", duration: 1500 });\n * ```\n */\nexport async function showToast(\n options: Record<string, unknown>,\n): Promise<void> {\n const toast = await toastController.create(options);\n await toast.present();\n}\n\n/**\n * Present a loading overlay, run an async task, then dismiss.\n * Returns the task result. If the task throws, the loading is still dismissed.\n *\n * @example\n * ```ts\n * import { withLoading } from \"@elurjs/ionic\";\n *\n * const data = await withLoading(\n * { message: \"Fetching...\" },\n * () => fetch(\"/api/data\").then(r => r.json()),\n * );\n * ```\n */\nexport async function withLoading<T>(\n options: Record<string, unknown>,\n task: () => Promise<T>,\n): Promise<T> {\n const loading = await loadingController.create(options);\n await loading.present();\n try {\n return await task();\n } finally {\n await loading.dismiss();\n }\n}\n\n/**\n * Present a confirm alert and return true if \"OK\" was clicked, false otherwise.\n *\n * @example\n * ```ts\n * import { confirm } from \"@elurjs/ionic\";\n *\n * const yes = await confirm({\n * header: \"Delete\",\n * message: \"Are you sure?\",\n * confirmText: \"Delete\",\n * });\n * if (yes) deleteItem();\n * ```\n */\nexport async function confirm(\n options: { header?: string; message?: string; confirmText?: string; cancelText?: string },\n): Promise<boolean> {\n return new Promise((resolve) => {\n alertController\n .create({\n header: options.header,\n message: options.message,\n buttons: [\n { text: options.cancelText ?? \"Cancel\", role: \"cancel\", handler: () => resolve(false) },\n { text: options.confirmText ?? \"OK\", role: \"confirm\", handler: () => resolve(true) },\n ],\n } as any)\n .then((el) => el.present());\n });\n}\n\n// Re-export Ionic controllers for advanced use\nexport {\n toastController,\n alertController,\n loadingController,\n actionSheetController,\n popoverController,\n modalController,\n pickerController,\n};\n\n// =============================================================================\n// --- Elur delegate for modal/popover content mounting ---\n// =============================================================================\n\n/**\n * A Elur FrameworkDelegate that can mount ElurTemplate or ElurComponent\n * instances inside Ionic overlays (modal, popover).\n *\n * Ionic's `FrameworkDelegate` interface has two methods:\n * - `attachViewToDom(container, component, props, cssClasses)` → HTMLElement\n * - `removeViewFromDom(container, component)` → void\n *\n * For Elur, \"component\" is a function that returns a ElurTemplate or\n * ElurComponent. The delegate creates a wrapper div, mounts the Elur\n * content into it, appends it to the overlay, and tracks the unmount\n * handle for cleanup on dismiss.\n */\nexport interface ElurOverlayDelegate {\n attachViewToDom(\n container: HTMLElement,\n component: () => ElurTemplate | ElurComponent,\n propsOrData?: Record<string, unknown>,\n cssClasses?: string[],\n ): Promise<HTMLElement>;\n removeViewFromDom(container: HTMLElement, component: unknown): Promise<void>;\n}\n\n/**\n * Create a Elur delegate for use with Ionic modal/popover controllers.\n * The delegate mounts Elur templates inside overlays and cleans up on\n * removal.\n *\n * @example\n * ```ts\n * import { createElurDelegate, createModal } from \"@elurjs/ionic\";\n * import { html, signal } from \"@elurjs/core\";\n *\n * const delegate = createElurDelegate();\n * const modal = createModal();\n *\n * // The delegate is passed via `delegate` option\n * modal.present({\n * component: () => html`<ion-content><h1>Hello from modal!</h1></ion-content>`,\n * delegate,\n * });\n * ```\n */\nexport function createElurDelegate(): ElurOverlayDelegate {\n // Track unmount handles by wrapper element for cleanup\n const handles = new Map<HTMLElement, () => void>();\n\n return {\n async attachViewToDom(\n container: HTMLElement,\n component: () => ElurTemplate | ElurComponent,\n _propsOrData?: Record<string, unknown>,\n cssClasses?: string[],\n ): Promise<HTMLElement> {\n // Create a wrapper div to hold the Elur content\n const wrapper = document.createElement(\"div\");\n if (cssClasses) {\n for (const cls of cssClasses) wrapper.classList.add(cls);\n }\n // Mount the Elur content into the wrapper\n const handle = mount(component(), wrapper);\n handles.set(wrapper, handle.unmount);\n // Append to the overlay container\n container.appendChild(wrapper);\n return wrapper;\n },\n\n async removeViewFromDom(_container: HTMLElement, component: unknown): Promise<void> {\n // component is the wrapper element returned by attachViewToDom\n const wrapper = component as HTMLElement;\n const unmount = handles.get(wrapper);\n if (unmount) {\n unmount();\n handles.delete(wrapper);\n }\n if (wrapper.parentElement) {\n wrapper.remove();\n }\n },\n };\n}\n\n// =============================================================================\n// --- Enhanced modal/popover with delegate support ---\n// =============================================================================\n\n/**\n * Options for presenting a modal with Elur content.\n * The `component` function returns a ElurTemplate or ElurComponent that will\n * be mounted inside the modal via the Elur delegate.\n */\nexport interface ModalOptions {\n /** Function returning the Elur content to mount inside the modal. */\n component: () => ElurTemplate | ElurComponent;\n /** Component props/data passed to the delegate. */\n componentProps?: Record<string, unknown>;\n /** CSS classes to add to the mounted content wrapper. */\n cssClasses?: string[];\n /** Modal-specific options. */\n backdropDismiss?: boolean;\n showBackdrop?: boolean;\n animated?: boolean;\n canDismiss?: boolean | (() => Promise<boolean>);\n /** Custom delegate (if not provided, a default Elur delegate is used). */\n delegate?: ElurOverlayDelegate;\n [key: string]: unknown;\n}\n\n/**\n * Options for presenting a popover with Elur content.\n */\nexport interface PopoverOptions {\n /** Function returning the Elur content to mount inside the popover. */\n component: () => ElurTemplate | ElurComponent;\n /** Component props/data passed to the delegate. */\n componentProps?: Record<string, unknown>;\n /** CSS classes to add to the mounted content wrapper. */\n cssClasses?: string[];\n /** The element that the popover should be anchored to. */\n event?: Event | { target: HTMLElement };\n /** Popover-specific options. */\n backdropDismiss?: boolean;\n showBackdrop?: boolean;\n animated?: boolean;\n /** Custom delegate (if not provided, a default Elur delegate is used). */\n delegate?: ElurOverlayDelegate;\n [key: string]: unknown;\n}\n\n/**\n * Enhanced modal controller with Elur delegate support.\n * Unlike the basic `createModal()`, this automatically creates and uses\n * a Elur delegate so `component` can be a ElurTemplate/ElurComponent.\n *\n * @example\n * ```ts\n * import { createModalController, createElurDelegate } from \"@elurjs/ionic\";\n * import { html, signal } from \"@elurjs/core\";\n *\n * const modal = createModalController();\n *\n * modal.present({\n * component: () => html`\n * <ion-header>\n * <ion-toolbar><ion-title>Settings</ion-title></ion-toolbar>\n * </ion-header>\n * <ion-content><p>Modal content here</p></ion-content>\n * `,\n * });\n *\n * // React to dismiss\n * effect(() => {\n * if (modal.result.value) {\n * console.log(\"Modal dismissed:\", modal.result.value);\n * }\n * });\n * ```\n */\nexport function createModalController(delegate?: ElurOverlayDelegate): OverlayHandle {\n const elurDelegate = delegate ?? createElurDelegate();\n const handle = createOverlayHandle(modalController as any);\n\n // Wrap present to inject the delegate and convert component\n const originalPresent = handle.present;\n async function present(options: ModalOptions): Promise<void> {\n await originalPresent({\n ...options,\n delegate: elurDelegate,\n });\n }\n\n return { ...handle, present };\n}\n\n/**\n * Enhanced popover controller with Elur delegate support.\n * Automatically creates and uses a Elur delegate so `component` can be\n * a ElurTemplate/ElurComponent.\n *\n * @example\n * ```ts\n * import { createPopoverController } from \"@elurjs/ionic\";\n * import { html } from \"@elurjs/core\";\n *\n * const popover = createPopoverController();\n *\n * // In an event handler:\n * popover.present({\n * component: () => html`<ion-content><p>Popover content</p></ion-content>`,\n * event, // the click event for anchoring\n * });\n * ```\n */\nexport function createPopoverController(delegate?: ElurOverlayDelegate): OverlayHandle {\n const elurDelegate = delegate ?? createElurDelegate();\n const handle = createOverlayHandle(popoverController as any);\n\n const originalPresent = handle.present;\n async function present(options: PopoverOptions): Promise<void> {\n await originalPresent({\n ...options,\n delegate: elurDelegate,\n });\n }\n\n return { ...handle, present };\n}\n\n// =============================================================================\n// --- Picker wrapper ---\n// =============================================================================\n\n/**\n * Options for a picker column.\n */\nexport interface PickerColumnOption {\n text: string;\n value: string | number;\n disabled?: boolean;\n}\n\n/**\n * Options for presenting a picker overlay.\n */\nexport interface PickerOptions {\n columns: Array<{\n name: string;\n options: PickerColumnOption[];\n selectedIndex?: number;\n }>;\n buttons?: Array<{\n text: string;\n role?: string;\n handler?: (selected: Record<string, PickerColumnOption>) => void;\n }>;\n cssClass?: string;\n animated?: boolean;\n backdropDismiss?: boolean;\n [key: string]: unknown;\n}\n\n/**\n * Create a reactive picker overlay controller.\n * Pickers are column-based selection overlays (like iOS date pickers).\n *\n * @example\n * ```ts\n * import { createPicker } from \"@elurjs/ionic\";\n *\n * const picker = createPicker();\n * await picker.present({\n * columns: [{\n * name: \"size\",\n * options: [\n * { text: \"Small\", value: \"sm\" },\n * { text: \"Medium\", value: \"md\" },\n * { text: \"Large\", value: \"lg\" },\n * ],\n * }],\n * buttons: [\n * { text: \"Cancel\", role: \"cancel\" },\n * { text: \"Done\", role: \"confirm\" },\n * ],\n * });\n * ```\n */\nexport function createPicker(): OverlayHandle {\n // Ionic 8's ion-picker is a wheel-style component without columns/buttons/isOpen.\n // The legacy picker (with columns, buttons, isOpen) is accessed via pickerController,\n // which creates <ion-picker-legacy> internally. These legacy components must be\n // registered as custom elements, otherwise customElements.whenDefined() hangs\n // forever and the picker never appears.\n //\n // We register them lazily here (not in setup.ts) to preserve tree-shaking —\n // apps that don't use the picker won't bundle the legacy picker code.\n if (typeof customElements !== \"undefined\") {\n if (!customElements.get(\"ion-picker-legacy\")) {\n import(\"@ionic/core/components/ion-picker-legacy.js\")\n .then((m) => m.defineCustomElement())\n .catch(() => { });\n }\n if (!customElements.get(\"ion-picker-legacy-column\")) {\n import(\"@ionic/core/components/ion-picker-legacy-column.js\")\n .then((m) => m.defineCustomElement())\n .catch(() => { });\n }\n if (!customElements.get(\"ion-backdrop\")) {\n import(\"@ionic/core/components/ion-backdrop.js\")\n .then((m) => m.defineCustomElement())\n .catch(() => { });\n }\n }\n return createOverlayHandle(pickerController as any);\n}\n"],"mappings":";;;AAyJA,IAAI,IAAkD,MAEhD,IAAkC;CACpC,MAAM,gBAAgB,GAAe,GAAe,GAAQ,GAAY;EAEpE,IAAI,OAAO,KAAkB,YAAY;GACrC,IAAM,IAAY,EAAqC;GAGvD,OADA,IADe,EAAM,GAAU,CACb,GACX;EACX;EAEA,IAAI,OAAO,KAAkB,UAAU;GACnC,IAAM,IAAK,SAAS,cAAc,CAAa;GAG/C,OAFI,KAAY,EAAW,SAAS,MAAM,EAAG,UAAU,IAAI,CAAC,CAAC,GAC7D,EAAc,YAAY,CAAE,GACrB;EACX;EAQA,OANI,aAAyB,eACrB,KAAY,EAAW,SAAS,MAAM,EAAc,UAAU,IAAI,CAAC,CAAC,GACxE,EAAc,YAAY,CAAa,GAChC,KAGJ;CACX;CACA,MAAM,kBAAkB,GAAS,GAAQ;EACrC,AAEI,OADA,EAAgB,QAAQ,GACN;CAE1B;AACJ;AAyBA,SAAS,EACL,GAGsB;CACtB,IAAM,IAAY,EAAO,EAAK,GACxB,IAAS,EAAuB,IAAI,GACtC,IAA+B,MAC/B,IAAW;CAEf,eAAe,EAAQ,GAAiD;EACpE,IAAI,GAAU;EAGd,IAAI,GACA,IAAI;GACA,MAAM,EAAO,GAAG,QAAQ;EAC5B,QAAQ,CAAe;EAG3B,IAAM,KAAS,GAAQ,SAAS,KAAK,GAK/B,IAAO,EAAE,GAAG,EAAQ;EAC1B,AAAI,OAAO,EAAK,aAAc,cAAc,CAAC,EAAK,aAC9C,EAAK,WAAW;EAGpB,IAAM,IAAK,MAAM,EAAW,OAAO,CAAI;EAQvC,AAPA,MAAM,EAAG,QAAQ,GACjB,IAAS;GAAE;GAAI,gBAAgB,EAAG,eAAe,KAAK,QAAQ,QAAQ,IAAI;GAAG;EAAM,GACnF,EAAU,QAAQ,IAKlB,EAAO,eACF,MAAM,MAAW;GACd,AAAI,GAAQ,UAAU,KAAS,CAAC,MAC5B,EAAO,QAAQ,GACf,EAAU,QAAQ,IAClB,IAAS;EAEjB,CAAC,CAAC,CACD,YAAY;GACT,AAAI,GAAQ,UAAU,KAAS,CAAC,MAC5B,EAAU,QAAQ,IAClB,IAAS;EAEjB,CAAC;CACT;CAEA,eAAe,EAAQ,GAAgB,GAAiC;EACpE,IAAI,CAAC,KAAU,GAAU,OAAO;EAChC,IAAI;GACA,OAAO,MAAM,EAAO,GAAG,QAAQ,GAAM,CAAI;EAC7C,QAAQ;GACJ,OAAO;EACX;CACJ;CAEA,SAAS,IAAgB;EACjB,QAEJ;OADA,IAAW,IACP,GAAQ;IACR,IAAI;KACA,EAAO,GAAG,QAAQ;IACtB,QAAQ,CAAe;IACvB,IAAS;GACb;GACA,EAAU,QAAQ;EADlB;CAEJ;CAEA,OAAO;EAAE;EAAW;EAAQ;EAAS;EAAS;CAAQ;AAC1D;AAQA,SAAgB,IAA6B;CACzC,OAAO,EAAoB,CAAsB;AACrD;AAMA,SAAgB,IAA6B;CACzC,OAAO,EAAoB,CAAsB;AACrD;AAMA,SAAgB,IAA+B;CAC3C,OAAO,EAAoB,CAAwB;AACvD;AAMA,SAAgB,IAAmC;CAC/C,OAAO,EAAoB,CAA4B;AAC3D;AAMA,SAAgB,IAA+B;CAC3C,OAAO,EAAoB,CAAwB;AACvD;AAMA,SAAgB,IAA6B;CACzC,OAAO,EAAoB,CAAsB;AACrD;AAeA,eAAsB,EAClB,GACa;CAEb,OAAM,MADc,EAAgB,OAAO,CAAO,EAAA,CACtC,QAAQ;AACxB;AAgBA,eAAsB,EAClB,GACA,GACU;CACV,IAAM,IAAU,MAAM,EAAkB,OAAO,CAAO;CACtD,MAAM,EAAQ,QAAQ;CACtB,IAAI;EACA,OAAO,MAAM,EAAK;CACtB,UAAU;EACN,MAAM,EAAQ,QAAQ;CAC1B;AACJ;AAiBA,eAAsB,EAClB,GACgB;CAChB,OAAO,IAAI,SAAS,MAAY;EAC5B,EACK,OAAO;GACJ,QAAQ,EAAQ;GAChB,SAAS,EAAQ;GACjB,SAAS,CACL;IAAE,MAAM,EAAQ,cAAc;IAAU,MAAM;IAAU,eAAe,EAAQ,EAAK;GAAE,GACtF;IAAE,MAAM,EAAQ,eAAe;IAAM,MAAM;IAAW,eAAe,EAAQ,EAAI;GAAE,CACvF;EACJ,CAAQ,CAAC,CACR,MAAM,MAAO,EAAG,QAAQ,CAAC;CAClC,CAAC;AACL;AA4DA,SAAgB,IAA0C;CAEtD,IAAM,oBAAU,IAAI,IAA6B;CAEjD,OAAO;EACH,MAAM,gBACF,GACA,GACA,GACA,GACoB;GAEpB,IAAM,IAAU,SAAS,cAAc,KAAK;GAC5C,IAAI,GACA,KAAK,IAAM,KAAO,GAAY,EAAQ,UAAU,IAAI,CAAG;GAG3D,IAAM,IAAS,EAAM,EAAU,GAAG,CAAO;GAIzC,OAHA,EAAQ,IAAI,GAAS,EAAO,OAAO,GAEnC,EAAU,YAAY,CAAO,GACtB;EACX;EAEA,MAAM,kBAAkB,GAAyB,GAAmC;GAEhF,IAAM,IAAU,GACV,IAAU,EAAQ,IAAI,CAAO;GAKnC,AAJI,MACA,EAAQ,GACR,EAAQ,OAAO,CAAO,IAEtB,EAAQ,iBACR,EAAQ,OAAO;EAEvB;CACJ;AACJ;AA8EA,SAAgB,EAAsB,GAA+C;CACjF,IAAM,IAAe,KAAY,EAAmB,GAC9C,IAAS,EAAoB,CAAsB,GAGnD,IAAkB,EAAO;CAC/B,eAAe,EAAQ,GAAsC;EACzD,MAAM,EAAgB;GAClB,GAAG;GACH,UAAU;EACd,CAAC;CACL;CAEA,OAAO;EAAE,GAAG;EAAQ;CAAQ;AAChC;AAqBA,SAAgB,EAAwB,GAA+C;CACnF,IAAM,IAAe,KAAY,EAAmB,GAC9C,IAAS,EAAoB,CAAwB,GAErD,IAAkB,EAAO;CAC/B,eAAe,EAAQ,GAAwC;EAC3D,MAAM,EAAgB;GAClB,GAAG;GACH,UAAU;EACd,CAAC;CACL;CAEA,OAAO;EAAE,GAAG;EAAQ;CAAQ;AAChC;AA4DA,SAAgB,IAA8B;CA0B1C,OAjBI,OAAO,iBAAmB,QACrB,eAAe,IAAI,mBAAmB,KACvC,OAAO,8CAA8C,CAChD,MAAM,MAAM,EAAE,oBAAoB,CAAC,CAAC,CACpC,YAAY,CAAE,CAAC,GAEnB,eAAe,IAAI,0BAA0B,KAC9C,OAAO,qDAAqD,CACvD,MAAM,MAAM,EAAE,oBAAoB,CAAC,CAAC,CACpC,YAAY,CAAE,CAAC,GAEnB,eAAe,IAAI,cAAc,KAClC,OAAO,yCAAyC,CAC3C,MAAM,MAAM,EAAE,oBAAoB,CAAC,CAAC,CACpC,YAAY,CAAE,CAAC,IAGrB,EAAoB,CAAuB;AACtD"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(t){if(t===void 0)return!1;if(t===null)return!0;if(typeof t==`function`||typeof t==`symbol`||typeof t==`bigint`||typeof window<`u`&&t instanceof Node||typeof window<`u`&&t instanceof Element||typeof window<`u`&&t instanceof DocumentFragment)return!1;if(typeof t!=`object`)return!0;if(Array.isArray(t))return t.every(e);let n=Object.getPrototypeOf(t);if(n!==null&&n!==Object.prototype)return!1;try{return JSON.stringify(t),!0}catch{return!1}}function t(e){return typeof window>`u`?null:e===`local`?window.localStorage:window.sessionStorage}function n(n,r,i={}){let{storage:a=`session`,namespace:o=`elur-ionic`,keySuffix:s=``}=i,c=`${o}:${n}${s?`:`+s:``}`;function l(){let i=t(a);if(!i)return;let o={},s=!1;for(let[t,i]of Object.entries(r)){let r=i.value;if(!e(r)){typeof console<`u`&&console.warn&&console.warn(`[elur-ionic] PageState: skipping non-serializable value for "${t}" on page "${n}". Only serializable data (primitives, plain arrays, plain objects) can be persisted. DOM nodes, functions, and class instances are not allowed.`);continue}o[t]=r,s=!0}if(s)try{i.setItem(c,JSON.stringify(o))}catch{typeof console<`u`&&console.warn&&console.warn(`[elur-ionic] PageState: failed to save state for page "${n}" (storage quota exceeded or storage disabled).`)}}function u(){let e=t(a);if(!e)return!1;let n=null;try{n=e.getItem(c)}catch{return!1}if(!n)return!1;let i;try{i=JSON.parse(n)}catch{try{e.removeItem(c)}catch{}return!1}for(let[e,t]of Object.entries(r))e in i&&(t.value=i[e]);return!0}function d(){let e=t(a);if(e)try{e.removeItem(c)}catch{}}return{save:l,restore:u,clear:d,get key(){return c}}}function r(e=`session`,n=`elur-ionic`){let r=t(e);if(!r)return;let i=`${n}:`,a=[];try{for(let e=0;e<r.length;e++){let t=r.key(e);t&&t.startsWith(i)&&a.push(t)}for(let e of a)r.removeItem(e)}catch{}}exports.clearAllPageState=r,exports.createPageState=n,exports.isSerializable=e;
|
|
2
|
+
//# sourceMappingURL=page-state.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-state.cjs","names":[],"sources":["../../src/page-state.ts"],"sourcesContent":["/**\n * @elurjs/ionic / page-state.ts\n *\n * Opt-in page-state persistence protocol. Allows pages to save and restore\n * serializable state across navigation, cache eviction, and app reloads.\n *\n * Key design rules:\n * - **Only serializable data** — JSON.stringify is used; DOM nodes, functions,\n * symbols, class instances with methods are rejected.\n * - **Never persist DOM/view instances** — the protocol validates values\n * before storage and throws on non-serializable content.\n * - **Opt-in** — pages must explicitly call `save()` to persist state.\n * - **Per cache key** — state is keyed by route path + params + query,\n * matching the IonRouterOutlet cache key logic.\n * - **Storage choice** — `sessionStorage` (default, cleared on tab close)\n * or `localStorage` (persists across sessions).\n *\n * @example Basic usage in a page component\n * ```ts\n * import { signal, html } from \"@elurjs/core\";\n * import { createPageState, IonPage } from \"@elurjs/ionic\";\n *\n * class SearchPage extends IonPage {\n * private query = signal(\"\");\n * private results = signal<string[]>([]);\n * private pageState = createPageState(\"search\", {\n * // Declare which signals are persistable\n * query: this.query,\n * results: this.results,\n * });\n *\n * override onMount() {\n * // Restore saved state on mount\n * this.pageState.restore();\n * }\n *\n * override onUnmount() {\n * // Save state before leaving\n * this.pageState.save();\n * }\n *\n * override render() {\n * return html`\n * <ion-content>\n * <ion-searchbar value=${() => this.query.value} @input=${(e: any) => {\n * this.query.value = e.target.value;\n * this.pageState.save(); // save on change\n * }}></ion-searchbar>\n * <ion-list>\n * ${() => this.results.value.map(r => html`<ion-item>${r}</ion-item>`)}\n * </ion-list>\n * </ion-content>\n * `;\n * }\n * }\n * ```\n *\n * @example With localStorage (persists across app restarts)\n * ```ts\n * const pageState = createPageState(\"cart\", {\n * items: cartItems,\n * total: cartTotal,\n * }, { storage: \"local\" });\n * ```\n */\n\n// --- Types ---\n\n/** Storage backend selection. */\nexport type StorageBackend = \"session\" | \"local\";\n\n/** Options for page-state persistence. */\nexport interface PageStateOptions {\n /**\n * Storage backend: `\"session\"` (sessionStorage, cleared on tab close)\n * or `\"local\"` (localStorage, persists across sessions).\n * @default \"session\"\n */\n storage?: StorageBackend;\n /**\n * Namespace prefix for storage keys. Defaults to \"elur-ionic\".\n * Useful for multi-app scenarios on the same origin.\n */\n namespace?: string;\n /**\n * Additional key suffix (e.g. user ID) to isolate state between users.\n */\n keySuffix?: string;\n}\n\n/**\n * A map of signal names to signals. Each signal's value must be serializable.\n */\nexport type SignalMap = Record<string, { value: unknown }>;\n\n/**\n * Page-state persistence controller. Created per page instance.\n */\nexport interface PageState {\n /**\n * Save the current state of all declared signals to storage.\n * Only serializable values are stored; non-serializable values are\n * silently skipped (with a console.warn in dev).\n */\n save(): void;\n /**\n * Restore saved state from storage into the declared signals.\n * Returns true if state was found and restored, false otherwise.\n */\n restore(): boolean;\n /**\n * Clear saved state for this page's key.\n */\n clear(): void;\n /**\n * Get the storage key that would be used (for debugging).\n */\n readonly key: string;\n}\n\n// --- Serialization validation ---\n\n/**\n * Check if a value is serializable (can survive JSON.stringify + parse).\n * Returns true for: primitives, plain arrays, plain objects.\n * Returns false for: functions, symbols, DOM nodes, class instances,\n * undefined, circular references.\n */\nfunction isSerializable(value: unknown): boolean {\n if (value === undefined) return false;\n if (value === null) return true;\n if (typeof value === \"function\") return false;\n if (typeof value === \"symbol\") return false;\n if (typeof value === \"bigint\") return false; // JSON.stringify throws on bigint\n\n // DOM nodes and elements\n if (typeof window !== \"undefined\" && value instanceof Node) return false;\n if (typeof window !== \"undefined\" && value instanceof Element) return false;\n if (typeof window !== \"undefined\" && value instanceof DocumentFragment) return false;\n\n // Primitives\n if (typeof value !== \"object\") return true;\n\n // Arrays — check each element\n if (Array.isArray(value)) {\n return value.every(isSerializable);\n }\n\n // Plain objects — check constructor and each value\n const proto = Object.getPrototypeOf(value);\n if (proto !== null && proto !== Object.prototype) {\n // Class instances (has non-trivial prototype) — reject\n return false;\n }\n\n try {\n // Final check: can it survive a round-trip?\n JSON.stringify(value);\n return true;\n } catch {\n return false; // circular reference or other stringify error\n }\n}\n\n// --- Storage abstraction ---\n\nfunction getStorage(backend: StorageBackend): Storage | null {\n if (typeof window === \"undefined\") return null;\n return backend === \"local\" ? window.localStorage : window.sessionStorage;\n}\n\n// --- Factory ---\n\n/**\n * Create a page-state persistence controller.\n *\n * @param pageId Unique identifier for the page (e.g. route path).\n * @param signals Map of signal names to signals whose values should be persisted.\n * @param options Persistence options.\n *\n * @example\n * ```ts\n * const state = createPageState(\"search\", {\n * query: searchQuery,\n * filters: filterSignal,\n * }, { storage: \"local\" });\n *\n * // On page mount:\n * state.restore();\n *\n * // On page leave or data change:\n * state.save();\n * ```\n */\nexport function createPageState(\n pageId: string,\n signals: SignalMap,\n options: PageStateOptions = {},\n): PageState {\n const {\n storage: backend = \"session\",\n namespace = \"elur-ionic\",\n keySuffix = \"\",\n } = options;\n\n const baseKey = `${namespace}:${pageId}${keySuffix ? \":\" + keySuffix : \"\"}`;\n\n function save(): void {\n const storage = getStorage(backend);\n if (!storage) return;\n\n const data: Record<string, unknown> = {};\n let hasData = false;\n\n for (const [name, sig] of Object.entries(signals)) {\n const value = sig.value;\n if (!isSerializable(value)) {\n if (typeof console !== \"undefined\" && console.warn) {\n console.warn(\n `[elur-ionic] PageState: skipping non-serializable value for \"${name}\" ` +\n `on page \"${pageId}\". Only serializable data (primitives, plain arrays, ` +\n `plain objects) can be persisted. DOM nodes, functions, and class ` +\n `instances are not allowed.`,\n );\n }\n continue;\n }\n data[name] = value;\n hasData = true;\n }\n\n if (hasData) {\n try {\n storage.setItem(baseKey, JSON.stringify(data));\n } catch {\n // Quota exceeded or storage disabled — fail silently\n if (typeof console !== \"undefined\" && console.warn) {\n console.warn(\n `[elur-ionic] PageState: failed to save state for page \"${pageId}\" ` +\n `(storage quota exceeded or storage disabled).`,\n );\n }\n }\n }\n }\n\n function restore(): boolean {\n const storage = getStorage(backend);\n if (!storage) return false;\n\n let raw: string | null = null;\n try {\n raw = storage.getItem(baseKey);\n } catch {\n return false; // storage access denied\n }\n\n if (!raw) return false;\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(raw);\n } catch {\n // Corrupted data — clear it\n try { storage.removeItem(baseKey); } catch { /* ignore */ }\n return false;\n }\n\n for (const [name, sig] of Object.entries(signals)) {\n if (name in data) {\n sig.value = data[name];\n }\n }\n\n return true;\n }\n\n function clear(): void {\n const storage = getStorage(backend);\n if (!storage) return;\n try {\n storage.removeItem(baseKey);\n } catch { /* ignore */ }\n }\n\n return {\n save,\n restore,\n clear,\n get key() { return baseKey; },\n };\n}\n\n// --- Batch helpers ---\n\n/**\n * Clear all elur-ionic page-state entries from a storage backend.\n * Useful for logout flows.\n *\n * @example\n * ```ts\n * import { clearAllPageState } from \"@elurjs/ionic\";\n *\n * function logout() {\n * clearAllPageState(); // sessionStorage\n * clearAllPageState(\"local\"); // localStorage\n * }\n * ```\n */\nexport function clearAllPageState(backend: StorageBackend = \"session\", namespace = \"elur-ionic\"): void {\n const storage = getStorage(backend);\n if (!storage) return;\n\n const prefix = `${namespace}:`;\n const keysToRemove: string[] = [];\n\n try {\n for (let i = 0; i < storage.length; i++) {\n const key = storage.key(i);\n if (key && key.startsWith(prefix)) {\n keysToRemove.push(key);\n }\n }\n for (const key of keysToRemove) {\n storage.removeItem(key);\n }\n } catch { /* ignore */ }\n}\n\n// --- Serialization utilities (exported for testing) ---\n\nexport { isSerializable };\n"],"mappings":"mEAgIA,SAAS,EAAe,EAAyB,CAC7C,GAAI,IAAU,IAAA,GAAW,MAAO,GAChC,GAAI,IAAU,KAAM,MAAO,GAQ3B,GAPI,OAAO,GAAU,YACjB,OAAO,GAAU,UACjB,OAAO,GAAU,UAGjB,OAAO,OAAW,KAAe,aAAiB,MAClD,OAAO,OAAW,KAAe,aAAiB,SAClD,OAAO,OAAW,KAAe,aAAiB,iBAAkB,MAAO,GAG/E,GAAI,OAAO,GAAU,SAAU,MAAO,GAGtC,GAAI,MAAM,QAAQ,CAAK,EACnB,OAAO,EAAM,MAAM,CAAc,EAIrC,IAAM,EAAQ,OAAO,eAAe,CAAK,EACzC,GAAI,IAAU,MAAQ,IAAU,OAAO,UAEnC,MAAO,GAGX,GAAI,CAGA,OADA,KAAK,UAAU,CAAK,EACb,EACX,MAAQ,CACJ,MAAO,EACX,CACJ,CAIA,SAAS,EAAW,EAAyC,CAEzD,OADI,OAAO,OAAW,IAAoB,KACnC,IAAY,QAAU,OAAO,aAAe,OAAO,cAC9D,CAyBA,SAAgB,EACZ,EACA,EACA,EAA4B,CAAC,EACpB,CACT,GAAM,CACF,QAAS,EAAU,UACnB,YAAY,aACZ,YAAY,IACZ,EAEE,EAAU,GAAG,EAAU,GAAG,IAAS,EAAY,IAAM,EAAY,KAEvE,SAAS,GAAa,CAClB,IAAM,EAAU,EAAW,CAAO,EAClC,GAAI,CAAC,EAAS,OAEd,IAAM,EAAgC,CAAC,EACnC,EAAU,GAEd,IAAK,GAAM,CAAC,EAAM,KAAQ,OAAO,QAAQ,CAAO,EAAG,CAC/C,IAAM,EAAQ,EAAI,MAClB,GAAI,CAAC,EAAe,CAAK,EAAG,CACpB,OAAO,QAAY,KAAe,QAAQ,MAC1C,QAAQ,KACJ,gEAAgE,EAAK,aACzD,EAAO,iJAGvB,EAEJ,QACJ,CACA,EAAK,GAAQ,EACb,EAAU,EACd,CAEA,GAAI,EACA,GAAI,CACA,EAAQ,QAAQ,EAAS,KAAK,UAAU,CAAI,CAAC,CACjD,MAAQ,CAEA,OAAO,QAAY,KAAe,QAAQ,MAC1C,QAAQ,KACJ,0DAA0D,EAAO,gDAErE,CAER,CAER,CAEA,SAAS,GAAmB,CACxB,IAAM,EAAU,EAAW,CAAO,EAClC,GAAI,CAAC,EAAS,MAAO,GAErB,IAAI,EAAqB,KACzB,GAAI,CACA,EAAM,EAAQ,QAAQ,CAAO,CACjC,MAAQ,CACJ,MAAO,EACX,CAEA,GAAI,CAAC,EAAK,MAAO,GAEjB,IAAI,EACJ,GAAI,CACA,EAAO,KAAK,MAAM,CAAG,CACzB,MAAQ,CAEJ,GAAI,CAAE,EAAQ,WAAW,CAAO,CAAG,MAAQ,CAAe,CAC1D,MAAO,EACX,CAEA,IAAK,GAAM,CAAC,EAAM,KAAQ,OAAO,QAAQ,CAAO,EACxC,KAAQ,IACR,EAAI,MAAQ,EAAK,IAIzB,MAAO,EACX,CAEA,SAAS,GAAc,CACnB,IAAM,EAAU,EAAW,CAAO,EAC7B,KACL,GAAI,CACA,EAAQ,WAAW,CAAO,CAC9B,MAAQ,CAAe,CAC3B,CAEA,MAAO,CACH,OACA,UACA,QACA,IAAI,KAAM,CAAE,OAAO,CAAS,CAChC,CACJ,CAkBA,SAAgB,EAAkB,EAA0B,UAAW,EAAY,aAAoB,CACnG,IAAM,EAAU,EAAW,CAAO,EAClC,GAAI,CAAC,EAAS,OAEd,IAAM,EAAS,GAAG,EAAU,GACtB,EAAyB,CAAC,EAEhC,GAAI,CACA,IAAK,IAAI,EAAI,EAAG,EAAI,EAAQ,OAAQ,IAAK,CACrC,IAAM,EAAM,EAAQ,IAAI,CAAC,EACrB,GAAO,EAAI,WAAW,CAAM,GAC5B,EAAa,KAAK,CAAG,CAE7B,CACA,IAAK,IAAM,KAAO,EACd,EAAQ,WAAW,CAAG,CAE9B,MAAQ,CAAe,CAC3B"}
|