@hjmds/react 1.18.0 → 1.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/advanced-display.d.ts.map +1 -1
- package/dist/advanced-display.js +41 -9
- package/dist/advanced-display.js.map +1 -1
- package/dist/advanced-forms.d.ts.map +1 -1
- package/dist/advanced-forms.js +22 -9
- package/dist/advanced-forms.js.map +1 -1
- package/dist/agreement.d.ts.map +1 -1
- package/dist/agreement.js +17 -11
- package/dist/agreement.js.map +1 -1
- package/dist/auth-screen.d.ts.map +1 -1
- package/dist/auth-screen.js +15 -2
- package/dist/auth-screen.js.map +1 -1
- package/dist/avatar-blobatar-motion.d.ts.map +1 -1
- package/dist/avatar-blobatar-motion.js +10 -2
- package/dist/avatar-blobatar-motion.js.map +1 -1
- package/dist/bottom-navigation.d.ts.map +1 -1
- package/dist/bottom-navigation.js +1 -1
- package/dist/bottom-navigation.js.map +1 -1
- package/dist/calendar.d.ts.map +1 -1
- package/dist/calendar.js +10 -2
- package/dist/calendar.js.map +1 -1
- package/dist/carousel-motion.d.ts +1 -0
- package/dist/carousel-motion.d.ts.map +1 -1
- package/dist/carousel-motion.js +4 -3
- package/dist/carousel-motion.js.map +1 -1
- package/dist/carousel.d.ts.map +1 -1
- package/dist/carousel.js +22 -4
- package/dist/carousel.js.map +1 -1
- package/dist/collapsible.d.ts.map +1 -1
- package/dist/collapsible.js +28 -4
- package/dist/collapsible.js.map +1 -1
- package/dist/collection-rail.d.ts.map +1 -1
- package/dist/collection-rail.js +19 -1
- package/dist/collection-rail.js.map +1 -1
- package/dist/color-picker.d.ts.map +1 -1
- package/dist/color-picker.js +12 -4
- package/dist/color-picker.js.map +1 -1
- package/dist/command-palette.d.ts.map +1 -1
- package/dist/command-palette.js +14 -2
- package/dist/command-palette.js.map +1 -1
- package/dist/context-menu.d.ts.map +1 -1
- package/dist/context-menu.js +31 -10
- package/dist/context-menu.js.map +1 -1
- package/dist/date-entry.d.ts +1 -1
- package/dist/date-entry.d.ts.map +1 -1
- package/dist/date-entry.js +20 -7
- package/dist/date-entry.js.map +1 -1
- package/dist/date-picker.d.ts.map +1 -1
- package/dist/date-picker.js +22 -6
- package/dist/date-picker.js.map +1 -1
- package/dist/display.d.ts +5 -1
- package/dist/display.d.ts.map +1 -1
- package/dist/display.js +3 -2
- package/dist/display.js.map +1 -1
- package/dist/duration-field.d.ts +2 -2
- package/dist/duration-field.d.ts.map +1 -1
- package/dist/duration-field.js +11 -3
- package/dist/duration-field.js.map +1 -1
- package/dist/evidence.d.ts +1 -1
- package/dist/evidence.js +1 -1
- package/dist/evidence.js.map +1 -1
- package/dist/feedback.d.ts.map +1 -1
- package/dist/feedback.js +3 -2
- package/dist/feedback.js.map +1 -1
- package/dist/field-group.d.ts +4 -2
- package/dist/field-group.d.ts.map +1 -1
- package/dist/field-group.js +4 -3
- package/dist/field-group.js.map +1 -1
- package/dist/file-picker.d.ts.map +1 -1
- package/dist/file-picker.js +15 -5
- package/dist/file-picker.js.map +1 -1
- package/dist/forms.d.ts +2 -2
- package/dist/forms.js +1 -1
- package/dist/forms.js.map +1 -1
- package/dist/gravity-letters.d.ts.map +1 -1
- package/dist/gravity-letters.js +9 -1
- package/dist/gravity-letters.js.map +1 -1
- package/dist/grid-reveal.d.ts.map +1 -1
- package/dist/grid-reveal.js +9 -1
- package/dist/grid-reveal.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/inline-confirm.d.ts.map +1 -1
- package/dist/inline-confirm.js +25 -4
- package/dist/inline-confirm.js.map +1 -1
- package/dist/internal/document-resource.d.ts.map +1 -1
- package/dist/internal/document-resource.js +2 -2
- package/dist/internal/document-resource.js.map +1 -1
- package/dist/masonry.d.ts.map +1 -1
- package/dist/masonry.js +2 -1
- package/dist/masonry.js.map +1 -1
- package/dist/mentions.d.ts.map +1 -1
- package/dist/mentions.js +12 -4
- package/dist/mentions.js.map +1 -1
- package/dist/menu-morph.js +1 -1
- package/dist/menu-morph.js.map +1 -1
- package/dist/menubar.d.ts.map +1 -1
- package/dist/menubar.js +43 -19
- package/dist/menubar.js.map +1 -1
- package/dist/modal.d.ts.map +1 -1
- package/dist/modal.js +15 -4
- package/dist/modal.js.map +1 -1
- package/dist/navigation.d.ts.map +1 -1
- package/dist/navigation.js +8 -2
- package/dist/navigation.js.map +1 -1
- package/dist/number-field.d.ts.map +1 -1
- package/dist/number-field.js +3 -1
- package/dist/number-field.js.map +1 -1
- package/dist/overlay-stack.d.ts.map +1 -1
- package/dist/overlay-stack.js +23 -6
- package/dist/overlay-stack.js.map +1 -1
- package/dist/overlays.d.ts.map +1 -1
- package/dist/overlays.js +8 -0
- package/dist/overlays.js.map +1 -1
- package/dist/pagination.d.ts.map +1 -1
- package/dist/pagination.js +16 -4
- package/dist/pagination.js.map +1 -1
- package/dist/saved-items.d.ts.map +1 -1
- package/dist/saved-items.js +6 -1
- package/dist/saved-items.js.map +1 -1
- package/dist/screen-flows.d.ts +1 -2
- package/dist/screen-flows.d.ts.map +1 -1
- package/dist/screen-flows.js +120 -16
- package/dist/screen-flows.js.map +1 -1
- package/dist/screens.d.ts.map +1 -1
- package/dist/screens.js +32 -7
- package/dist/screens.js.map +1 -1
- package/dist/select.d.ts.map +1 -1
- package/dist/select.js +6 -1
- package/dist/select.js.map +1 -1
- package/dist/side-panel.d.ts.map +1 -1
- package/dist/side-panel.js +11 -2
- package/dist/side-panel.js.map +1 -1
- package/dist/sidebar.d.ts.map +1 -1
- package/dist/sidebar.js +8 -1
- package/dist/sidebar.js.map +1 -1
- package/dist/skip-nav.d.ts.map +1 -1
- package/dist/skip-nav.js +16 -3
- package/dist/skip-nav.js.map +1 -1
- package/dist/sortable.d.ts.map +1 -1
- package/dist/sortable.js +2 -1
- package/dist/sortable.js.map +1 -1
- package/dist/splitter.d.ts.map +1 -1
- package/dist/splitter.js +23 -11
- package/dist/splitter.js.map +1 -1
- package/dist/statistic-motion.d.ts.map +1 -1
- package/dist/statistic-motion.js +30 -2
- package/dist/statistic-motion.js.map +1 -1
- package/dist/status-text.d.ts +11 -0
- package/dist/status-text.d.ts.map +1 -0
- package/dist/status-text.js +7 -0
- package/dist/status-text.js.map +1 -0
- package/dist/styles.css +14 -4
- package/dist/styles.layered.css +14 -4
- package/dist/supplemental-display.d.ts.map +1 -1
- package/dist/supplemental-display.js +19 -8
- package/dist/supplemental-display.js.map +1 -1
- package/dist/supplemental-navigation.d.ts.map +1 -1
- package/dist/supplemental-navigation.js +10 -1
- package/dist/supplemental-navigation.js.map +1 -1
- package/dist/tags-input.d.ts.map +1 -1
- package/dist/tags-input.js +18 -8
- package/dist/tags-input.js.map +1 -1
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +1 -0
- package/dist/theme.js.map +1 -1
- package/dist/toast.d.ts.map +1 -1
- package/dist/toast.js +31 -16
- package/dist/toast.js.map +1 -1
- package/dist/toggle-group.d.ts.map +1 -1
- package/dist/toggle-group.js +6 -1
- package/dist/toggle-group.js.map +1 -1
- package/dist/tour.d.ts.map +1 -1
- package/dist/tour.js +15 -2
- package/dist/tour.js.map +1 -1
- package/dist/transfer-list.d.ts +1 -1
- package/dist/transfer-list.d.ts.map +1 -1
- package/dist/transfer-list.js +52 -7
- package/dist/transfer-list.js.map +1 -1
- package/dist/tree.d.ts.map +1 -1
- package/dist/tree.js +41 -8
- package/dist/tree.js.map +1 -1
- package/dist/virtual-list.d.ts.map +1 -1
- package/dist/virtual-list.js +44 -7
- package/dist/virtual-list.js.map +1 -1
- package/package.json +8 -3
package/dist/tour.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tour.js","sourceRoot":"","sources":["../src/tour.tsx"],"names":[],"mappings":";AAAA,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,UAAU,EACV,qBAAqB,GAOtB,MAAM,yCAAyC,CAAC;AACjD,OAAO,EACL,UAAU,EACV,WAAW,EACX,SAAS,EACT,KAAK,EACL,MAAM,EACN,QAAQ,GAET,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,aAAa,EAAE,aAAa,EAAuB,MAAM,YAAY,CAAC;AAiBzG;;;;;EAKE;AACF,SAAS,aAAa,CAAC,MAA0B,EAAE,MAAe;IAChE,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAiB,IAAI,CAAC,CAAC;IACvD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;YAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAAC,OAAO;QAAC,CAAC;QAClD,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,qBAAqB,EAAE,CAAC,CAAC;QAC9D,OAAO,EAAE,CAAC;QACV,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC,OAAO,CAAC,CAAC;QAC7C,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACzB,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QACjD,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC3C,OAAO,GAAG,EAAE;YACV,QAAQ,CAAC,UAAU,EAAE,CAAC;YACtB,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;YACpD,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAChD,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACrB,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,CAAC,MAAM,IAAI,GAAG,UAAU,CAAC,SAAS,IAAI,CAC1C,EACE,UAAU,EACV,aAAa,EACb,mBAAmB,EACnB,YAAY,EACZ,IAAI,EAAE,QAAQ,EACd,WAAW,EACX,YAAY,EACZ,OAAO,EACP,eAAe,EACf,SAAS,GACK,EAChB,YAAuC;IAEvC,qBAAqB,CAAC;QACpB,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;QACrD,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;QACrD,GAAG,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC;KACT,CAAC,CAAC;IAClD,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAC,WAAW,IAAI,KAAK,CAAC,CAAC;IACvE,MAAM,IAAI,GAAG,QAAQ,IAAI,YAAY,CAAC;IACtC,MAAM,QAAQ,GAAG,qBAAqB,CAAC,UAAU,EAAE,EAAE,mBAAmB,EAAE,CAAC,CAAC;IAC5E,MAAM,IAAI,GAAG,QAAQ,CAAC,WAAW,CAAC;IAElC,MAAM,OAAO,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAC;IAC7C,MAAM,UAAU,GAAG,MAAM,CAAc,IAAI,CAAC,CAAC;IAC7C,MAAM,SAAS,GAAG,MAAM,CAAqB,IAAI,CAAC,CAAC;IACnD,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAwB,IAAI,CAAC,CAAC;IAC9D,MAAM,EAAE,GAAG,GAAG,KAAK,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC;IACjD,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC1D,SAAS,CAAC,OAAO,GAAG,MAAM,CAAC;IAC3B,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAE/C,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,IAAa,EAAE,MAAsB,EAAE,EAAE;QACnE,IAAI,QAAQ,KAAK,SAAS;YAAE,eAAe,CAAC,IAAI,CAAC,CAAC;QAClD,YAAY,EAAE,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IACnC,CAAC,EAAE,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC;IAC7B,MAAM,KAAK,GAAG,CAAC,MAAuB,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACjE,MAAM,OAAO,GAAG,CAAC,MAAyB,EAAE,EAAE;QAC5C,MAAM,OAAO,GAAG,kBAAkB,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QACvD,6EAA6E;QAC7E,6EAA6E;QAC7E,4BAA4B;QAC5B,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM;YAAE,YAAY,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;aAC7D,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO;YAAE,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3D,CAAC,CAAC;IAEF,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC7B,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IACvB,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAChC,SAAS,CAAC,OAAO,GAAG,KAAK,CAAC;IAC1B,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IAC3B,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,GAAG,CAAC,CAAC;QACnC,QAAQ,CAAC,OAAO,GAAG,KAAK,CAAC;QACzB,OAAO,GAAG,EAAE;YACV,cAAc,CAAC,GAAG,EAAE;gBAClB,sEAAsE;gBACtE,kEAAkE;gBAClE,kEAAkE;gBAClE,IAAI,QAAQ,CAAC,OAAO,KAAK,KAAK,IAAI,CAAC,OAAO,CAAC,OAAO;oBAAE,OAAO;gBAC3D,SAAS,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;YACnC,CAAC,CAAC,CAAC;QACL,CAAC,CAAC;IACJ,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,aAAa,CAAC;QACZ,MAAM,EAAE,IAAI;QACZ,UAAU,EAAE,OAAO;QACnB,2EAA2E;QAC3E,qEAAqE;QACrE,eAAe,EAAE,OAAO;QACxB,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,iBAAiB,EAAE,UAAU,EAAE,CAAC;QACnE,QAAQ,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC;KAChC,CAAC,CAAC;IACH,SAAS,CAAC,GAAG,EAAE;QACb,4EAA4E;QAC5E,2CAA2C;QAC3C,IAAI,IAAI;YAAE,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IAEpB,MAAM,QAAQ,GAAG,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE;QAC/D,GAAG,CAAC,IAAI,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC;QACtE,GAAG,CAAC,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;QAC1D,GAAG,EAAE,UAAU,CAAC,UAAU;QAC1B,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC;QAC5B,YAAY,EAAE,IAAI;KACnB,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,WAAW,CAAC,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;IAE1F,MAAM,eAAe,GAAG,OAAO,KAAK,SAAS;QAC3C,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,aAAa,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC;IAC1F,IAAI,CAAC,IAAI;QAAE,OAAO,eAAe,CAAC;IAClC,OAAO,CACL,8BACG,eAAe,EAChB,KAAC,SAAS,OAAK,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,eAAe,EAAE,CAAC,YAKlF,cAAK,SAAS,EAAC,mBAAmB,gBAAY,MAAM,EAAC,KAAK,EAAE,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,YACrF,UAAU,CAAC,CAAC,CAAC,CACZ,cACE,SAAS,EAAC,oBAAoB,iBAClB,MAAM,EAClB,KAAK,EAAE;4BACL,eAAe,EAAE,UAAU,CAAC,GAAG;4BAC/B,gBAAgB,EAAE,UAAU,CAAC,IAAI;4BACjC,UAAU,EAAE,UAAU,CAAC,KAAK;4BAC5B,SAAS,EAAE,UAAU,CAAC,MAAM;yBACZ,GAClB,CACH,CAAC,CAAC,CAAC,IAAI,GACJ,GACI,EACZ,KAAC,cAAc,IAAC,SAAS,EAAE,SAAS,KAAM,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,YAC/F,eACE,GAAG,EAAE,QAAQ,EACb,EAAE,EAAE,EAAE,EACN,IAAI,EAAC,QAAQ,gBACF,MAAM,gBACL,QAAQ,CAAC,kBAAkB,sBACrB,GAAG,EAAE,eAAe,EACtC,QAAQ,EAAE,CAAC,CAAC,4BACW,EAAE,oBACT,QAAQ,CAAC,SAAS,gBACtB,QAAQ,CAAC,KAAK,EAC1B,SAAS,EAAE,UAAU,CAAC,UAAU,EAAE,SAAS,CAAC,EAC5C,KAAK,EAAE,EAAE,GAAG,QAAQ,CAAC,KAAK,EAAE,aAAa,EAAE,UAAU,CAAC,QAAQ,EAAmB,aAOjF,eAAM,EAAE,EAAE,GAAG,EAAE,eAAe,EAAE,SAAS,EAAC,qBAAqB,YAAE,IAAI,CAAC,YAAY,GAAQ,EAC1F,8BAAiB,MAAM,aACrB,aAAG,SAAS,EAAC,mBAAmB,aAAE,IAAI,CAAC,QAAQ,SAAK,IAAI,CAAC,KAAK,IAAK,EACnE,aAAI,SAAS,EAAC,iBAAiB,YAAE,IAAI,CAAC,KAAK,GAAM,EACjD,YAAG,SAAS,EAAC,uBAAuB,YAAE,IAAI,CAAC,WAAW,GAAK,IACvD,EACN,eAAK,SAAS,EAAC,mBAAmB,aAChC,KAAC,MAAM,IAAC,IAAI,EAAC,OAAO,EAAC,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,YAAG,UAAU,CAAC,MAAM,CAAC,IAAI,GAAU,EAOpF,KAAC,MAAM,IAAC,IAAI,EAAC,WAAW,mBAAgB,QAAQ,CAAC,WAAW,IAAI,SAAS,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,YAC1G,UAAU,CAAC,MAAM,CAAC,QAAQ,GACpB,EACT,KAAC,MAAM,IAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,YACnC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,GAC/D,IACL,IACF,GACS,IAChB,CACJ,CAAC;AACJ,CAAC,CAE6B,CAAC","sourcesContent":["import {\n resolveTourAdvance,\n resolveTourDescriptor,\n tourRecipe,\n validateTourOpenState,\n type ComposeTourStepAnnouncement,\n type TourAdvanceReason,\n type TourCloseReason,\n type TourDescriptor,\n type TourOpenReason,\n type TourStepChangeHandler,\n} from \"@hjmds/design-contracts/components/tour\";\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n type CSSProperties,\n} from \"react\";\nimport { Button } from \"./actions.js\";\nimport { classNames, composeRefs } from \"./internal.js\";\nimport { AnchoredPortal, useAnchoredPopup } from \"./portal.js\";\nimport { getModalLayer, HjmPortal, renderTrigger, useModalFocus, type OverlayTrigger } from \"./modal.js\";\n\n// No `layoutStyle`: the card is an anchored portal that follows each step's anchor; only the caller's trigger is in flow.\nexport type TourProps<Id extends string = string> = Readonly<{\n descriptor: TourDescriptor<Id>;\n /** Resolves the product-owned anchor key to the element to point at. */\n resolveAnchor: (anchorId: string) => HTMLElement | null;\n composeAnnouncement: ComposeTourStepAnnouncement;\n onStepChange: TourStepChangeHandler<Id>;\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean, detail: Readonly<{ reason: TourOpenReason }>) => void;\n trigger?: OverlayTrigger;\n portalContainer?: HTMLElement;\n className?: string;\n}>;\n\n/*\n The contract hands renderers an opaque `anchorId`, never a node, so the\n product owns what each step points at. This component therefore keeps its own\n element box: it re-reads the anchor on every step and on layout changes rather\n than storing a rect the product could invalidate by re-rendering the page.\n*/\nfunction useAnchorRect(anchor: HTMLElement | null, active: boolean): DOMRect | null {\n const [rect, setRect] = useState<DOMRect | null>(null);\n useEffect(() => {\n if (!active || !anchor) { setRect(null); return; }\n const measure = () => setRect(anchor.getBoundingClientRect());\n measure();\n const observer = new ResizeObserver(measure);\n observer.observe(anchor);\n window.addEventListener(\"scroll\", measure, true);\n window.addEventListener(\"resize\", measure);\n return () => {\n observer.disconnect();\n window.removeEventListener(\"scroll\", measure, true);\n window.removeEventListener(\"resize\", measure);\n };\n }, [anchor, active]);\n return rect;\n}\n\nexport const Tour = forwardRef(function Tour<Id extends string = string>(\n {\n descriptor,\n resolveAnchor,\n composeAnnouncement,\n onStepChange,\n open: openProp,\n defaultOpen,\n onOpenChange,\n trigger,\n portalContainer,\n className,\n }: TourProps<Id>,\n forwardedRef: React.Ref<HTMLDivElement>,\n) {\n validateTourOpenState({\n ...(openProp === undefined ? {} : { open: openProp }),\n ...(defaultOpen === undefined ? {} : { defaultOpen }),\n ...(onOpenChange === undefined ? {} : { onOpenChange }),\n } as Parameters<typeof validateTourOpenState>[0]);\n const [internalOpen, setInternalOpen] = useState(defaultOpen ?? false);\n const open = openProp ?? internalOpen;\n const resolved = resolveTourDescriptor(descriptor, { composeAnnouncement });\n const step = resolved.currentStep;\n\n const cardRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLElement>(null);\n const anchorRef = useRef<HTMLElement | null>(null);\n const [card, setCard] = useState<HTMLDivElement | null>(null);\n const id = `${useId().replaceAll(\":\", \"\")}-tour`;\n const anchor = open ? resolveAnchor(step.anchorId) : null;\n anchorRef.current = anchor;\n const anchorRect = useAnchorRect(anchor, open);\n\n const change = useCallback((next: boolean, reason: TourOpenReason) => {\n if (openProp === undefined) setInternalOpen(next);\n onOpenChange?.(next, { reason });\n }, [openProp, onOpenChange]);\n const close = (reason: TourCloseReason) => change(false, reason);\n const advance = (reason: TourAdvanceReason) => {\n const outcome = resolveTourAdvance(descriptor, reason);\n // `no-op` is Previous on the first step: the contract makes that a decision,\n // not a removed control. The button renders `aria-disabled` (below) and this\n // branch simply ignores it.\n if (outcome.type === \"step\") onStepChange(outcome.stepId, reason);\n else if (outcome.type === \"close\") close(outcome.reason);\n };\n\n const openRef = useRef(open);\n openRef.current = open;\n const settleRef = useRef(close);\n settleRef.current = close;\n const epochRef = useRef(0);\n useEffect(() => {\n const epoch = epochRef.current + 1;\n epochRef.current = epoch;\n return () => {\n queueMicrotask(() => {\n // StrictMode re-runs setup right after its probe cleanup; only a real\n // unmount has no later epoch. An open tour torn down that way was\n // interrupted — the user never skipped, escaped, or completed it.\n if (epochRef.current !== epoch || !openRef.current) return;\n settleRef.current(\"interrupted\");\n });\n };\n }, []);\n\n useModalFocus({\n active: open,\n contentRef: cardRef,\n // The card itself, not its first button: it carries the step announcement,\n // which is the whole substitute for a sighted user's visual pointer.\n initialFocusRef: cardRef,\n ...(trigger === undefined ? {} : { fallbackReturnRef: triggerRef }),\n onEscape: () => close(\"escape\"),\n });\n useEffect(() => {\n // Focus follows the card on every step change, never the anchor: the anchor\n // is inert background while the tour runs.\n if (open) cardRef.current?.focus({ preventScroll: true });\n }, [open, step.id]);\n\n const position = useAnchoredPopup(anchorRef, open ? card : null, {\n ...(step.placement === undefined ? {} : { placement: step.placement }),\n ...(step.align === undefined ? {} : { align: step.align }),\n gap: tourRecipe.sideOffset,\n zIndex: getModalLayer(0) + 1,\n fallbackAxis: true,\n });\n const cardRefs = useCallback(composeRefs(setCard, cardRef, forwardedRef), [forwardedRef]);\n\n const renderedTrigger = trigger === undefined\n ? null\n : renderTrigger(trigger, triggerRef, open, id, \"dialog\", () => change(true, \"trigger\"));\n if (!open) return renderedTrigger;\n return (\n <>\n {renderedTrigger}\n <HjmPortal {...(portalContainer === undefined ? {} : { container: portalContainer })}>\n {/*\n No dismiss handler on the veil: the contract has no `outside` close\n reason, so an accidental pointer must not end a walkthrough.\n */}\n <div className=\"hjm-tour-backdrop\" data-state=\"open\" style={{ zIndex: getModalLayer(0) }}>\n {anchorRect ? (\n <div\n className=\"hjm-tour-highlight\"\n aria-hidden=\"true\"\n style={{\n insetBlockStart: anchorRect.top,\n insetInlineStart: anchorRect.left,\n inlineSize: anchorRect.width,\n blockSize: anchorRect.height,\n } as CSSProperties}\n />\n ) : null}\n </div>\n </HjmPortal>\n <AnchoredPortal anchorRef={anchorRef} {...(portalContainer ? { container: portalContainer } : {})}>\n <div\n ref={cardRefs}\n id={id}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={resolved.accessibilityLabel}\n aria-describedby={`${id}-announcement`}\n tabIndex={-1}\n data-hjm-modal-content=\"\"\n data-placement={position.placement}\n data-align={position.align}\n className={classNames(\"hjm-tour\", className)}\n style={{ ...position.style, maxInlineSize: tourRecipe.maxWidth } as CSSProperties}\n >\n {/*\n The composed announcement already carries title, description, and\n position, so the visible copy is hidden from assistive technology\n instead of being read twice.\n */}\n <span id={`${id}-announcement`} className=\"hjm-visually-hidden\">{step.announcement}</span>\n <div aria-hidden=\"true\">\n <p className=\"hjm-tour__counter\">{step.position} / {step.total}</p>\n <h2 className=\"hjm-tour__title\">{step.title}</h2>\n <p className=\"hjm-tour__description\">{step.description}</p>\n </div>\n <div className=\"hjm-tour__actions\">\n <Button tone=\"ghost\" onClick={() => close(\"skip\")}>{descriptor.labels.skip}</Button>\n {/*\n `aria-disabled`, not `disabled`: a native disabled button drops focus to\n <body> when the user arrives back on the first step with Previous, and\n leaves a hole in the card's tab order. Button keeps an aria-disabled\n control focusable and swallows the press (the Carousel boundary pattern).\n */}\n <Button tone=\"secondary\" aria-disabled={resolved.isFirstStep || undefined} onClick={() => advance(\"previous\")}>\n {descriptor.labels.previous}\n </Button>\n <Button onClick={() => advance(\"next\")}>\n {resolved.isLastStep ? descriptor.labels.done : descriptor.labels.next}\n </Button>\n </div>\n </div>\n </AnchoredPortal>\n </>\n );\n}) as <Id extends string = string>(\n props: TourProps<Id> & { ref?: React.Ref<HTMLDivElement> },\n) => React.ReactElement | null;\n"]}
|
|
1
|
+
{"version":3,"file":"tour.js","sourceRoot":"","sources":["../src/tour.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n resolveTourAdvance,\n resolveTourDescriptor,\n tourRecipe,\n validateTourOpenState,\n type ComposeTourStepAnnouncement,\n type TourAdvanceReason,\n type TourCloseReason,\n type TourDescriptor,\n type TourOpenReason,\n type TourStepChangeHandler,\n} from \"@hjmds/design-contracts/components/tour\";\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n type CSSProperties,\n} from \"react\";\nimport { Button } from \"./actions.js\";\nimport { classNames, composeRefs } from \"./internal.js\";\nimport { AnchoredPortal, useAnchoredPopup } from \"./portal.js\";\nimport { getModalLayer, HjmPortal, renderTrigger, useModalFocus, type OverlayTrigger } from \"./modal.js\";\n\n// No `layoutStyle`: the card is an anchored portal that follows each step's anchor; only the caller's trigger is in flow.\nexport type TourProps<Id extends string = string> = Readonly<{\n descriptor: TourDescriptor<Id>;\n /** Resolves the product-owned anchor key to the element to point at. */\n resolveAnchor: (anchorId: string) => HTMLElement | null;\n composeAnnouncement: ComposeTourStepAnnouncement;\n onStepChange: TourStepChangeHandler<Id>;\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean, detail: Readonly<{ reason: TourOpenReason }>) => void;\n trigger?: OverlayTrigger;\n portalContainer?: HTMLElement;\n className?: string;\n}>;\n\n/*\n The contract hands renderers an opaque `anchorId`, never a node, so the\n product owns what each step points at. This component therefore keeps its own\n element box: it re-reads the anchor on every step and on layout changes rather\n than storing a rect the product could invalidate by re-rendering the page.\n*/\nfunction useAnchorRect(anchor: HTMLElement | null, active: boolean): DOMRect | null {\n const [rect, setRect] = useState<DOMRect | null>(null);\n useEffect(() => {\n if (!active || !anchor) { setRect(null); return; }\n const measure = () => setRect(anchor.getBoundingClientRect());\n measure();\n const observer = new ResizeObserver(measure);\n observer.observe(anchor);\n window.addEventListener(\"scroll\", measure, true);\n window.addEventListener(\"resize\", measure);\n return () => {\n observer.disconnect();\n window.removeEventListener(\"scroll\", measure, true);\n window.removeEventListener(\"resize\", measure);\n };\n }, [anchor, active]);\n return rect;\n}\n\nexport const Tour = forwardRef(function Tour<Id extends string = string>(\n {\n descriptor,\n resolveAnchor,\n composeAnnouncement,\n onStepChange,\n open: openProp,\n defaultOpen,\n onOpenChange,\n trigger,\n portalContainer,\n className,\n }: TourProps<Id>,\n forwardedRef: React.Ref<HTMLDivElement>,\n) {\n validateTourOpenState({\n ...(openProp === undefined ? {} : { open: openProp }),\n ...(defaultOpen === undefined ? {} : { defaultOpen }),\n ...(onOpenChange === undefined ? {} : { onOpenChange }),\n } as Parameters<typeof validateTourOpenState>[0]);\n const [internalOpen, setInternalOpen] = useState(defaultOpen ?? false);\n const open = openProp ?? internalOpen;\n const resolved = resolveTourDescriptor(descriptor, { composeAnnouncement });\n const step = resolved.currentStep;\n\n const cardRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLElement>(null);\n const anchorRef = useRef<HTMLElement | null>(null);\n const [card, setCard] = useState<HTMLDivElement | null>(null);\n const id = `${useId().replaceAll(\":\", \"\")}-tour`;\n const anchor = open ? resolveAnchor(step.anchorId) : null;\n anchorRef.current = anchor;\n const anchorRect = useAnchorRect(anchor, open);\n const [, refreshAnchor] = useState(0);\n useEffect(() => {\n if (!open) return;\n // Product children may mount independently of the tour owner; re-resolve identity after DOM replacement.\n const observer = new MutationObserver(() => {\n if (resolveAnchor(step.anchorId) !== anchorRef.current) refreshAnchor(value => value + 1);\n });\n observer.observe(document.body, { childList: true, subtree: true });\n return () => observer.disconnect();\n }, [open, resolveAnchor, step.anchorId]);\n\n const change = useCallback((next: boolean, reason: TourOpenReason) => {\n if (openProp === undefined) setInternalOpen(next);\n onOpenChange?.(next, { reason });\n }, [openProp, onOpenChange]);\n const close = (reason: TourCloseReason) => change(false, reason);\n const advance = (reason: TourAdvanceReason) => {\n const outcome = resolveTourAdvance(descriptor, reason);\n // `no-op` is Previous on the first step: the contract makes that a decision,\n // not a removed control. The button renders `aria-disabled` (below) and this\n // branch simply ignores it.\n if (outcome.type === \"step\") onStepChange(outcome.stepId, reason);\n else if (outcome.type === \"close\") close(outcome.reason);\n };\n\n const openRef = useRef(open);\n openRef.current = open;\n const settleRef = useRef(close);\n settleRef.current = close;\n const epochRef = useRef(0);\n useEffect(() => {\n const epoch = epochRef.current + 1;\n epochRef.current = epoch;\n return () => {\n queueMicrotask(() => {\n // StrictMode re-runs setup right after its probe cleanup; only a real\n // unmount has no later epoch. An open tour torn down that way was\n // interrupted — the user never skipped, escaped, or completed it.\n if (epochRef.current !== epoch || !openRef.current) return;\n settleRef.current(\"interrupted\");\n });\n };\n }, []);\n\n useModalFocus({\n active: open,\n contentRef: cardRef,\n // The card itself, not its first button: it carries the step announcement,\n // which is the whole substitute for a sighted user's visual pointer.\n initialFocusRef: cardRef,\n ...(trigger === undefined ? {} : { fallbackReturnRef: triggerRef }),\n onEscape: () => close(\"escape\"),\n });\n useEffect(() => {\n // Focus follows the card on every step change, never the anchor: the anchor\n // is inert background while the tour runs.\n if (open) cardRef.current?.focus({ preventScroll: true });\n }, [open, step.id]);\n\n const position = useAnchoredPopup(anchorRef, open ? card : null, {\n ...(step.placement === undefined ? {} : { placement: step.placement }),\n ...(step.align === undefined ? {} : { align: step.align }),\n gap: tourRecipe.sideOffset,\n zIndex: getModalLayer(0) + 1,\n fallbackAxis: true,\n });\n const cardRefs = useCallback(composeRefs(setCard, cardRef, forwardedRef), [forwardedRef]);\n\n const renderedTrigger = trigger === undefined\n ? null\n : renderTrigger(trigger, triggerRef, open, id, \"dialog\", () => change(true, \"trigger\"));\n if (!open) return renderedTrigger;\n return (\n <>\n {renderedTrigger}\n <HjmPortal {...(portalContainer === undefined ? {} : { container: portalContainer })}>\n {/*\n No dismiss handler on the veil: the contract has no `outside` close\n reason, so an accidental pointer must not end a walkthrough.\n */}\n <div className=\"hjm-tour-backdrop\" data-state=\"open\" style={{ zIndex: getModalLayer(0) }}>\n {anchorRect ? (\n <div\n className=\"hjm-tour-highlight\"\n aria-hidden=\"true\"\n style={{\n // DOMRect uses physical coordinates even when the provider direction is RTL.\n top: anchorRect.top,\n left: anchorRect.left,\n inlineSize: anchorRect.width,\n blockSize: anchorRect.height,\n } as CSSProperties}\n />\n ) : null}\n </div>\n </HjmPortal>\n <AnchoredPortal anchorRef={anchorRef} {...(portalContainer ? { container: portalContainer } : {})}>\n <div\n ref={cardRefs}\n id={id}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={resolved.accessibilityLabel}\n aria-describedby={`${id}-announcement`}\n tabIndex={-1}\n data-hjm-modal-content=\"\"\n data-placement={position.placement}\n data-align={position.align}\n className={classNames(\"hjm-tour\", className)}\n style={{ ...position.style, maxInlineSize: tourRecipe.maxWidth } as CSSProperties}\n >\n {/*\n The composed announcement already carries title, description, and\n position, so the visible copy is hidden from assistive technology\n instead of being read twice.\n */}\n <span id={`${id}-announcement`} className=\"hjm-visually-hidden\">{step.announcement}</span>\n <div aria-hidden=\"true\">\n <p className=\"hjm-tour__counter\">{step.position} / {step.total}</p>\n <h2 className=\"hjm-tour__title\">{step.title}</h2>\n <p className=\"hjm-tour__description\">{step.description}</p>\n </div>\n <div className=\"hjm-tour__actions\">\n <Button tone=\"ghost\" onClick={() => close(\"skip\")}>{descriptor.labels.skip}</Button>\n {/*\n `aria-disabled`, not `disabled`: a native disabled button drops focus to\n <body> when the user arrives back on the first step with Previous, and\n leaves a hole in the card's tab order. Button keeps an aria-disabled\n control focusable and swallows the press (the Carousel boundary pattern).\n */}\n <Button tone=\"secondary\" aria-disabled={resolved.isFirstStep || undefined} onClick={() => advance(\"previous\")}>\n {descriptor.labels.previous}\n </Button>\n <Button onClick={() => advance(\"next\")}>\n {resolved.isLastStep ? descriptor.labels.done : descriptor.labels.next}\n </Button>\n </div>\n </div>\n </AnchoredPortal>\n </>\n );\n}) as <Id extends string = string>(\n props: TourProps<Id> & { ref?: React.Ref<HTMLDivElement> },\n) => React.ReactElement | null;\n"]}
|
package/dist/transfer-list.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type TransferListMoveDirection } from "@hjmds/design-contracts/components/transfer-list";
|
|
2
|
-
import type
|
|
2
|
+
import { type SelectItemDescriptor } from "@hjmds/design-contracts/behaviors";
|
|
3
3
|
import type { HjmCompositionStyleProp } from "./composition-style.js";
|
|
4
4
|
export type TransferListLabels = Readonly<{
|
|
5
5
|
source: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transfer-list.d.ts","sourceRoot":"","sources":["../src/transfer-list.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"transfer-list.d.ts","sourceRoot":"","sources":["../src/transfer-list.tsx"],"names":[],"mappings":"AAAA,OAAO,EASL,KAAK,yBAAyB,EAG/B,MAAM,kDAAkD,CAAC;AAC1D,OAAO,EAA8B,KAAK,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAY1G,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAEtE,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC,CAAC;AAEH,MAAM,MAAM,iBAAiB,CAAC,EAAE,SAAS,MAAM,GAAG,MAAM,IAAI,QAAQ,CAAC;IACnE,KAAK,EAAE,SAAS,oBAAoB,CAAC,EAAE,CAAC,EAAE,CAAC;IAC3C,MAAM,EAAE,kBAAkB,CAAC;IAC3B,UAAU,CAAC,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;IAC7B,iBAAiB,CAAC,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;IACpC,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC;IACrD,oFAAoF;IACpF,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,yBAAyB,KAAK,IAAI,CAAC;IACjF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gGAAgG;IAChG,WAAW,CAAC,EAAE,uBAAuB,CAAC;CACvC,CAAC,CAAC;AAOH,eAAO,MAAM,YAAY,EAiMnB,CAAC,EAAE,SAAS,MAAM,GAAG,MAAM,EAC/B,KAAK,EAAE,iBAAiB,CAAC,EAAE,CAAC,GAAG;IAAE,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;CAAE,KAC/D,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC"}
|
package/dist/transfer-list.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { moveTransferListSelection, reconcileTransferListSelection, resolveTransferListFocusAfterMove, resolveTransferListPanels, resolveTransferListSelectAllState, toggleTransferListSelectAll, toggleTransferListSelection, } from "@hjmds/design-contracts/components/transfer-list";
|
|
3
|
-
import {
|
|
2
|
+
import { moveTransferListSelection, reconcileTransferListSelection, resolveTransferListFocusAfterMove, resolveTransferListPanels, resolveTransferListSelectAllState, toggleTransferListSelectAll, toggleTransferListSelection, validateTransferListDescriptor, } from "@hjmds/design-contracts/components/transfer-list";
|
|
3
|
+
import { reconcileCheckboxSelection } from "@hjmds/design-contracts/behaviors";
|
|
4
|
+
import { forwardRef, useEffect, useLayoutEffect, useMemo, useRef, useState, } from "react";
|
|
4
5
|
import { Button } from "./actions.js";
|
|
5
6
|
import { classNames, composeRefs, useControllableState } from "./internal.js";
|
|
6
7
|
const emptySelection = () => ({
|
|
@@ -8,18 +9,53 @@ const emptySelection = () => ({
|
|
|
8
9
|
target: new Set(),
|
|
9
10
|
});
|
|
10
11
|
export const TransferList = forwardRef(function TransferList({ items, labels, targetKeys: controlledTargetKeys, defaultTargetKeys, onTargetKeysChange, onMove, className, layoutStyle, }, forwardedRef) {
|
|
11
|
-
const [
|
|
12
|
+
const [storedTargetKeys, setTargetKeys] = useControllableState({
|
|
12
13
|
...(controlledTargetKeys === undefined ? {} : { value: controlledTargetKeys }),
|
|
13
14
|
defaultValue: defaultTargetKeys ?? new Set(),
|
|
14
15
|
...(onTargetKeysChange === undefined ? {} : { onChange: onTargetKeysChange }),
|
|
15
16
|
});
|
|
17
|
+
const validatedInitialTargets = useRef(false);
|
|
18
|
+
if (!validatedInitialTargets.current) {
|
|
19
|
+
validateTransferListDescriptor({ items, targetKeys: storedTargetKeys });
|
|
20
|
+
validatedInitialTargets.current = true;
|
|
21
|
+
}
|
|
22
|
+
const targetKeys = useMemo(() => controlledTargetKeys === undefined
|
|
23
|
+
? reconcileCheckboxSelection(items, storedTargetKeys) : storedTargetKeys, [controlledTargetKeys, items, storedTargetKeys]);
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (controlledTargetKeys === undefined && targetKeys !== storedTargetKeys)
|
|
26
|
+
setTargetKeys(targetKeys);
|
|
27
|
+
}, [controlledTargetKeys, setTargetKeys, storedTargetKeys, targetKeys]);
|
|
16
28
|
const [rawSelection, setSelection] = useState((emptySelection));
|
|
17
29
|
const descriptor = useMemo(() => ({ items, targetKeys }), [items, targetKeys]);
|
|
18
30
|
// A row that left the list entirely must not keep a pending check mark.
|
|
19
31
|
const selection = useMemo(() => reconcileTransferListSelection(descriptor, rawSelection), [descriptor, rawSelection]);
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (selection !== rawSelection)
|
|
34
|
+
setSelection(selection);
|
|
35
|
+
}, [rawSelection, selection]);
|
|
20
36
|
const panels = resolveTransferListPanels(descriptor);
|
|
37
|
+
const canMove = (panel) => panels[panel].some(item => !item.disabled && selection[panel].has(item.id));
|
|
21
38
|
const rootRef = useRef(null);
|
|
22
39
|
const [focused, setFocused] = useState({ source: null, target: null });
|
|
40
|
+
const focusWithin = useRef(false);
|
|
41
|
+
const focusedPanel = useRef(null);
|
|
42
|
+
const previousPanels = useRef(panels);
|
|
43
|
+
useLayoutEffect(() => {
|
|
44
|
+
const before = previousPanels.current;
|
|
45
|
+
previousPanels.current = panels;
|
|
46
|
+
const panel = focusedPanel.current;
|
|
47
|
+
if (!focusWithin.current || panel === null || document.activeElement !== document.body)
|
|
48
|
+
return;
|
|
49
|
+
const id = focused[panel];
|
|
50
|
+
if (id === null || panels[panel].some(item => item.id === id))
|
|
51
|
+
return;
|
|
52
|
+
// External removals bypass the move handler, but must not drop row focus onto body.
|
|
53
|
+
const next = resolveTransferListFocusAfterMove(panels[panel], before[panel].findIndex(item => item.id === id));
|
|
54
|
+
if (next !== null)
|
|
55
|
+
focusRow(panel, next);
|
|
56
|
+
else
|
|
57
|
+
rootRef.current?.querySelector(`[data-hjm-transfer-empty="${panel}"]`)?.focus();
|
|
58
|
+
}, [focused, panels]);
|
|
23
59
|
const focusRow = (panel, id) => {
|
|
24
60
|
setFocused((previous) => ({ ...previous, [panel]: id }));
|
|
25
61
|
if (id === null)
|
|
@@ -35,12 +71,16 @@ export const TransferList = forwardRef(function TransferList({ items, labels, ta
|
|
|
35
71
|
const removedIndex = before.findIndex((item) => item.id === result.movedIds[0]);
|
|
36
72
|
const remaining = before.filter((item) => !result.movedIds.includes(item.id));
|
|
37
73
|
setTargetKeys(result.targetKeys);
|
|
38
|
-
|
|
74
|
+
// Controlled owners may refuse or defer the move; keep checks until the rows actually leave.
|
|
75
|
+
if (controlledTargetKeys === undefined)
|
|
76
|
+
setSelection(result.selection);
|
|
39
77
|
onMove?.(result.movedIds, direction);
|
|
40
78
|
// Focus lands on the row that slid into the first removed position, or on
|
|
41
79
|
// the panel's empty state — never on the document body.
|
|
42
80
|
const next = resolveTransferListFocusAfterMove(remaining, removedIndex);
|
|
43
81
|
queueMicrotask(() => {
|
|
82
|
+
if (result.movedIds.some(id => rootRef.current?.querySelector(`[data-hjm-transfer-row="${from}:${CSS.escape(id)}"]`)))
|
|
83
|
+
return;
|
|
44
84
|
if (next !== null)
|
|
45
85
|
focusRow(from, next);
|
|
46
86
|
else
|
|
@@ -59,10 +99,14 @@ export const TransferList = forwardRef(function TransferList({ items, labels, ta
|
|
|
59
99
|
const removedIndex = before.findIndex((item) => item.id === id);
|
|
60
100
|
const remaining = before.filter((item) => item.id !== id);
|
|
61
101
|
setTargetKeys(result.targetKeys);
|
|
62
|
-
|
|
102
|
+
if (controlledTargetKeys === undefined) {
|
|
103
|
+
setSelection((current) => ({ ...current, [panel]: new Set([...current[panel]].filter((key) => key !== id)) }));
|
|
104
|
+
}
|
|
63
105
|
onMove?.(result.movedIds, direction);
|
|
64
106
|
const next = resolveTransferListFocusAfterMove(remaining, removedIndex);
|
|
65
107
|
queueMicrotask(() => {
|
|
108
|
+
if (rootRef.current?.querySelector(`[data-hjm-transfer-row="${panel}:${CSS.escape(id)}"]`))
|
|
109
|
+
return;
|
|
66
110
|
if (next !== null)
|
|
67
111
|
focusRow(panel, next);
|
|
68
112
|
else
|
|
@@ -102,8 +146,9 @@ export const TransferList = forwardRef(function TransferList({ items, labels, ta
|
|
|
102
146
|
moveSingle(panel, item.id);
|
|
103
147
|
}
|
|
104
148
|
};
|
|
105
|
-
return (_jsxs("div", { className: "hjm-transfer-list__panel", role: "group", "aria-label": labels[panel], children: [_jsxs("div", { className: "hjm-transfer-list__header", children: [_jsx("span", { className: "hjm-transfer-list__title", children: labels[panel] }), _jsx("button", { type: "button", role: "checkbox", "aria-checked": selectAllState === "mixed" ? "mixed" : String(selectAllState === true), "aria-label": `${labels[panel]} ${labels.selectAll}`, className: "hjm-transfer-list__select-all", onClick: () => setSelection(toggleTransferListSelectAll(descriptor, selection, panel)), children: selectAllState === true ? "✓" : selectAllState === "mixed" ? "–" : "" })] }), panelItems.length === 0 ? (_jsx("p", { className: "hjm-transfer-list__empty", "data-hjm-transfer-empty": panel, tabIndex: -1, children: labels.empty })) : (_jsx("div", { role: "listbox", "aria-multiselectable": "true", "aria-label": labels[panel], className: "hjm-transfer-list__options", children: panelItems.map((item) => (_jsxs("div", { role: "option", "data-hjm-transfer-row": `${panel}:${item.id}`, "aria-selected": selected.has(item.id), "aria-disabled": item.disabled || undefined, tabIndex: item.id === activeId ? 0 : -1, className: "hjm-transfer-list__option", onFocus: () => setFocused((previous) => ({ ...previous, [panel]: item.id })), onClick: () => setSelection(toggleTransferListSelection(descriptor, selection, panel, item.id)), onKeyDown: (event) => onKeyDown(event, item), children: [_jsx("span", { "aria-hidden": "true", className: "hjm-transfer-list__mark", "data-checked": selected.has(item.id) || undefined, children: selected.has(item.id) ? "✓" : "" }), _jsx("span", { children: item.label })] }, item.id))) }))] }));
|
|
149
|
+
return (_jsxs("div", { className: "hjm-transfer-list__panel", role: "group", "aria-label": labels[panel], children: [_jsxs("div", { className: "hjm-transfer-list__header", children: [_jsx("span", { className: "hjm-transfer-list__title", children: labels[panel] }), _jsx("button", { type: "button", role: "checkbox", "aria-checked": selectAllState === "mixed" ? "mixed" : String(selectAllState === true), "aria-label": `${labels[panel]} ${labels.selectAll}`, className: "hjm-transfer-list__select-all", disabled: !panelItems.some(item => !item.disabled), onClick: () => setSelection(toggleTransferListSelectAll(descriptor, selection, panel)), children: selectAllState === true ? "✓" : selectAllState === "mixed" ? "–" : "" })] }), panelItems.length === 0 ? (_jsx("p", { className: "hjm-transfer-list__empty", "data-hjm-transfer-empty": panel, tabIndex: -1, children: labels.empty })) : (_jsx("div", { role: "listbox", "aria-multiselectable": "true", "aria-label": labels[panel], className: "hjm-transfer-list__options", children: panelItems.map((item) => (_jsxs("div", { role: "option", "data-hjm-transfer-row": `${panel}:${item.id}`, "aria-selected": selected.has(item.id), "aria-disabled": item.disabled || undefined, tabIndex: item.id === activeId ? 0 : -1, className: "hjm-transfer-list__option", onFocus: () => { focusedPanel.current = panel; setFocused((previous) => ({ ...previous, [panel]: item.id })); }, onClick: () => setSelection(toggleTransferListSelection(descriptor, selection, panel, item.id)), onKeyDown: (event) => onKeyDown(event, item), children: [_jsx("span", { "aria-hidden": "true", className: "hjm-transfer-list__mark", "data-checked": selected.has(item.id) || undefined, children: selected.has(item.id) ? "✓" : "" }), _jsx("span", { children: item.label })] }, item.id))) }))] }));
|
|
106
150
|
};
|
|
107
|
-
return (_jsxs("div", { ref: composeRefs(rootRef, forwardedRef), style: layoutStyle, className: classNames("hjm-transfer-list", className),
|
|
151
|
+
return (_jsxs("div", { ref: composeRefs(rootRef, forwardedRef), style: layoutStyle, className: classNames("hjm-transfer-list", className), onFocusCapture: () => { focusWithin.current = true; }, onBlurCapture: event => { if (!event.currentTarget.contains(event.relatedTarget))
|
|
152
|
+
focusWithin.current = false; }, children: [renderPanel("source"), _jsxs("div", { className: "hjm-transfer-list__actions", children: [_jsx(Button, { tone: "secondary", disabled: !canMove("source"), onClick: () => move("toTarget"), children: labels.toTarget }), _jsx(Button, { tone: "secondary", disabled: !canMove("target"), onClick: () => move("toSource"), children: labels.toSource })] }), renderPanel("target")] }));
|
|
108
153
|
});
|
|
109
154
|
//# sourceMappingURL=transfer-list.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transfer-list.js","sourceRoot":"","sources":["../src/transfer-list.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n moveTransferListSelection,\n reconcileTransferListSelection,\n resolveTransferListFocusAfterMove,\n resolveTransferListPanels,\n resolveTransferListSelectAllState,\n toggleTransferListSelectAll,\n toggleTransferListSelection,\n type TransferListMoveDirection,\n type TransferListPanel,\n type TransferListSelection,\n} from \"@hjmds/design-contracts/components/transfer-list\";\nimport type { SelectItemDescriptor } from \"@hjmds/design-contracts/behaviors\";\nimport {\n forwardRef,\n useMemo,\n useRef,\n useState,\n type KeyboardEvent,\n} from \"react\";\nimport { Button } from \"./actions.js\";\nimport { classNames, composeRefs, useControllableState } from \"./internal.js\";\nimport type { HjmCompositionStyleProp } from \"./composition-style.js\";\n\nexport type TransferListLabels = Readonly<{\n source: string;\n target: string;\n toTarget: string;\n toSource: string;\n selectAll: string;\n empty: string;\n}>;\n\nexport type TransferListProps<Id extends string = string> = Readonly<{\n items: readonly SelectItemDescriptor<Id>[];\n labels: TransferListLabels;\n targetKeys?: ReadonlySet<Id>;\n defaultTargetKeys?: ReadonlySet<Id>;\n onTargetKeysChange?: (keys: ReadonlySet<Id>) => void;\n /** Receives which ids moved, in origin-panel order, so the product announces it. */\n onMove?: (movedIds: readonly Id[], direction: TransferListMoveDirection) => void;\n className?: string;\n /** Canonical layout-only placement on the root element. Controlled visual keys are excluded. */\n layoutStyle?: HjmCompositionStyleProp;\n}>;\n\nconst emptySelection = <Id extends string>(): TransferListSelection<Id> => ({\n source: new Set<Id>(),\n target: new Set<Id>(),\n});\n\nexport const TransferList = forwardRef(function TransferList<Id extends string = string>(\n {\n items,\n labels,\n targetKeys: controlledTargetKeys,\n defaultTargetKeys,\n onTargetKeysChange,\n onMove,\n className,\n layoutStyle,\n }: TransferListProps<Id>,\n forwardedRef: React.Ref<HTMLDivElement>,\n) {\n const [targetKeys, setTargetKeys] = useControllableState<ReadonlySet<Id>>({\n ...(controlledTargetKeys === undefined ? {} : { value: controlledTargetKeys }),\n defaultValue: defaultTargetKeys ?? new Set<Id>(),\n ...(onTargetKeysChange === undefined ? {} : { onChange: onTargetKeysChange }),\n });\n const [rawSelection, setSelection] = useState<TransferListSelection<Id>>(emptySelection<Id>);\n const descriptor = useMemo(() => ({ items, targetKeys }), [items, targetKeys]);\n // A row that left the list entirely must not keep a pending check mark.\n const selection = useMemo(\n () => reconcileTransferListSelection(descriptor, rawSelection),\n [descriptor, rawSelection],\n );\n const panels = resolveTransferListPanels(descriptor);\n const rootRef = useRef<HTMLDivElement>(null);\n const [focused, setFocused] = useState<Readonly<Record<TransferListPanel, Id | null>>>({ source: null, target: null });\n\n const focusRow = (panel: TransferListPanel, id: Id | null) => {\n setFocused((previous) => ({ ...previous, [panel]: id }));\n if (id === null) return;\n rootRef.current?.querySelector<HTMLElement>(`[data-hjm-transfer-row=\"${panel}:${CSS.escape(id)}\"]`)?.focus();\n };\n const move = (direction: TransferListMoveDirection) => {\n const from: TransferListPanel = direction === \"toTarget\" ? \"source\" : \"target\";\n const before = panels[from];\n const result = moveTransferListSelection(descriptor, selection, direction);\n if (result.movedIds.length === 0) return;\n const removedIndex = before.findIndex((item) => item.id === result.movedIds[0]);\n const remaining = before.filter((item) => !result.movedIds.includes(item.id));\n setTargetKeys(result.targetKeys);\n setSelection(result.selection);\n onMove?.(result.movedIds, direction);\n // Focus lands on the row that slid into the first removed position, or on\n // the panel's empty state — never on the document body.\n const next = resolveTransferListFocusAfterMove(remaining, removedIndex);\n queueMicrotask(() => {\n if (next !== null) focusRow(from, next);\n else rootRef.current?.querySelector<HTMLElement>(`[data-hjm-transfer-empty=\"${from}\"]`)?.focus();\n });\n };\n const moveSingle = (panel: TransferListPanel, id: Id) => {\n // A focused row moves on its own: a single move must not require first\n // building a multi-selection.\n const single = { ...emptySelection<Id>(), [panel]: new Set<Id>([id]) } as TransferListSelection<Id>;\n const direction: TransferListMoveDirection = panel === \"source\" ? \"toTarget\" : \"toSource\";\n const result = moveTransferListSelection(descriptor, single, direction);\n if (result.movedIds.length === 0) return;\n const before = panels[panel];\n const removedIndex = before.findIndex((item) => item.id === id);\n const remaining = before.filter((item) => item.id !== id);\n setTargetKeys(result.targetKeys);\n setSelection((current) => ({ ...current, [panel]: new Set([...current[panel]].filter((key) => key !== id)) }));\n onMove?.(result.movedIds, direction);\n const next = resolveTransferListFocusAfterMove(remaining, removedIndex);\n queueMicrotask(() => {\n if (next !== null) focusRow(panel, next);\n else rootRef.current?.querySelector<HTMLElement>(`[data-hjm-transfer-empty=\"${panel}\"]`)?.focus();\n });\n };\n\n const renderPanel = (panel: TransferListPanel) => {\n const panelItems = panels[panel];\n const selected = selection[panel];\n const selectAllState = resolveTransferListSelectAllState(descriptor, selection, panel);\n const activeId = panelItems.some((item) => item.id === focused[panel])\n ? focused[panel]\n : panelItems.find((item) => !item.disabled)?.id ?? panelItems[0]?.id ?? null;\n const onKeyDown = (event: KeyboardEvent<HTMLElement>, item: SelectItemDescriptor<Id>) => {\n const index = panelItems.findIndex((candidate) => candidate.id === item.id);\n const goto = (nextIndex: number) => {\n const target = panelItems[Math.min(Math.max(nextIndex, 0), panelItems.length - 1)];\n if (target) { event.preventDefault(); focusRow(panel, target.id); }\n };\n if (event.key === \"ArrowDown\") goto(index + 1);\n else if (event.key === \"ArrowUp\") goto(index - 1);\n else if (event.key === \"Home\") goto(0);\n else if (event.key === \"End\") goto(panelItems.length - 1);\n else if (event.key === \" \") {\n event.preventDefault();\n setSelection(toggleTransferListSelection(descriptor, selection, panel, item.id));\n } else if (event.key === \"Enter\") {\n event.preventDefault();\n moveSingle(panel, item.id);\n }\n };\n return (\n <div className=\"hjm-transfer-list__panel\" role=\"group\" aria-label={labels[panel]}>\n <div className=\"hjm-transfer-list__header\">\n <span className=\"hjm-transfer-list__title\">{labels[panel]}</span>\n <button\n type=\"button\"\n role=\"checkbox\"\n aria-checked={selectAllState === \"mixed\" ? \"mixed\" : String(selectAllState === true) as \"true\" | \"false\"}\n aria-label={`${labels[panel]} ${labels.selectAll}`}\n className=\"hjm-transfer-list__select-all\"\n onClick={() => setSelection(toggleTransferListSelectAll(descriptor, selection, panel))}\n >\n {selectAllState === true ? \"✓\" : selectAllState === \"mixed\" ? \"–\" : \"\"}\n </button>\n </div>\n {panelItems.length === 0 ? (\n <p className=\"hjm-transfer-list__empty\" data-hjm-transfer-empty={panel} tabIndex={-1}>{labels.empty}</p>\n ) : (\n <div role=\"listbox\" aria-multiselectable=\"true\" aria-label={labels[panel]} className=\"hjm-transfer-list__options\">\n {panelItems.map((item) => (\n <div\n key={item.id}\n role=\"option\"\n data-hjm-transfer-row={`${panel}:${item.id}`}\n aria-selected={selected.has(item.id)}\n aria-disabled={item.disabled || undefined}\n tabIndex={item.id === activeId ? 0 : -1}\n className=\"hjm-transfer-list__option\"\n onFocus={() => setFocused((previous) => ({ ...previous, [panel]: item.id }))}\n onClick={() => setSelection(toggleTransferListSelection(descriptor, selection, panel, item.id))}\n onKeyDown={(event) => onKeyDown(event, item)}\n >\n <span aria-hidden=\"true\" className=\"hjm-transfer-list__mark\" data-checked={selected.has(item.id) || undefined}>\n {selected.has(item.id) ? \"✓\" : \"\"}\n </span>\n <span>{item.label}</span>\n </div>\n ))}\n </div>\n )}\n </div>\n );\n };\n\n return (\n <div ref={composeRefs(rootRef, forwardedRef)} style={layoutStyle} className={classNames(\"hjm-transfer-list\", className)}>\n {renderPanel(\"source\")}\n <div className=\"hjm-transfer-list__actions\">\n <Button tone=\"secondary\" disabled={selection.source.size === 0} onClick={() => move(\"toTarget\")}>\n {labels.toTarget}\n </Button>\n <Button tone=\"secondary\" disabled={selection.target.size === 0} onClick={() => move(\"toSource\")}>\n {labels.toSource}\n </Button>\n </div>\n {renderPanel(\"target\")}\n </div>\n );\n}) as <Id extends string = string>(\n props: TransferListProps<Id> & { ref?: React.Ref<HTMLDivElement> },\n) => React.ReactElement | null;\n"]}
|
|
1
|
+
{"version":3,"file":"transfer-list.js","sourceRoot":"","sources":["../src/transfer-list.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n moveTransferListSelection,\n reconcileTransferListSelection,\n resolveTransferListFocusAfterMove,\n resolveTransferListPanels,\n resolveTransferListSelectAllState,\n toggleTransferListSelectAll,\n toggleTransferListSelection,\n validateTransferListDescriptor,\n type TransferListMoveDirection,\n type TransferListPanel,\n type TransferListSelection,\n} from \"@hjmds/design-contracts/components/transfer-list\";\nimport { reconcileCheckboxSelection, type SelectItemDescriptor } from \"@hjmds/design-contracts/behaviors\";\nimport {\n forwardRef,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type KeyboardEvent,\n} from \"react\";\nimport { Button } from \"./actions.js\";\nimport { classNames, composeRefs, useControllableState } from \"./internal.js\";\nimport type { HjmCompositionStyleProp } from \"./composition-style.js\";\n\nexport type TransferListLabels = Readonly<{\n source: string;\n target: string;\n toTarget: string;\n toSource: string;\n selectAll: string;\n empty: string;\n}>;\n\nexport type TransferListProps<Id extends string = string> = Readonly<{\n items: readonly SelectItemDescriptor<Id>[];\n labels: TransferListLabels;\n targetKeys?: ReadonlySet<Id>;\n defaultTargetKeys?: ReadonlySet<Id>;\n onTargetKeysChange?: (keys: ReadonlySet<Id>) => void;\n /** Receives which ids moved, in origin-panel order, so the product announces it. */\n onMove?: (movedIds: readonly Id[], direction: TransferListMoveDirection) => void;\n className?: string;\n /** Canonical layout-only placement on the root element. Controlled visual keys are excluded. */\n layoutStyle?: HjmCompositionStyleProp;\n}>;\n\nconst emptySelection = <Id extends string>(): TransferListSelection<Id> => ({\n source: new Set<Id>(),\n target: new Set<Id>(),\n});\n\nexport const TransferList = forwardRef(function TransferList<Id extends string = string>(\n {\n items,\n labels,\n targetKeys: controlledTargetKeys,\n defaultTargetKeys,\n onTargetKeysChange,\n onMove,\n className,\n layoutStyle,\n }: TransferListProps<Id>,\n forwardedRef: React.Ref<HTMLDivElement>,\n) {\n const [storedTargetKeys, setTargetKeys] = useControllableState<ReadonlySet<Id>>({\n ...(controlledTargetKeys === undefined ? {} : { value: controlledTargetKeys }),\n defaultValue: defaultTargetKeys ?? new Set<Id>(),\n ...(onTargetKeysChange === undefined ? {} : { onChange: onTargetKeysChange }),\n });\n const validatedInitialTargets = useRef(false);\n if (!validatedInitialTargets.current) {\n validateTransferListDescriptor({ items, targetKeys: storedTargetKeys });\n validatedInitialTargets.current = true;\n }\n const targetKeys = useMemo(() => controlledTargetKeys === undefined\n ? reconcileCheckboxSelection(items, storedTargetKeys) : storedTargetKeys,\n [controlledTargetKeys, items, storedTargetKeys]);\n useEffect(() => {\n if (controlledTargetKeys === undefined && targetKeys !== storedTargetKeys) setTargetKeys(targetKeys);\n }, [controlledTargetKeys, setTargetKeys, storedTargetKeys, targetKeys]);\n const [rawSelection, setSelection] = useState<TransferListSelection<Id>>(emptySelection<Id>);\n const descriptor = useMemo(() => ({ items, targetKeys }), [items, targetKeys]);\n // A row that left the list entirely must not keep a pending check mark.\n const selection = useMemo(\n () => reconcileTransferListSelection(descriptor, rawSelection),\n [descriptor, rawSelection],\n );\n useEffect(() => {\n if (selection !== rawSelection) setSelection(selection);\n }, [rawSelection, selection]);\n const panels = resolveTransferListPanels(descriptor);\n const canMove = (panel: TransferListPanel) => panels[panel].some(item => !item.disabled && selection[panel].has(item.id));\n const rootRef = useRef<HTMLDivElement>(null);\n const [focused, setFocused] = useState<Readonly<Record<TransferListPanel, Id | null>>>({ source: null, target: null });\n const focusWithin = useRef(false);\n const focusedPanel = useRef<TransferListPanel | null>(null);\n const previousPanels = useRef(panels);\n useLayoutEffect(() => {\n const before = previousPanels.current;\n previousPanels.current = panels;\n const panel = focusedPanel.current;\n if (!focusWithin.current || panel === null || document.activeElement !== document.body) return;\n const id = focused[panel];\n if (id === null || panels[panel].some(item => item.id === id)) return;\n // External removals bypass the move handler, but must not drop row focus onto body.\n const next = resolveTransferListFocusAfterMove(panels[panel], before[panel].findIndex(item => item.id === id));\n if (next !== null) focusRow(panel, next);\n else rootRef.current?.querySelector<HTMLElement>(`[data-hjm-transfer-empty=\"${panel}\"]`)?.focus();\n }, [focused, panels]);\n\n const focusRow = (panel: TransferListPanel, id: Id | null) => {\n setFocused((previous) => ({ ...previous, [panel]: id }));\n if (id === null) return;\n rootRef.current?.querySelector<HTMLElement>(`[data-hjm-transfer-row=\"${panel}:${CSS.escape(id)}\"]`)?.focus();\n };\n const move = (direction: TransferListMoveDirection) => {\n const from: TransferListPanel = direction === \"toTarget\" ? \"source\" : \"target\";\n const before = panels[from];\n const result = moveTransferListSelection(descriptor, selection, direction);\n if (result.movedIds.length === 0) return;\n const removedIndex = before.findIndex((item) => item.id === result.movedIds[0]);\n const remaining = before.filter((item) => !result.movedIds.includes(item.id));\n setTargetKeys(result.targetKeys);\n // Controlled owners may refuse or defer the move; keep checks until the rows actually leave.\n if (controlledTargetKeys === undefined) setSelection(result.selection);\n onMove?.(result.movedIds, direction);\n // Focus lands on the row that slid into the first removed position, or on\n // the panel's empty state — never on the document body.\n const next = resolveTransferListFocusAfterMove(remaining, removedIndex);\n queueMicrotask(() => {\n if (result.movedIds.some(id => rootRef.current?.querySelector(`[data-hjm-transfer-row=\"${from}:${CSS.escape(id)}\"]`))) return;\n if (next !== null) focusRow(from, next);\n else rootRef.current?.querySelector<HTMLElement>(`[data-hjm-transfer-empty=\"${from}\"]`)?.focus();\n });\n };\n const moveSingle = (panel: TransferListPanel, id: Id) => {\n // A focused row moves on its own: a single move must not require first\n // building a multi-selection.\n const single = { ...emptySelection<Id>(), [panel]: new Set<Id>([id]) } as TransferListSelection<Id>;\n const direction: TransferListMoveDirection = panel === \"source\" ? \"toTarget\" : \"toSource\";\n const result = moveTransferListSelection(descriptor, single, direction);\n if (result.movedIds.length === 0) return;\n const before = panels[panel];\n const removedIndex = before.findIndex((item) => item.id === id);\n const remaining = before.filter((item) => item.id !== id);\n setTargetKeys(result.targetKeys);\n if (controlledTargetKeys === undefined) {\n setSelection((current) => ({ ...current, [panel]: new Set([...current[panel]].filter((key) => key !== id)) }));\n }\n onMove?.(result.movedIds, direction);\n const next = resolveTransferListFocusAfterMove(remaining, removedIndex);\n queueMicrotask(() => {\n if (rootRef.current?.querySelector(`[data-hjm-transfer-row=\"${panel}:${CSS.escape(id)}\"]`)) return;\n if (next !== null) focusRow(panel, next);\n else rootRef.current?.querySelector<HTMLElement>(`[data-hjm-transfer-empty=\"${panel}\"]`)?.focus();\n });\n };\n\n const renderPanel = (panel: TransferListPanel) => {\n const panelItems = panels[panel];\n const selected = selection[panel];\n const selectAllState = resolveTransferListSelectAllState(descriptor, selection, panel);\n const activeId = panelItems.some((item) => item.id === focused[panel])\n ? focused[panel]\n : panelItems.find((item) => !item.disabled)?.id ?? panelItems[0]?.id ?? null;\n const onKeyDown = (event: KeyboardEvent<HTMLElement>, item: SelectItemDescriptor<Id>) => {\n const index = panelItems.findIndex((candidate) => candidate.id === item.id);\n const goto = (nextIndex: number) => {\n const target = panelItems[Math.min(Math.max(nextIndex, 0), panelItems.length - 1)];\n if (target) { event.preventDefault(); focusRow(panel, target.id); }\n };\n if (event.key === \"ArrowDown\") goto(index + 1);\n else if (event.key === \"ArrowUp\") goto(index - 1);\n else if (event.key === \"Home\") goto(0);\n else if (event.key === \"End\") goto(panelItems.length - 1);\n else if (event.key === \" \") {\n event.preventDefault();\n setSelection(toggleTransferListSelection(descriptor, selection, panel, item.id));\n } else if (event.key === \"Enter\") {\n event.preventDefault();\n moveSingle(panel, item.id);\n }\n };\n return (\n <div className=\"hjm-transfer-list__panel\" role=\"group\" aria-label={labels[panel]}>\n <div className=\"hjm-transfer-list__header\">\n <span className=\"hjm-transfer-list__title\">{labels[panel]}</span>\n <button\n type=\"button\"\n role=\"checkbox\"\n aria-checked={selectAllState === \"mixed\" ? \"mixed\" : String(selectAllState === true) as \"true\" | \"false\"}\n aria-label={`${labels[panel]} ${labels.selectAll}`}\n className=\"hjm-transfer-list__select-all\"\n disabled={!panelItems.some(item => !item.disabled)}\n onClick={() => setSelection(toggleTransferListSelectAll(descriptor, selection, panel))}\n >\n {selectAllState === true ? \"✓\" : selectAllState === \"mixed\" ? \"–\" : \"\"}\n </button>\n </div>\n {panelItems.length === 0 ? (\n <p className=\"hjm-transfer-list__empty\" data-hjm-transfer-empty={panel} tabIndex={-1}>{labels.empty}</p>\n ) : (\n <div role=\"listbox\" aria-multiselectable=\"true\" aria-label={labels[panel]} className=\"hjm-transfer-list__options\">\n {panelItems.map((item) => (\n <div\n key={item.id}\n role=\"option\"\n data-hjm-transfer-row={`${panel}:${item.id}`}\n aria-selected={selected.has(item.id)}\n aria-disabled={item.disabled || undefined}\n tabIndex={item.id === activeId ? 0 : -1}\n className=\"hjm-transfer-list__option\"\n onFocus={() => { focusedPanel.current = panel; setFocused((previous) => ({ ...previous, [panel]: item.id })); }}\n onClick={() => setSelection(toggleTransferListSelection(descriptor, selection, panel, item.id))}\n onKeyDown={(event) => onKeyDown(event, item)}\n >\n <span aria-hidden=\"true\" className=\"hjm-transfer-list__mark\" data-checked={selected.has(item.id) || undefined}>\n {selected.has(item.id) ? \"✓\" : \"\"}\n </span>\n <span>{item.label}</span>\n </div>\n ))}\n </div>\n )}\n </div>\n );\n };\n\n return (\n <div ref={composeRefs(rootRef, forwardedRef)} style={layoutStyle} className={classNames(\"hjm-transfer-list\", className)}\n onFocusCapture={() => { focusWithin.current = true; }}\n onBlurCapture={event => { if (!event.currentTarget.contains(event.relatedTarget as Node | null)) focusWithin.current = false; }}>\n {renderPanel(\"source\")}\n <div className=\"hjm-transfer-list__actions\">\n <Button tone=\"secondary\" disabled={!canMove(\"source\")} onClick={() => move(\"toTarget\")}>\n {labels.toTarget}\n </Button>\n <Button tone=\"secondary\" disabled={!canMove(\"target\")} onClick={() => move(\"toSource\")}>\n {labels.toSource}\n </Button>\n </div>\n {renderPanel(\"target\")}\n </div>\n );\n}) as <Id extends string = string>(\n props: TransferListProps<Id> & { ref?: React.Ref<HTMLDivElement> },\n) => React.ReactElement | null;\n"]}
|
package/dist/tree.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../src/tree.tsx"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,yBAAyB,EAE9B,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACxB,MAAM,yCAAyC,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAkB,MAAM,mCAAmC,CAAC;AACvF,OAAO,
|
|
1
|
+
{"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../src/tree.tsx"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,yBAAyB,EAE9B,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACxB,MAAM,yCAAyC,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAkB,MAAM,mCAAmC,CAAC;AACvF,OAAO,EASL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAGf,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAEtE,MAAM,MAAM,SAAS,CAAC,EAAE,SAAS,MAAM,GAAG,MAAM,IAAI,QAAQ,CAAC;IAC3D,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,kBAAkB,CAAC,EAAE,CAAC,EAAE,CAAC;IACzC,qBAAqB,EAAE,yBAAyB,CAAC;IACjD,YAAY,CAAC,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;IAC/B,mBAAmB,CAAC,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;IACtC,oBAAoB,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC;IACvD,SAAS,CAAC,EAAE,kBAAkB,CAAC,EAAE,CAAC,CAAC;IACnC;;;;OAIG;IACH,aAAa,CAAC,EAAE,WAAW,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC;IAC/C,eAAe,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,IAAI,CAAC;IACnC,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,sFAAsF;IACtF,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC;QAAE,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC,KAAK,SAAS,CAAC;IACrE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gGAAgG;IAChG,WAAW,CAAC,EAAE,uBAAuB,CAAC;CACvC,CAAC,CAAC;AAaH,eAAO,MAAM,IAAI,EAwMX,CAAC,EAAE,SAAS,MAAM,GAAG,MAAM,EAC/B,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC,GAAG;IAAE,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;CAAE,KACvD,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC"}
|
package/dist/tree.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { getTreeArrowKeyIntent, getTreeArrowResult, getVisibleTreeNavigationTarget, getVisibleTreeTypeaheadMatch, reconcileTreeExpansion, resolveTreeDescriptor, treeRecipe, } from "@hjmds/design-contracts/components/tree";
|
|
3
|
-
import { forwardRef, useMemo, useRef, useState, } from "react";
|
|
3
|
+
import { forwardRef, useEffect, useLayoutEffect, useMemo, useRef, useState, } from "react";
|
|
4
4
|
import { classNames, composeRefs, useControllableState } from "./internal.js";
|
|
5
5
|
import { useOptionalHjmTheme } from "./provider.js";
|
|
6
6
|
const typeaheadResetMs = 500;
|
|
@@ -33,13 +33,46 @@ export const Tree = forwardRef(function Tree({ label, nodes, composeAccessibleNa
|
|
|
33
33
|
? selection.selectedKey !== undefined
|
|
34
34
|
: selection?.mode === "multiple" ? selection.selectedKeys !== undefined : true;
|
|
35
35
|
const [uncontrolledSelected, setUncontrolledSelected] = useState(() => selectedIds(selection));
|
|
36
|
-
const
|
|
36
|
+
const rawSelected = selectionControlled ? selectedIds(selection) : uncontrolledSelected;
|
|
37
|
+
const selected = useMemo(() => {
|
|
38
|
+
const liveIds = new Set(resolved.map(node => node.id));
|
|
39
|
+
const keys = [...rawSelected].filter(id => liveIds.has(id));
|
|
40
|
+
return keys.length === rawSelected.size ? rawSelected : new Set(keys);
|
|
41
|
+
}, [rawSelected, resolved]);
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
if (selectionControlled || selected === uncontrolledSelected || !selection || selection.mode === "none")
|
|
44
|
+
return;
|
|
45
|
+
// A removed node must not leak into the next callback or revive when reinserted.
|
|
46
|
+
setUncontrolledSelected(selected);
|
|
47
|
+
if (selection.mode === "single")
|
|
48
|
+
selection.onSelectionChange?.([...selected][0] ?? null);
|
|
49
|
+
else
|
|
50
|
+
selection.onSelectionChange?.(selected);
|
|
51
|
+
}, [selected, selection, selectionControlled, uncontrolledSelected]);
|
|
37
52
|
const [focusedId, setFocusedId] = useState(null);
|
|
38
53
|
// Roving tab stop: one node is tabbable, and it must stay a real visible node
|
|
39
|
-
// after a collapse removed the previously focused descendant.
|
|
40
|
-
|
|
54
|
+
// after a collapse removed the previously focused descendant. The decorative
|
|
55
|
+
// glyph is not a nested control, so each row has only one focus target.
|
|
56
|
+
const previousResolved = useRef(resolved);
|
|
57
|
+
let fallbackId = focusedId;
|
|
58
|
+
while (fallbackId !== null && !visible.some(node => node.id === fallbackId)) {
|
|
59
|
+
fallbackId = previousResolved.current.find(node => node.id === fallbackId)?.parentId ?? null;
|
|
60
|
+
}
|
|
61
|
+
const activeId = fallbackId ?? visible[0]?.id ?? null;
|
|
62
|
+
const focusWithin = useRef(false);
|
|
41
63
|
const rootRef = useRef(null);
|
|
42
64
|
const typeahead = useRef({ query: "", at: 0 });
|
|
65
|
+
useLayoutEffect(() => {
|
|
66
|
+
previousResolved.current = resolved;
|
|
67
|
+
if (focusedId === null || activeId === null || focusedId === activeId)
|
|
68
|
+
return;
|
|
69
|
+
setFocusedId(activeId);
|
|
70
|
+
// External collapse/removal can unmount the focused row without firing blur.
|
|
71
|
+
// Restore its surviving ancestor only while focus still belongs to this tree.
|
|
72
|
+
if (focusWithin.current) {
|
|
73
|
+
rootRef.current?.querySelector(`[data-hjm-tree-node="${CSS.escape(activeId)}"]`)?.focus();
|
|
74
|
+
}
|
|
75
|
+
}, [activeId, focusedId, resolved]);
|
|
43
76
|
const focusNode = (id) => {
|
|
44
77
|
setFocusedId(id);
|
|
45
78
|
rootRef.current?.querySelector(`[data-hjm-tree-node="${CSS.escape(id)}"]`)?.focus();
|
|
@@ -116,10 +149,10 @@ export const Tree = forwardRef(function Tree({ label, nodes, composeAccessibleNa
|
|
|
116
149
|
focusNode(match);
|
|
117
150
|
}
|
|
118
151
|
};
|
|
119
|
-
return (_jsxs("div", { ref: composeRefs(rootRef, forwardedRef), role: "tree",
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
152
|
+
return (_jsxs("div", { ref: composeRefs(rootRef, forwardedRef), role: "tree", onFocusCapture: () => { focusWithin.current = true; }, onBlurCapture: (event) => {
|
|
153
|
+
if (!event.currentTarget.contains(event.relatedTarget))
|
|
154
|
+
focusWithin.current = false;
|
|
155
|
+
}, "aria-label": label, "aria-busy": asyncState.status === "loading" || asyncState.status === "loadingMore" || undefined, "aria-multiselectable": selection?.mode === "multiple" || undefined, className: classNames("hjm-tree", className), style: { ...layoutStyle, "--hjm-tree-indent": `${treeRecipe.indentPerLevel}px` }, children: [asyncState.status === "empty" || asyncState.status === "error" || asyncState.status === "loading" ? (_jsx("p", { className: "hjm-tree__state", role: asyncState.status === "error" ? "alert" : "status", children: asyncState.message })) : null, visible.map((node) => (_jsxs("div", { role: "treeitem", "data-hjm-tree-node": node.id, "aria-level": node.depth, "aria-posinset": node.position, "aria-setsize": node.siblingCount, "aria-expanded": node.hasChildren ? node.expanded : undefined, "aria-selected": selection && selection.mode !== "none" ? selected.has(node.id) : undefined, "aria-checked": checkedStates ? (checkedStates.get(node.id) === "mixed" ? "mixed" : String(checkedStates.get(node.id) === true)) : undefined, "aria-disabled": node.disabled || undefined, "aria-label": node.accessibleName, tabIndex: node.id === activeId ? 0 : -1, className: "hjm-tree__node", style: { "--hjm-tree-depth": node.depth - 1 }, onClick: (event) => {
|
|
123
156
|
setFocusedId(node.id);
|
|
124
157
|
// Pointer users had no way to expand: only arrow keys toggled (2026-09-30
|
|
125
158
|
// audit WR-0930-2). The contract lets a row click change expansion; the
|
package/dist/tree.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tree.js","sourceRoot":"","sources":["../src/tree.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n getTreeArrowKeyIntent,\n getTreeArrowResult,\n getVisibleTreeNavigationTarget,\n getVisibleTreeTypeaheadMatch,\n reconcileTreeExpansion,\n resolveTreeDescriptor,\n treeRecipe,\n type ComposeTreeAccessibleName,\n type ResolvedTreeNodeDescriptor,\n type TreeAsyncState,\n type TreeNodeDescriptor,\n type TreeSelectionModel,\n} from \"@hjmds/design-contracts/components/tree\";\nimport type { CheckboxState, WebKeyboardKey } from \"@hjmds/design-contracts/behaviors\";\nimport {\n forwardRef,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type KeyboardEvent,\n type ReactNode,\n} from \"react\";\nimport { classNames, composeRefs, useControllableState } from \"./internal.js\";\nimport { useOptionalHjmTheme } from \"./provider.js\";\nimport type { HjmCompositionStyleProp } from \"./composition-style.js\";\n\nexport type TreeProps<Id extends string = string> = Readonly<{\n label: string;\n nodes: readonly TreeNodeDescriptor<Id>[];\n composeAccessibleName: ComposeTreeAccessibleName;\n expandedKeys?: ReadonlySet<Id>;\n defaultExpandedKeys?: ReadonlySet<Id>;\n onExpandedKeysChange?: (keys: ReadonlySet<Id>) => void;\n selection?: TreeSelectionModel<Id>;\n /**\n * Tri-state check marks per node, as `resolveTreeCheckedStates` derives them.\n * Checkboxes are not nested controls in a tree: the state rides on the node\n * itself as `aria-checked`, keeping the contract's one-tab-stop-per-node rule.\n */\n checkedStates?: ReadonlyMap<Id, CheckboxState>;\n onCheckedToggle?: (id: Id) => void;\n asyncState?: TreeAsyncState;\n /** Product-owned glyph for the expand/collapse affordance; decorative by contract. */\n renderToggle?: (state: Readonly<{ expanded: boolean }>) => ReactNode;\n className?: string;\n /** Canonical layout-only placement on the root element. Controlled visual keys are excluded. */\n layoutStyle?: HjmCompositionStyleProp;\n}>;\n\nconst typeaheadResetMs = 500;\n\nfunction selectedIds<Id extends string>(selection: TreeSelectionModel<Id> | undefined): ReadonlySet<Id> {\n if (!selection || selection.mode === \"none\") return new Set<Id>();\n if (selection.mode === \"single\") {\n const key = selection.selectedKey ?? selection.defaultSelectedKey ?? null;\n return key === null ? new Set<Id>() : new Set<Id>([key]);\n }\n return selection.selectedKeys ?? selection.defaultSelectedKeys ?? new Set<Id>();\n}\n\nexport const Tree = forwardRef(function Tree<Id extends string = string>(\n {\n label,\n nodes,\n composeAccessibleName,\n expandedKeys: controlledExpanded,\n defaultExpandedKeys,\n onExpandedKeysChange,\n selection,\n checkedStates,\n onCheckedToggle,\n asyncState = { status: \"idle\" },\n renderToggle,\n className,\n layoutStyle,\n }: TreeProps<Id>,\n forwardedRef: React.Ref<HTMLDivElement>,\n) {\n const [expandedKeys, setExpandedKeys] = useControllableState<ReadonlySet<Id>>({\n ...(controlledExpanded === undefined ? {} : { value: controlledExpanded }),\n defaultValue: defaultExpandedKeys ?? new Set<Id>(),\n ...(onExpandedKeysChange === undefined ? {} : { onChange: onExpandedKeysChange }),\n });\n const direction = useOptionalHjmTheme()?.environment.direction ?? \"ltr\";\n // Expansion keys survive a node list change only where the node still exists\n // and can still hold children; the contract owns that reconciliation.\n const liveExpanded = useMemo(() => reconcileTreeExpansion(nodes, expandedKeys), [nodes, expandedKeys]);\n const resolved = useMemo(\n () => resolveTreeDescriptor(nodes, liveExpanded, { composeAccessibleName }),\n [nodes, liveExpanded, composeAccessibleName],\n );\n const visible = useMemo(() => resolved.filter((node) => node.visible), [resolved]);\n // `defaultSelectedKey(s)` used to be re-read on every render, so an\n // uncontrolled tree reported the click to `onSelectionChange` but kept showing\n // the default. Keep the uncontrolled value in state like `useControllableState`\n // does for expansion; a controlled `selectedKey(s)` (including `null`) still wins.\n const selectionControlled = selection?.mode === \"single\"\n ? selection.selectedKey !== undefined\n : selection?.mode === \"multiple\" ? selection.selectedKeys !== undefined : true;\n const [uncontrolledSelected, setUncontrolledSelected] = useState<ReadonlySet<Id>>(() => selectedIds(selection));\n const selected = selectionControlled ? selectedIds(selection) : uncontrolledSelected;\n const [focusedId, setFocusedId] = useState<Id | null>(null);\n // Roving tab stop: one node is tabbable, and it must stay a real visible node\n // after a collapse removed the previously focused descendant.\n const activeId = visible.some((node) => node.id === focusedId) ? focusedId : visible[0]?.id ?? null;\n const rootRef = useRef<HTMLDivElement>(null);\n const typeahead = useRef<Readonly<{ query: string; at: number }>>({ query: \"\", at: 0 });\n\n const focusNode = (id: Id) => {\n setFocusedId(id);\n rootRef.current?.querySelector<HTMLElement>(`[data-hjm-tree-node=\"${CSS.escape(id)}\"]`)?.focus();\n };\n const setExpanded = (id: Id, expanded: boolean) => {\n const next = new Set(liveExpanded);\n if (expanded) next.add(id); else next.delete(id);\n setExpandedKeys(next);\n };\n const toggleSelection = (node: ResolvedTreeNodeDescriptor<Id>) => {\n if (node.disabled || !selection || selection.mode === \"none\") return;\n if (selection.mode === \"single\") {\n const current = selected.has(node.id) ? node.id : null;\n const next = current === node.id && selection.disallowEmptySelection !== true ? null : node.id;\n if (!selectionControlled) setUncontrolledSelected(next === null ? new Set<Id>() : new Set<Id>([next]));\n selection.onSelectionChange?.(next);\n return;\n }\n const next = new Set(selected);\n if (next.has(node.id)) next.delete(node.id); else next.add(node.id);\n if (!selectionControlled) setUncontrolledSelected(next);\n selection.onSelectionChange?.(next);\n };\n\n const onKeyDown = (event: KeyboardEvent<HTMLDivElement>, node: ResolvedTreeNodeDescriptor<Id>) => {\n const key = event.key as WebKeyboardKey;\n const arrow = getTreeArrowKeyIntent(key, direction);\n if (arrow) {\n const result = getTreeArrowResult(visible, node.id, arrow);\n if (result.action === \"none\") return;\n event.preventDefault();\n if (result.action === \"moveFocus\") focusNode(result.targetId);\n else setExpanded(node.id, result.action === \"expand\");\n return;\n }\n if (key === \"ArrowDown\" || key === \"ArrowUp\" || key === \"Home\" || key === \"End\") {\n const intent = key === \"ArrowDown\" ? \"next\" : key === \"ArrowUp\" ? \"previous\" : key === \"Home\" ? \"first\" : \"last\";\n const target = getVisibleTreeNavigationTarget(visible, node.id, intent);\n if (target === undefined) return;\n event.preventDefault();\n focusNode(target);\n return;\n }\n if (key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n if (checkedStates && onCheckedToggle) { if (!node.disabled) onCheckedToggle(node.id); }\n else toggleSelection(node);\n return;\n }\n if (event.key.length !== 1 || event.metaKey || event.ctrlKey || event.altKey) return;\n const now = Date.now();\n const query = now - typeahead.current.at > typeaheadResetMs ? event.key : typeahead.current.query + event.key;\n typeahead.current = { query, at: now };\n const match = getVisibleTreeTypeaheadMatch(visible, query, { startsAfterKey: node.id });\n if (match !== undefined) { event.preventDefault(); focusNode(match); }\n };\n\n return (\n <div\n ref={composeRefs(rootRef, forwardedRef)}\n role=\"tree\"\n aria-label={label}\n aria-busy={asyncState.status === \"loading\" || asyncState.status === \"loadingMore\" || undefined}\n aria-multiselectable={selection?.mode === \"multiple\" || undefined}\n className={classNames(\"hjm-tree\", className)}\n style={{ ...layoutStyle, \"--hjm-tree-indent\": `${treeRecipe.indentPerLevel}px` } as CSSProperties}\n >\n {asyncState.status === \"empty\" || asyncState.status === \"error\" || asyncState.status === \"loading\" ? (\n <p className=\"hjm-tree__state\" role={asyncState.status === \"error\" ? \"alert\" : \"status\"}>{asyncState.message}</p>\n ) : null}\n {visible.map((node) => (\n <div\n key={node.id}\n role=\"treeitem\"\n data-hjm-tree-node={node.id}\n aria-level={node.depth}\n aria-posinset={node.position}\n aria-setsize={node.siblingCount}\n aria-expanded={node.hasChildren ? node.expanded : undefined}\n aria-selected={selection && selection.mode !== \"none\" ? selected.has(node.id) : undefined}\n aria-checked={checkedStates ? (checkedStates.get(node.id) === \"mixed\" ? \"mixed\" : String(checkedStates.get(node.id) === true)) as \"true\" | \"false\" | \"mixed\" : undefined}\n aria-disabled={node.disabled || undefined}\n aria-label={node.accessibleName}\n // One tab stop for the whole tree; the glyph is decorative, not a\n // nested control, so a node is the only focusable thing in a row.\n tabIndex={node.id === activeId ? 0 : -1}\n className=\"hjm-tree__node\"\n style={{ \"--hjm-tree-depth\": node.depth - 1 } as CSSProperties}\n onClick={(event) => {\n setFocusedId(node.id);\n // Pointer users had no way to expand: only arrow keys toggled (2026-09-30\n // audit WR-0930-2). The contract lets a row click change expansion; the\n // glyph toggles only, and in check mode the row click stays the check.\n const onGlyph = node.hasChildren &&\n (event.target as Element).closest?.(\".hjm-tree__toggle\") !== null;\n if (onGlyph) { setExpanded(node.id, !node.expanded); return; }\n if (checkedStates && onCheckedToggle) { if (!node.disabled) onCheckedToggle(node.id); return; }\n if (node.hasChildren) setExpanded(node.id, !node.expanded);\n toggleSelection(node);\n }}\n onFocus={() => setFocusedId(node.id)}\n onKeyDown={(event) => onKeyDown(event, node)}\n >\n <span aria-hidden=\"true\" className=\"hjm-tree__indent\" />\n {checkedStates ? (\n <span aria-hidden=\"true\" className=\"hjm-tree__check\" data-state={String(checkedStates.get(node.id) ?? false)}>\n {checkedStates.get(node.id) === true ? \"✓\" : checkedStates.get(node.id) === \"mixed\" ? \"–\" : \"\"}\n </span>\n ) : null}\n <span aria-hidden=\"true\" className=\"hjm-tree__toggle\" data-visible={node.hasChildren || undefined}>\n {node.hasChildren ? renderToggle?.({ expanded: node.expanded }) ?? (node.expanded ? \"▾\" : \"▸\") : null}\n </span>\n <span aria-hidden=\"true\" className=\"hjm-tree__label\">\n {node.label}\n {node.description ? <span className=\"hjm-tree__description\">{node.description}</span> : null}\n </span>\n </div>\n ))}\n {asyncState.status === \"loadingMore\" ? <p className=\"hjm-tree__state\" role=\"status\">{asyncState.message}</p> : null}\n </div>\n );\n}) as <Id extends string = string>(\n props: TreeProps<Id> & { ref?: React.Ref<HTMLDivElement> },\n) => React.ReactElement | null;\n"]}
|
|
1
|
+
{"version":3,"file":"tree.js","sourceRoot":"","sources":["../src/tree.tsx"],"names":[],"mappings":";AAAA,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,8BAA8B,EAC9B,4BAA4B,EAC5B,sBAAsB,EACtB,qBAAqB,EACrB,UAAU,GAMX,MAAM,yCAAyC,CAAC;AAEjD,OAAO,EACL,UAAU,EACV,SAAS,EACT,eAAe,EACf,OAAO,EACP,MAAM,EACN,QAAQ,GAIT,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AA0BpD,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAE7B,SAAS,WAAW,CAAoB,SAA6C;IACnF,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,IAAI,KAAK,MAAM;QAAE,OAAO,IAAI,GAAG,EAAM,CAAC;IAClE,IAAI,SAAS,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAChC,MAAM,GAAG,GAAG,SAAS,CAAC,WAAW,IAAI,SAAS,CAAC,kBAAkB,IAAI,IAAI,CAAC;QAC1E,OAAO,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,EAAM,CAAC,CAAC,CAAC,IAAI,GAAG,CAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,SAAS,CAAC,YAAY,IAAI,SAAS,CAAC,mBAAmB,IAAI,IAAI,GAAG,EAAM,CAAC;AAClF,CAAC;AAED,MAAM,CAAC,MAAM,IAAI,GAAG,UAAU,CAAC,SAAS,IAAI,CAC1C,EACE,KAAK,EACL,KAAK,EACL,qBAAqB,EACrB,YAAY,EAAE,kBAAkB,EAChC,mBAAmB,EACnB,oBAAoB,EACpB,SAAS,EACT,aAAa,EACb,eAAe,EACf,UAAU,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,EAC/B,YAAY,EACZ,SAAS,EACT,WAAW,GACG,EAChB,YAAuC;IAEvC,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,oBAAoB,CAAkB;QAC5E,GAAG,CAAC,kBAAkB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC;QAC1E,YAAY,EAAE,mBAAmB,IAAI,IAAI,GAAG,EAAM;QAClD,GAAG,CAAC,oBAAoB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,oBAAoB,EAAE,CAAC;KAClF,CAAC,CAAC;IACH,MAAM,SAAS,GAAG,mBAAmB,EAAE,EAAE,WAAW,CAAC,SAAS,IAAI,KAAK,CAAC;IACxE,6EAA6E;IAC7E,sEAAsE;IACtE,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,sBAAsB,CAAC,KAAK,EAAE,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC;IACvG,MAAM,QAAQ,GAAG,OAAO,CACtB,GAAG,EAAE,CAAC,qBAAqB,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE,qBAAqB,EAAE,CAAC,EAC3E,CAAC,KAAK,EAAE,YAAY,EAAE,qBAAqB,CAAC,CAC7C,CAAC;IACF,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IACnF,oEAAoE;IACpE,+EAA+E;IAC/E,gFAAgF;IAChF,mFAAmF;IACnF,MAAM,mBAAmB,GAAG,SAAS,EAAE,IAAI,KAAK,QAAQ;QACtD,CAAC,CAAC,SAAS,CAAC,WAAW,KAAK,SAAS;QACrC,CAAC,CAAC,SAAS,EAAE,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;IACjF,MAAM,CAAC,oBAAoB,EAAE,uBAAuB,CAAC,GAAG,QAAQ,CAAkB,GAAG,EAAE,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC;IAChH,MAAM,WAAW,GAAG,mBAAmB,CAAC,CAAC,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,oBAAoB,CAAC;IACxF,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,EAAE;QAC5B,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QACvD,MAAM,IAAI,GAAG,CAAC,GAAG,WAAW,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAC5D,OAAO,IAAI,CAAC,MAAM,KAAK,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC;IACxE,CAAC,EAAE,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC5B,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,mBAAmB,IAAI,QAAQ,KAAK,oBAAoB,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,IAAI,KAAK,MAAM;YAAE,OAAO;QAChH,iFAAiF;QACjF,uBAAuB,CAAC,QAAQ,CAAC,CAAC;QAClC,IAAI,SAAS,CAAC,IAAI,KAAK,QAAQ;YAAE,SAAS,CAAC,iBAAiB,EAAE,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC;;YACpF,SAAS,CAAC,iBAAiB,EAAE,CAAC,QAAQ,CAAC,CAAC;IAC/C,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,mBAAmB,EAAE,oBAAoB,CAAC,CAAC,CAAC;IACrE,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAY,IAAI,CAAC,CAAC;IAC5D,8EAA8E;IAC9E,6EAA6E;IAC7E,wEAAwE;IACxE,MAAM,gBAAgB,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC1C,IAAI,UAAU,GAAG,SAAS,CAAC;IAC3B,OAAO,UAAU,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,UAAU,CAAC,EAAE,CAAC;QAC5E,UAAU,GAAG,gBAAgB,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,UAAU,CAAC,EAAE,QAAQ,IAAI,IAAI,CAAC;IAC/F,CAAC;IACD,MAAM,QAAQ,GAAG,UAAU,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,IAAI,CAAC;IACtD,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAClC,MAAM,OAAO,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAC;IAC7C,MAAM,SAAS,GAAG,MAAM,CAA0C,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;IACxF,eAAe,CAAC,GAAG,EAAE;QACnB,gBAAgB,CAAC,OAAO,GAAG,QAAQ,CAAC;QACpC,IAAI,SAAS,KAAK,IAAI,IAAI,QAAQ,KAAK,IAAI,IAAI,SAAS,KAAK,QAAQ;YAAE,OAAO;QAC9E,YAAY,CAAC,QAAQ,CAAC,CAAC;QACvB,6EAA6E;QAC7E,8EAA8E;QAC9E,IAAI,WAAW,CAAC,OAAO,EAAE,CAAC;YACxB,OAAO,CAAC,OAAO,EAAE,aAAa,CAAc,wBAAwB,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC;QACzG,CAAC;IACH,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC;IAEpC,MAAM,SAAS,GAAG,CAAC,EAAM,EAAE,EAAE;QAC3B,YAAY,CAAC,EAAE,CAAC,CAAC;QACjB,OAAO,CAAC,OAAO,EAAE,aAAa,CAAc,wBAAwB,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC;IACnG,CAAC,CAAC;IACF,MAAM,WAAW,GAAG,CAAC,EAAM,EAAE,QAAiB,EAAE,EAAE;QAChD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,CAAC;QACnC,IAAI,QAAQ;YAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;;YAAM,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACjD,eAAe,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC,CAAC;IACF,MAAM,eAAe,GAAG,CAAC,IAAoC,EAAE,EAAE;QAC/D,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,IAAI,KAAK,MAAM;YAAE,OAAO;QACrE,IAAI,SAAS,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAChC,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;YACvD,MAAM,IAAI,GAAG,OAAO,KAAK,IAAI,CAAC,EAAE,IAAI,SAAS,CAAC,sBAAsB,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/F,IAAI,CAAC,mBAAmB;gBAAE,uBAAuB,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,EAAM,CAAC,CAAC,CAAC,IAAI,GAAG,CAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACvG,SAAS,CAAC,iBAAiB,EAAE,CAAC,IAAI,CAAC,CAAC;YACpC,OAAO;QACT,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC/B,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;;YAAM,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACpE,IAAI,CAAC,mBAAmB;YAAE,uBAAuB,CAAC,IAAI,CAAC,CAAC;QACxD,SAAS,CAAC,iBAAiB,EAAE,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC,CAAC;IAEF,MAAM,SAAS,GAAG,CAAC,KAAoC,EAAE,IAAoC,EAAE,EAAE;QAC/F,MAAM,GAAG,GAAG,KAAK,CAAC,GAAqB,CAAC;QACxC,MAAM,KAAK,GAAG,qBAAqB,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QACpD,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,MAAM,GAAG,kBAAkB,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;YAC3D,IAAI,MAAM,CAAC,MAAM,KAAK,MAAM;gBAAE,OAAO;YACrC,KAAK,CAAC,cAAc,EAAE,CAAC;YACvB,IAAI,MAAM,CAAC,MAAM,KAAK,WAAW;gBAAE,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;;gBACzD,WAAW,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC;YACtD,OAAO;QACT,CAAC;QACD,IAAI,GAAG,KAAK,WAAW,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,MAAM,IAAI,GAAG,KAAK,KAAK,EAAE,CAAC;YAChF,MAAM,MAAM,GAAG,GAAG,KAAK,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;YACjH,MAAM,MAAM,GAAG,8BAA8B,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;YACxE,IAAI,MAAM,KAAK,SAAS;gBAAE,OAAO;YACjC,KAAK,CAAC,cAAc,EAAE,CAAC;YACvB,SAAS,CAAC,MAAM,CAAC,CAAC;YAClB,OAAO;QACT,CAAC;QACD,IAAI,GAAG,KAAK,OAAO,IAAI,KAAK,CAAC,GAAG,KAAK,GAAG,EAAE,CAAC;YACzC,KAAK,CAAC,cAAc,EAAE,CAAC;YACvB,IAAI,aAAa,IAAI,eAAe,EAAE,CAAC;gBAAC,IAAI,CAAC,IAAI,CAAC,QAAQ;oBAAE,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAAC,CAAC;;gBAClF,eAAe,CAAC,IAAI,CAAC,CAAC;YAC3B,OAAO;QACT,CAAC;QACD,IAAI,KAAK,CAAC,GAAG,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,MAAM;YAAE,OAAO;QACrF,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,GAAG,GAAG,SAAS,CAAC,OAAO,CAAC,EAAE,GAAG,gBAAgB,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC;QAC9G,SAAS,CAAC,OAAO,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,4BAA4B,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,cAAc,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;QACxF,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YAAC,KAAK,CAAC,cAAc,EAAE,CAAC;YAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAAC,CAAC;IACxE,CAAC,CAAC;IAEF,OAAO,CACL,eACE,GAAG,EAAE,WAAW,CAAC,OAAO,EAAE,YAAY,CAAC,EACvC,IAAI,EAAC,MAAM,EACX,cAAc,EAAE,GAAG,EAAE,GAAG,WAAW,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,EACrD,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE;YACvB,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,aAA4B,CAAC;gBAAE,WAAW,CAAC,OAAO,GAAG,KAAK,CAAC;QACrG,CAAC,gBACW,KAAK,eACN,UAAU,CAAC,MAAM,KAAK,SAAS,IAAI,UAAU,CAAC,MAAM,KAAK,aAAa,IAAI,SAAS,0BACxE,SAAS,EAAE,IAAI,KAAK,UAAU,IAAI,SAAS,EACjE,SAAS,EAAE,UAAU,CAAC,UAAU,EAAE,SAAS,CAAC,EAC5C,KAAK,EAAE,EAAE,GAAG,WAAW,EAAE,mBAAmB,EAAE,GAAG,UAAU,CAAC,cAAc,IAAI,EAAmB,aAEhG,UAAU,CAAC,MAAM,KAAK,OAAO,IAAI,UAAU,CAAC,MAAM,KAAK,OAAO,IAAI,UAAU,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,CACnG,YAAG,SAAS,EAAC,iBAAiB,EAAC,IAAI,EAAE,UAAU,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,YAAG,UAAU,CAAC,OAAO,GAAK,CAClH,CAAC,CAAC,CAAC,IAAI,EACP,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CACrB,eAEE,IAAI,EAAC,UAAU,wBACK,IAAI,CAAC,EAAE,gBACf,IAAI,CAAC,KAAK,mBACP,IAAI,CAAC,QAAQ,kBACd,IAAI,CAAC,YAAY,mBAChB,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,mBAC5C,SAAS,IAAI,SAAS,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,kBAC3E,aAAa,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC,CAA+B,CAAC,CAAC,CAAC,SAAS,mBACzJ,IAAI,CAAC,QAAQ,IAAI,SAAS,gBAC7B,IAAI,CAAC,cAAc,EAC/B,QAAQ,EAAE,IAAI,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EACvC,SAAS,EAAC,gBAAgB,EAC1B,KAAK,EAAE,EAAE,kBAAkB,EAAE,IAAI,CAAC,KAAK,GAAG,CAAC,EAAmB,EAC9D,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;oBACjB,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;oBACtB,0EAA0E;oBAC1E,wEAAwE;oBACxE,uEAAuE;oBACvE,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW;wBAC7B,KAAK,CAAC,MAAkB,CAAC,OAAO,EAAE,CAAC,mBAAmB,CAAC,KAAK,IAAI,CAAC;oBACpE,IAAI,OAAO,EAAE,CAAC;wBAAC,WAAW,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;wBAAC,OAAO;oBAAC,CAAC;oBAC9D,IAAI,aAAa,IAAI,eAAe,EAAE,CAAC;wBAAC,IAAI,CAAC,IAAI,CAAC,QAAQ;4BAAE,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;wBAAC,OAAO;oBAAC,CAAC;oBAC/F,IAAI,IAAI,CAAC,WAAW;wBAAE,WAAW,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;oBAC3D,eAAe,CAAC,IAAI,CAAC,CAAC;gBACxB,CAAC,EACD,OAAO,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,EACpC,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,aAE5C,8BAAkB,MAAM,EAAC,SAAS,EAAC,kBAAkB,GAAG,EACvD,aAAa,CAAC,CAAC,CAAC,CACf,8BAAkB,MAAM,EAAC,SAAS,EAAC,iBAAiB,gBAAa,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,CAAC,YACzG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GACzF,CACR,CAAC,CAAC,CAAC,IAAI,EACR,8BAAkB,MAAM,EAAC,SAAS,EAAC,kBAAkB,kBAAe,IAAI,CAAC,WAAW,IAAI,SAAS,YAC9F,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAChG,EACP,+BAAkB,MAAM,EAAC,SAAS,EAAC,iBAAiB,aACjD,IAAI,CAAC,KAAK,EACV,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,eAAM,SAAS,EAAC,uBAAuB,YAAE,IAAI,CAAC,WAAW,GAAQ,CAAC,CAAC,CAAC,IAAI,IACvF,KAzCF,IAAI,CAAC,EAAE,CA0CR,CACP,CAAC,EACD,UAAU,CAAC,MAAM,KAAK,aAAa,CAAC,CAAC,CAAC,YAAG,SAAS,EAAC,iBAAiB,EAAC,IAAI,EAAC,QAAQ,YAAE,UAAU,CAAC,OAAO,GAAK,CAAC,CAAC,CAAC,IAAI,IAC/G,CACP,CAAC;AACJ,CAAC,CAE6B,CAAC","sourcesContent":["import {\n getTreeArrowKeyIntent,\n getTreeArrowResult,\n getVisibleTreeNavigationTarget,\n getVisibleTreeTypeaheadMatch,\n reconcileTreeExpansion,\n resolveTreeDescriptor,\n treeRecipe,\n type ComposeTreeAccessibleName,\n type ResolvedTreeNodeDescriptor,\n type TreeAsyncState,\n type TreeNodeDescriptor,\n type TreeSelectionModel,\n} from \"@hjmds/design-contracts/components/tree\";\nimport type { CheckboxState, WebKeyboardKey } from \"@hjmds/design-contracts/behaviors\";\nimport {\n forwardRef,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type KeyboardEvent,\n type ReactNode,\n} from \"react\";\nimport { classNames, composeRefs, useControllableState } from \"./internal.js\";\nimport { useOptionalHjmTheme } from \"./provider.js\";\nimport type { HjmCompositionStyleProp } from \"./composition-style.js\";\n\nexport type TreeProps<Id extends string = string> = Readonly<{\n label: string;\n nodes: readonly TreeNodeDescriptor<Id>[];\n composeAccessibleName: ComposeTreeAccessibleName;\n expandedKeys?: ReadonlySet<Id>;\n defaultExpandedKeys?: ReadonlySet<Id>;\n onExpandedKeysChange?: (keys: ReadonlySet<Id>) => void;\n selection?: TreeSelectionModel<Id>;\n /**\n * Tri-state check marks per node, as `resolveTreeCheckedStates` derives them.\n * Checkboxes are not nested controls in a tree: the state rides on the node\n * itself as `aria-checked`, keeping the contract's one-tab-stop-per-node rule.\n */\n checkedStates?: ReadonlyMap<Id, CheckboxState>;\n onCheckedToggle?: (id: Id) => void;\n asyncState?: TreeAsyncState;\n /** Product-owned glyph for the expand/collapse affordance; decorative by contract. */\n renderToggle?: (state: Readonly<{ expanded: boolean }>) => ReactNode;\n className?: string;\n /** Canonical layout-only placement on the root element. Controlled visual keys are excluded. */\n layoutStyle?: HjmCompositionStyleProp;\n}>;\n\nconst typeaheadResetMs = 500;\n\nfunction selectedIds<Id extends string>(selection: TreeSelectionModel<Id> | undefined): ReadonlySet<Id> {\n if (!selection || selection.mode === \"none\") return new Set<Id>();\n if (selection.mode === \"single\") {\n const key = selection.selectedKey ?? selection.defaultSelectedKey ?? null;\n return key === null ? new Set<Id>() : new Set<Id>([key]);\n }\n return selection.selectedKeys ?? selection.defaultSelectedKeys ?? new Set<Id>();\n}\n\nexport const Tree = forwardRef(function Tree<Id extends string = string>(\n {\n label,\n nodes,\n composeAccessibleName,\n expandedKeys: controlledExpanded,\n defaultExpandedKeys,\n onExpandedKeysChange,\n selection,\n checkedStates,\n onCheckedToggle,\n asyncState = { status: \"idle\" },\n renderToggle,\n className,\n layoutStyle,\n }: TreeProps<Id>,\n forwardedRef: React.Ref<HTMLDivElement>,\n) {\n const [expandedKeys, setExpandedKeys] = useControllableState<ReadonlySet<Id>>({\n ...(controlledExpanded === undefined ? {} : { value: controlledExpanded }),\n defaultValue: defaultExpandedKeys ?? new Set<Id>(),\n ...(onExpandedKeysChange === undefined ? {} : { onChange: onExpandedKeysChange }),\n });\n const direction = useOptionalHjmTheme()?.environment.direction ?? \"ltr\";\n // Expansion keys survive a node list change only where the node still exists\n // and can still hold children; the contract owns that reconciliation.\n const liveExpanded = useMemo(() => reconcileTreeExpansion(nodes, expandedKeys), [nodes, expandedKeys]);\n const resolved = useMemo(\n () => resolveTreeDescriptor(nodes, liveExpanded, { composeAccessibleName }),\n [nodes, liveExpanded, composeAccessibleName],\n );\n const visible = useMemo(() => resolved.filter((node) => node.visible), [resolved]);\n // `defaultSelectedKey(s)` used to be re-read on every render, so an\n // uncontrolled tree reported the click to `onSelectionChange` but kept showing\n // the default. Keep the uncontrolled value in state like `useControllableState`\n // does for expansion; a controlled `selectedKey(s)` (including `null`) still wins.\n const selectionControlled = selection?.mode === \"single\"\n ? selection.selectedKey !== undefined\n : selection?.mode === \"multiple\" ? selection.selectedKeys !== undefined : true;\n const [uncontrolledSelected, setUncontrolledSelected] = useState<ReadonlySet<Id>>(() => selectedIds(selection));\n const rawSelected = selectionControlled ? selectedIds(selection) : uncontrolledSelected;\n const selected = useMemo(() => {\n const liveIds = new Set(resolved.map(node => node.id));\n const keys = [...rawSelected].filter(id => liveIds.has(id));\n return keys.length === rawSelected.size ? rawSelected : new Set(keys);\n }, [rawSelected, resolved]);\n useEffect(() => {\n if (selectionControlled || selected === uncontrolledSelected || !selection || selection.mode === \"none\") return;\n // A removed node must not leak into the next callback or revive when reinserted.\n setUncontrolledSelected(selected);\n if (selection.mode === \"single\") selection.onSelectionChange?.([...selected][0] ?? null);\n else selection.onSelectionChange?.(selected);\n }, [selected, selection, selectionControlled, uncontrolledSelected]);\n const [focusedId, setFocusedId] = useState<Id | null>(null);\n // Roving tab stop: one node is tabbable, and it must stay a real visible node\n // after a collapse removed the previously focused descendant. The decorative\n // glyph is not a nested control, so each row has only one focus target.\n const previousResolved = useRef(resolved);\n let fallbackId = focusedId;\n while (fallbackId !== null && !visible.some(node => node.id === fallbackId)) {\n fallbackId = previousResolved.current.find(node => node.id === fallbackId)?.parentId ?? null;\n }\n const activeId = fallbackId ?? visible[0]?.id ?? null;\n const focusWithin = useRef(false);\n const rootRef = useRef<HTMLDivElement>(null);\n const typeahead = useRef<Readonly<{ query: string; at: number }>>({ query: \"\", at: 0 });\n useLayoutEffect(() => {\n previousResolved.current = resolved;\n if (focusedId === null || activeId === null || focusedId === activeId) return;\n setFocusedId(activeId);\n // External collapse/removal can unmount the focused row without firing blur.\n // Restore its surviving ancestor only while focus still belongs to this tree.\n if (focusWithin.current) {\n rootRef.current?.querySelector<HTMLElement>(`[data-hjm-tree-node=\"${CSS.escape(activeId)}\"]`)?.focus();\n }\n }, [activeId, focusedId, resolved]);\n\n const focusNode = (id: Id) => {\n setFocusedId(id);\n rootRef.current?.querySelector<HTMLElement>(`[data-hjm-tree-node=\"${CSS.escape(id)}\"]`)?.focus();\n };\n const setExpanded = (id: Id, expanded: boolean) => {\n const next = new Set(liveExpanded);\n if (expanded) next.add(id); else next.delete(id);\n setExpandedKeys(next);\n };\n const toggleSelection = (node: ResolvedTreeNodeDescriptor<Id>) => {\n if (node.disabled || !selection || selection.mode === \"none\") return;\n if (selection.mode === \"single\") {\n const current = selected.has(node.id) ? node.id : null;\n const next = current === node.id && selection.disallowEmptySelection !== true ? null : node.id;\n if (!selectionControlled) setUncontrolledSelected(next === null ? new Set<Id>() : new Set<Id>([next]));\n selection.onSelectionChange?.(next);\n return;\n }\n const next = new Set(selected);\n if (next.has(node.id)) next.delete(node.id); else next.add(node.id);\n if (!selectionControlled) setUncontrolledSelected(next);\n selection.onSelectionChange?.(next);\n };\n\n const onKeyDown = (event: KeyboardEvent<HTMLDivElement>, node: ResolvedTreeNodeDescriptor<Id>) => {\n const key = event.key as WebKeyboardKey;\n const arrow = getTreeArrowKeyIntent(key, direction);\n if (arrow) {\n const result = getTreeArrowResult(visible, node.id, arrow);\n if (result.action === \"none\") return;\n event.preventDefault();\n if (result.action === \"moveFocus\") focusNode(result.targetId);\n else setExpanded(node.id, result.action === \"expand\");\n return;\n }\n if (key === \"ArrowDown\" || key === \"ArrowUp\" || key === \"Home\" || key === \"End\") {\n const intent = key === \"ArrowDown\" ? \"next\" : key === \"ArrowUp\" ? \"previous\" : key === \"Home\" ? \"first\" : \"last\";\n const target = getVisibleTreeNavigationTarget(visible, node.id, intent);\n if (target === undefined) return;\n event.preventDefault();\n focusNode(target);\n return;\n }\n if (key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n if (checkedStates && onCheckedToggle) { if (!node.disabled) onCheckedToggle(node.id); }\n else toggleSelection(node);\n return;\n }\n if (event.key.length !== 1 || event.metaKey || event.ctrlKey || event.altKey) return;\n const now = Date.now();\n const query = now - typeahead.current.at > typeaheadResetMs ? event.key : typeahead.current.query + event.key;\n typeahead.current = { query, at: now };\n const match = getVisibleTreeTypeaheadMatch(visible, query, { startsAfterKey: node.id });\n if (match !== undefined) { event.preventDefault(); focusNode(match); }\n };\n\n return (\n <div\n ref={composeRefs(rootRef, forwardedRef)}\n role=\"tree\"\n onFocusCapture={() => { focusWithin.current = true; }}\n onBlurCapture={(event) => {\n if (!event.currentTarget.contains(event.relatedTarget as Node | null)) focusWithin.current = false;\n }}\n aria-label={label}\n aria-busy={asyncState.status === \"loading\" || asyncState.status === \"loadingMore\" || undefined}\n aria-multiselectable={selection?.mode === \"multiple\" || undefined}\n className={classNames(\"hjm-tree\", className)}\n style={{ ...layoutStyle, \"--hjm-tree-indent\": `${treeRecipe.indentPerLevel}px` } as CSSProperties}\n >\n {asyncState.status === \"empty\" || asyncState.status === \"error\" || asyncState.status === \"loading\" ? (\n <p className=\"hjm-tree__state\" role={asyncState.status === \"error\" ? \"alert\" : \"status\"}>{asyncState.message}</p>\n ) : null}\n {visible.map((node) => (\n <div\n key={node.id}\n role=\"treeitem\"\n data-hjm-tree-node={node.id}\n aria-level={node.depth}\n aria-posinset={node.position}\n aria-setsize={node.siblingCount}\n aria-expanded={node.hasChildren ? node.expanded : undefined}\n aria-selected={selection && selection.mode !== \"none\" ? selected.has(node.id) : undefined}\n aria-checked={checkedStates ? (checkedStates.get(node.id) === \"mixed\" ? \"mixed\" : String(checkedStates.get(node.id) === true)) as \"true\" | \"false\" | \"mixed\" : undefined}\n aria-disabled={node.disabled || undefined}\n aria-label={node.accessibleName}\n tabIndex={node.id === activeId ? 0 : -1}\n className=\"hjm-tree__node\"\n style={{ \"--hjm-tree-depth\": node.depth - 1 } as CSSProperties}\n onClick={(event) => {\n setFocusedId(node.id);\n // Pointer users had no way to expand: only arrow keys toggled (2026-09-30\n // audit WR-0930-2). The contract lets a row click change expansion; the\n // glyph toggles only, and in check mode the row click stays the check.\n const onGlyph = node.hasChildren &&\n (event.target as Element).closest?.(\".hjm-tree__toggle\") !== null;\n if (onGlyph) { setExpanded(node.id, !node.expanded); return; }\n if (checkedStates && onCheckedToggle) { if (!node.disabled) onCheckedToggle(node.id); return; }\n if (node.hasChildren) setExpanded(node.id, !node.expanded);\n toggleSelection(node);\n }}\n onFocus={() => setFocusedId(node.id)}\n onKeyDown={(event) => onKeyDown(event, node)}\n >\n <span aria-hidden=\"true\" className=\"hjm-tree__indent\" />\n {checkedStates ? (\n <span aria-hidden=\"true\" className=\"hjm-tree__check\" data-state={String(checkedStates.get(node.id) ?? false)}>\n {checkedStates.get(node.id) === true ? \"✓\" : checkedStates.get(node.id) === \"mixed\" ? \"–\" : \"\"}\n </span>\n ) : null}\n <span aria-hidden=\"true\" className=\"hjm-tree__toggle\" data-visible={node.hasChildren || undefined}>\n {node.hasChildren ? renderToggle?.({ expanded: node.expanded }) ?? (node.expanded ? \"▾\" : \"▸\") : null}\n </span>\n <span aria-hidden=\"true\" className=\"hjm-tree__label\">\n {node.label}\n {node.description ? <span className=\"hjm-tree__description\">{node.description}</span> : null}\n </span>\n </div>\n ))}\n {asyncState.status === \"loadingMore\" ? <p className=\"hjm-tree__state\" role=\"status\">{asyncState.message}</p> : null}\n </div>\n );\n}) as <Id extends string = string>(\n props: TreeProps<Id> & { ref?: React.Ref<HTMLDivElement> },\n) => React.ReactElement | null;\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"virtual-list.d.ts","sourceRoot":"","sources":["../src/virtual-list.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAqD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE1F,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACtE,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI;IAAE,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC;IAAC,YAAY,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,CAAC;IAAC,UAAU,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IACpO,mHAAmH;IACnH,WAAW,CAAC,EAAE,uBAAuB,CAAA;CAAE,CAAC;AAC1C,gGAAgG;AAChG,wBAAgB,WAAW,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,QAAY,EAAE,WAAW,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC,+
|
|
1
|
+
{"version":3,"file":"virtual-list.d.ts","sourceRoot":"","sources":["../src/virtual-list.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAqD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE1F,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACtE,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI;IAAE,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC;IAAC,YAAY,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,CAAC;IAAC,UAAU,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IACpO,mHAAmH;IACnH,WAAW,CAAC,EAAE,uBAAuB,CAAA;CAAE,CAAC;AAC1C,gGAAgG;AAChG,wBAAgB,WAAW,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,QAAY,EAAE,WAAW,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC,+BAyDlJ"}
|