From 8dce794ab497bf3b2ead8d6e3b86e7ab4e80ee37 Mon Sep 17 00:00:00 2001 From: dev Date: Tue, 26 May 2026 19:43:17 +0200 Subject: [PATCH] refactor(pickers): collapse close-* into polymorphic close on 5 picker morfos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirror of the dialog/drawer/popover refactor (book §5.3) applied to the picker family: - color-picker - date-picker - date-range-picker (incl. close-range-commit cause) - time-picker - time-range-picker Each had 4 close-* events with per-event prewrite; collapsed to a single polymorphic `close` event: semantic: { family: 'emerge', verb: 'close', target: v.partRef('calendar'|'clock'|'content'), sequence: 'pre', persistence: 'transient', allowedFamilies: ['emerge', 'commit', 'signal'] } **Behaviour preserved**: the picker providers don't currently fire any `runtime.trigger('close*')` — they just toggle `opts.open = false` and let the popover unmount. The previous `close-*` events were declared but inert (only consumed by schema validator + compiler tests). Refactor is doctrinal alignment, not behavioural change. **Sema cascade**: only color-picker has a sema pack and it did not reference any close-* event (only handle-pick / handle-drag / commit-set). Zero cascade updates needed. **Test fixtures**: compile.test.ts + runtime.svelte.test.ts already migrated to the synthetic `prewriteFixtureMorfo` in the core commit, so this refactor doesn't break them. After this commit the only morfo with the pre-polymorphic per-event prewrite shape is the synthetic test fixture itself — every production morfo is on the polymorphic close. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/uix/morfo/components/color-picker.ts | 66 +++------------- src/uix/morfo/components/date-picker.ts | 74 +++--------------- src/uix/morfo/components/date-range-picker.ts | 77 ++----------------- src/uix/morfo/components/time-picker.ts | 65 ++-------------- src/uix/morfo/components/time-range-picker.ts | 64 ++------------- 5 files changed, 44 insertions(+), 302 deletions(-) diff --git a/src/uix/morfo/components/color-picker.ts b/src/uix/morfo/components/color-picker.ts index 9c1860757..3d5d9d896 100644 --- a/src/uix/morfo/components/color-picker.ts +++ b/src/uix/morfo/components/color-picker.ts @@ -31,66 +31,24 @@ export const colorPickerMorfo = { } }, { - name: 'close-commit', - semantic: { - family: 'commit', - verb: 'save', - target: v.partRef('content'), - sequence: 'post', - intent: 'fulfill' - }, - regime: 'lock', - prewrite: [{ part: v.partRef('content'), attr: 'data-last-action', value: 'committed' }], - commits: { - part: v.partRef('content'), - attr: 'data-state', - value: 'closed' - } - }, - { - name: 'close-cancel', + // Polymorphic close (book §5.3) — mirror of Dialog / Drawer / + // Popover. Provider-set `data-last-action` ('committed' / + // 'cancelled' / 'dismissed' / 'dismissed-outside') discriminates + // the cause; the picker shell's commit/cancel/clear handle uses + // `runtime.trigger('close', { semantic })` if it ever fires + // these events (today the provider just sets `opts.open = + // false` and lets the popover unmount). Persistence transient — + // the popover disappears. + name: 'close', semantic: { family: 'emerge', verb: 'close', target: v.partRef('content'), - sequence: 'pre' - }, - regime: 'lock', - prewrite: [{ part: v.partRef('content'), attr: 'data-last-action', value: 'cancelled' }], - commits: { - part: v.partRef('content'), - attr: 'data-state', - value: 'closed' - } - }, - { - name: 'close-dismiss', - semantic: { - family: 'emerge', - verb: 'dismiss', - target: v.partRef('content'), - sequence: 'pre' - }, - regime: 'lock', - prewrite: [{ part: v.partRef('content'), attr: 'data-last-action', value: 'dismissed' }], - commits: { - part: v.partRef('content'), - attr: 'data-state', - value: 'closed' - } - }, - { - name: 'close-dismiss-outside', - semantic: { - family: 'emerge', - verb: 'dismiss', - target: v.partRef('content'), - sequence: 'pre' + sequence: 'pre', + persistence: 'transient', + allowedFamilies: ['emerge', 'commit', 'signal'] }, regime: 'lock', - prewrite: [ - { part: v.partRef('content'), attr: 'data-last-action', value: 'dismissed-outside' } - ], commits: { part: v.partRef('content'), attr: 'data-state', diff --git a/src/uix/morfo/components/date-picker.ts b/src/uix/morfo/components/date-picker.ts index e6cbe5432..ee0c93b2b 100644 --- a/src/uix/morfo/components/date-picker.ts +++ b/src/uix/morfo/components/date-picker.ts @@ -40,75 +40,23 @@ export const datePickerMorfo = { } }, { - // The user committed a date and the popover closed. - // `fulfill` because the picker's job — pick a date — is done. - name: 'close-commit', - semantic: { - family: 'commit', - verb: 'save', - target: v.partRef('calendar'), - sequence: 'post', - intent: 'fulfill' - }, - regime: 'lock', - prewrite: [{ part: v.partRef('calendar'), attr: 'data-last-action', value: 'committed' }], - commits: { - part: v.partRef('calendar'), - attr: 'data-state', - value: 'closed' - } - }, - { - // Explicit cancel from a Save/Cancel button (modal mode). - name: 'close-cancel', + // Polymorphic close (book §5.3) — mirror of color-picker / + // dialog. Provider-set `data-last-action` ('committed' / + // 'cancelled' / 'dismissed' / 'dismissed-outside') discriminates + // the cause. Today's date-picker provider just toggles + // `opts.open`; if it ever explicitly calls + // `runtime.trigger('close', { semantic })`, the polymorphic + // shape is ready. + name: 'close', semantic: { family: 'emerge', verb: 'close', target: v.partRef('calendar'), - sequence: 'pre' - }, - regime: 'lock', - prewrite: [{ part: v.partRef('calendar'), attr: 'data-last-action', value: 'cancelled' }], - commits: { - part: v.partRef('calendar'), - attr: 'data-state', - value: 'closed' - } - }, - { - // Escape key. - name: 'close-dismiss', - semantic: { - family: 'emerge', - verb: 'dismiss', - target: v.partRef('calendar'), - sequence: 'pre' - }, - regime: 'lock', - prewrite: [{ part: v.partRef('calendar'), attr: 'data-last-action', value: 'dismissed' }], - commits: { - part: v.partRef('calendar'), - attr: 'data-state', - value: 'closed' - } - }, - { - // Click outside the popover. - name: 'close-dismiss-outside', - semantic: { - family: 'emerge', - verb: 'dismiss', - target: v.partRef('calendar'), - sequence: 'pre' + sequence: 'pre', + persistence: 'transient', + allowedFamilies: ['emerge', 'commit', 'signal'] }, regime: 'lock', - prewrite: [ - { - part: v.partRef('calendar'), - attr: 'data-last-action', - value: 'dismissed-outside' - } - ], commits: { part: v.partRef('calendar'), attr: 'data-state', diff --git a/src/uix/morfo/components/date-range-picker.ts b/src/uix/morfo/components/date-range-picker.ts index 9d8d20ae0..f8103338a 100644 --- a/src/uix/morfo/components/date-range-picker.ts +++ b/src/uix/morfo/components/date-range-picker.ts @@ -40,81 +40,20 @@ export const dateRangePickerMorfo = { } }, { - // The user committed a complete range and the popover closed. - // `fulfill` because the picker's job — pick a complete range — is done. - name: 'close-range-commit', - semantic: { - family: 'commit', - verb: 'save', - target: v.partRef('calendar'), - sequence: 'post', - intent: 'fulfill' - }, - regime: 'lock', - prewrite: [ - { part: v.partRef('calendar'), attr: 'data-last-action', value: 'range-committed' } - ], - commits: { - part: v.partRef('calendar'), - attr: 'data-state', - value: 'closed' - } - }, - { - // Explicit cancel from a Save/Cancel button (modal mode). - name: 'close-cancel', + // Polymorphic close (book §5.3). Provider-set `data-last-action` + // values: 'range-committed' (Save when range complete) / + // 'cancelled' (Cancel button) / 'dismissed' (Escape) / + // 'dismissed-outside' (click outside). + name: 'close', semantic: { family: 'emerge', verb: 'close', target: v.partRef('calendar'), - sequence: 'pre' - }, - regime: 'lock', - prewrite: [ - { part: v.partRef('calendar'), attr: 'data-last-action', value: 'cancelled' } - ], - commits: { - part: v.partRef('calendar'), - attr: 'data-state', - value: 'closed' - } - }, - { - // Escape key. - name: 'close-dismiss', - semantic: { - family: 'emerge', - verb: 'dismiss', - target: v.partRef('calendar'), - sequence: 'pre' - }, - regime: 'lock', - prewrite: [ - { part: v.partRef('calendar'), attr: 'data-last-action', value: 'dismissed' } - ], - commits: { - part: v.partRef('calendar'), - attr: 'data-state', - value: 'closed' - } - }, - { - // Click outside the popover. - name: 'close-dismiss-outside', - semantic: { - family: 'emerge', - verb: 'dismiss', - target: v.partRef('calendar'), - sequence: 'pre' + sequence: 'pre', + persistence: 'transient', + allowedFamilies: ['emerge', 'commit', 'signal'] }, regime: 'lock', - prewrite: [ - { - part: v.partRef('calendar'), - attr: 'data-last-action', - value: 'dismissed-outside' - } - ], commits: { part: v.partRef('calendar'), attr: 'data-state', diff --git a/src/uix/morfo/components/time-picker.ts b/src/uix/morfo/components/time-picker.ts index de07139fe..517a11917 100644 --- a/src/uix/morfo/components/time-picker.ts +++ b/src/uix/morfo/components/time-picker.ts @@ -49,70 +49,19 @@ export const timePickerMorfo = { } }, { - name: 'close-commit', - semantic: { - family: 'commit', - verb: 'save', - target: v.partRef('clock'), - sequence: 'post', - intent: 'fulfill' - }, - regime: 'lock', - prewrite: [{ part: v.partRef('clock'), attr: 'data-last-action', value: 'committed' }], - commits: { - part: v.partRef('clock'), - attr: 'data-state', - value: 'closed' - } - }, - { - name: 'close-cancel', + // Polymorphic close (book §5.3). Provider-set `data-last-action` + // ('committed' / 'cancelled' / 'dismissed' / 'dismissed-outside') + // discriminates the cause. + name: 'close', semantic: { family: 'emerge', verb: 'close', target: v.partRef('clock'), - sequence: 'pre' - }, - regime: 'lock', - prewrite: [{ part: v.partRef('clock'), attr: 'data-last-action', value: 'cancelled' }], - commits: { - part: v.partRef('clock'), - attr: 'data-state', - value: 'closed' - } - }, - { - name: 'close-dismiss', - semantic: { - family: 'emerge', - verb: 'dismiss', - target: v.partRef('clock'), - sequence: 'pre' - }, - regime: 'lock', - prewrite: [{ part: v.partRef('clock'), attr: 'data-last-action', value: 'dismissed' }], - commits: { - part: v.partRef('clock'), - attr: 'data-state', - value: 'closed' - } - }, - { - name: 'close-dismiss-outside', - semantic: { - family: 'emerge', - verb: 'dismiss', - target: v.partRef('clock'), - sequence: 'pre' + sequence: 'pre', + persistence: 'transient', + allowedFamilies: ['emerge', 'commit', 'signal'] }, regime: 'lock', - prewrite: [ - { - part: v.partRef('clock'), - attr: 'data-last-action', - value: 'dismissed-outside' - } - ], commits: { part: v.partRef('clock'), attr: 'data-state', diff --git a/src/uix/morfo/components/time-range-picker.ts b/src/uix/morfo/components/time-range-picker.ts index c6fe97299..66934c826 100644 --- a/src/uix/morfo/components/time-range-picker.ts +++ b/src/uix/morfo/components/time-range-picker.ts @@ -50,70 +50,18 @@ export const timeRangePickerMorfo = { } }, { - name: 'close-commit', - semantic: { - family: 'commit', - verb: 'save', - target: v.partRef('clock'), - sequence: 'post', - intent: 'fulfill' - }, - regime: 'lock', - prewrite: [{ part: v.partRef('clock'), attr: 'data-last-action', value: 'committed' }], - commits: { - part: v.partRef('clock'), - attr: 'data-state', - value: 'closed' - } - }, - { - name: 'close-cancel', + // Polymorphic close (book §5.3). Provider-set `data-last-action` + // ('committed' / 'cancelled' / 'dismissed' / 'dismissed-outside'). + name: 'close', semantic: { family: 'emerge', verb: 'close', target: v.partRef('clock'), - sequence: 'pre' - }, - regime: 'lock', - prewrite: [{ part: v.partRef('clock'), attr: 'data-last-action', value: 'cancelled' }], - commits: { - part: v.partRef('clock'), - attr: 'data-state', - value: 'closed' - } - }, - { - name: 'close-dismiss', - semantic: { - family: 'emerge', - verb: 'dismiss', - target: v.partRef('clock'), - sequence: 'pre' - }, - regime: 'lock', - prewrite: [{ part: v.partRef('clock'), attr: 'data-last-action', value: 'dismissed' }], - commits: { - part: v.partRef('clock'), - attr: 'data-state', - value: 'closed' - } - }, - { - name: 'close-dismiss-outside', - semantic: { - family: 'emerge', - verb: 'dismiss', - target: v.partRef('clock'), - sequence: 'pre' + sequence: 'pre', + persistence: 'transient', + allowedFamilies: ['emerge', 'commit', 'signal'] }, regime: 'lock', - prewrite: [ - { - part: v.partRef('clock'), - attr: 'data-last-action', - value: 'dismissed-outside' - } - ], commits: { part: v.partRef('clock'), attr: 'data-state',