termcast 1.3.10 → 1.3.18
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/apis/toast.d.ts +1 -5
- package/dist/apis/toast.d.ts.map +1 -1
- package/dist/apis/toast.js +23 -17
- package/dist/apis/toast.js.map +1 -1
- package/dist/build.d.ts +2 -0
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +4 -3
- package/dist/build.js.map +1 -1
- package/dist/cli.js +42 -0
- package/dist/cli.js.map +1 -1
- package/dist/colors.d.ts +8 -1
- package/dist/colors.d.ts.map +1 -1
- package/dist/colors.js +12 -0
- package/dist/colors.js.map +1 -1
- package/dist/compile.d.ts +30 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +156 -0
- package/dist/compile.js.map +1 -0
- package/dist/components/actions.d.ts +1 -0
- package/dist/components/actions.d.ts.map +1 -1
- package/dist/components/actions.js +5 -2
- package/dist/components/actions.js.map +1 -1
- package/dist/components/command-arguments.d.ts +9 -0
- package/dist/components/command-arguments.d.ts.map +1 -0
- package/dist/components/command-arguments.js +21 -0
- package/dist/components/command-arguments.js.map +1 -0
- package/dist/components/detail.d.ts +1 -1
- package/dist/components/detail.d.ts.map +1 -1
- package/dist/components/detail.js +17 -39
- package/dist/components/detail.js.map +1 -1
- package/dist/components/dropdown.d.ts +1 -0
- package/dist/components/dropdown.d.ts.map +1 -1
- package/dist/components/dropdown.js +77 -21
- package/dist/components/dropdown.js.map +1 -1
- package/dist/components/extension-preferences.d.ts.map +1 -1
- package/dist/components/extension-preferences.js +19 -29
- package/dist/components/extension-preferences.js.map +1 -1
- package/dist/components/form/checkbox.d.ts.map +1 -1
- package/dist/components/form/checkbox.js +9 -2
- package/dist/components/form/checkbox.js.map +1 -1
- package/dist/components/form/date-picker.d.ts.map +1 -1
- package/dist/components/form/date-picker.js +15 -28
- package/dist/components/form/date-picker.js.map +1 -1
- package/dist/components/form/description.d.ts +2 -0
- package/dist/components/form/description.d.ts.map +1 -1
- package/dist/components/form/description.js +20 -2
- package/dist/components/form/description.js.map +1 -1
- package/dist/components/form/dropdown.d.ts.map +1 -1
- package/dist/components/form/dropdown.js +13 -28
- package/dist/components/form/dropdown.js.map +1 -1
- package/dist/components/form/file-autocomplete.d.ts +7 -4
- package/dist/components/form/file-autocomplete.d.ts.map +1 -1
- package/dist/components/form/file-autocomplete.js +54 -46
- package/dist/components/form/file-autocomplete.js.map +1 -1
- package/dist/components/form/file-picker.d.ts +5 -0
- package/dist/components/form/file-picker.d.ts.map +1 -1
- package/dist/components/form/file-picker.js +46 -49
- package/dist/components/form/file-picker.js.map +1 -1
- package/dist/components/form/form-ref.d.ts +43 -0
- package/dist/components/form/form-ref.d.ts.map +1 -0
- package/dist/components/form/form-ref.js +53 -0
- package/dist/components/form/form-ref.js.map +1 -0
- package/dist/components/form/index.d.ts +16 -0
- package/dist/components/form/index.d.ts.map +1 -1
- package/dist/components/form/index.js +86 -23
- package/dist/components/form/index.js.map +1 -1
- package/dist/components/form/password-field.d.ts.map +1 -1
- package/dist/components/form/password-field.js +32 -16
- package/dist/components/form/password-field.js.map +1 -1
- package/dist/components/form/text-area.d.ts.map +1 -1
- package/dist/components/form/text-area.js +32 -15
- package/dist/components/form/text-area.js.map +1 -1
- package/dist/components/form/text-field.d.ts.map +1 -1
- package/dist/components/form/text-field.js +37 -17
- package/dist/components/form/text-field.js.map +1 -1
- package/dist/components/form/use-form-navigation.d.ts +4 -0
- package/dist/components/form/use-form-navigation.d.ts.map +1 -1
- package/dist/components/form/use-form-navigation.js +35 -18
- package/dist/components/form/use-form-navigation.js.map +1 -1
- package/dist/components/form/with-left-border.d.ts.map +1 -1
- package/dist/components/form/with-left-border.js +2 -2
- package/dist/components/form/with-left-border.js.map +1 -1
- package/dist/components/icon.d.ts +3 -1
- package/dist/components/icon.d.ts.map +1 -1
- package/dist/components/icon.js +494 -469
- package/dist/components/icon.js.map +1 -1
- package/dist/components/list.d.ts +11 -7
- package/dist/components/list.d.ts.map +1 -1
- package/dist/components/list.js +170 -38
- package/dist/components/list.js.map +1 -1
- package/dist/components/loading-bar.js +1 -1
- package/dist/components/loading-bar.js.map +1 -1
- package/dist/descendants.d.ts.map +1 -1
- package/dist/descendants.js +8 -5
- package/dist/descendants.js.map +1 -1
- package/dist/examples/action-show-in-finder.js +1 -1
- package/dist/examples/action-show-in-finder.js.map +1 -1
- package/dist/examples/environment-test.js +1 -1
- package/dist/examples/environment-test.js.map +1 -1
- package/dist/examples/error-boundary.js +1 -1
- package/dist/examples/error-boundary.js.map +1 -1
- package/dist/examples/form-basic.d.ts.map +1 -1
- package/dist/examples/form-basic.js +2 -2
- package/dist/examples/form-basic.js.map +1 -1
- package/dist/examples/form-dropdown.js +1 -1
- package/dist/examples/form-dropdown.js.map +1 -1
- package/dist/examples/form-scroll.d.ts +2 -0
- package/dist/examples/form-scroll.d.ts.map +1 -0
- package/dist/examples/form-scroll.js +8 -0
- package/dist/examples/form-scroll.js.map +1 -0
- package/dist/examples/form-tagpicker.js +1 -1
- package/dist/examples/form-tagpicker.js.map +1 -1
- package/dist/examples/internal/descendants-filtering.js +8 -3
- package/dist/examples/internal/descendants-filtering.js.map +1 -1
- package/dist/examples/internal/descendants.js +10 -3
- package/dist/examples/internal/descendants.js.map +1 -1
- package/dist/examples/internal/rhf-custom-ref.d.ts +2 -0
- package/dist/examples/internal/rhf-custom-ref.d.ts.map +1 -0
- package/dist/examples/internal/rhf-custom-ref.js +67 -0
- package/dist/examples/internal/rhf-custom-ref.js.map +1 -0
- package/dist/examples/internal/scrollbox-demo.js +3 -7
- package/dist/examples/internal/scrollbox-demo.js.map +1 -1
- package/dist/examples/internal/scrollbox-with-descendants.d.ts +2 -0
- package/dist/examples/internal/scrollbox-with-descendants.d.ts.map +1 -0
- package/dist/examples/internal/scrollbox-with-descendants.js +63 -0
- package/dist/examples/internal/scrollbox-with-descendants.js.map +1 -0
- package/dist/examples/internal/simple-dialog.js +1 -1
- package/dist/examples/internal/simple-dialog.js.map +1 -1
- package/dist/examples/internal/simple-scrollbox.js +2 -3
- package/dist/examples/internal/simple-scrollbox.js.map +1 -1
- package/dist/examples/internal/text-stacking.js +4 -2
- package/dist/examples/internal/text-stacking.js.map +1 -1
- package/dist/examples/list-dropdown-default.js +1 -1
- package/dist/examples/list-dropdown-default.js.map +1 -1
- package/dist/examples/list-fetch-data.js +1 -1
- package/dist/examples/list-fetch-data.js.map +1 -1
- package/dist/examples/list-scrollbox.d.ts +2 -0
- package/dist/examples/list-scrollbox.d.ts.map +1 -0
- package/dist/examples/list-scrollbox.js +29 -0
- package/dist/examples/list-scrollbox.js.map +1 -0
- package/dist/examples/list-with-detail.js +1 -1
- package/dist/examples/list-with-detail.js.map +1 -1
- package/dist/examples/list-with-dropdown.js +1 -1
- package/dist/examples/list-with-dropdown.js.map +1 -1
- package/dist/examples/list-with-sections.js +3 -3
- package/dist/examples/list-with-sections.js.map +1 -1
- package/dist/examples/miscellaneous.js +1 -1
- package/dist/examples/miscellaneous.js.map +1 -1
- package/dist/examples/nested-navigation.js +4 -2
- package/dist/examples/nested-navigation.js.map +1 -1
- package/dist/examples/preferences-test.js +1 -1
- package/dist/examples/preferences-test.js.map +1 -1
- package/dist/examples/simple-dropdown.js +1 -1
- package/dist/examples/simple-dropdown.js.map +1 -1
- package/dist/examples/simple-file-picker.js +1 -1
- package/dist/examples/simple-file-picker.js.map +1 -1
- package/dist/examples/simple-grid.js +1 -1
- package/dist/examples/simple-grid.js.map +1 -1
- package/dist/examples/simple-hud.js +1 -1
- package/dist/examples/simple-hud.js.map +1 -1
- package/dist/examples/simple-list-search.js +1 -1
- package/dist/examples/simple-list-search.js.map +1 -1
- package/dist/examples/simple-list.js +1 -1
- package/dist/examples/simple-list.js.map +1 -1
- package/dist/examples/simple-navigation.js +4 -2
- package/dist/examples/simple-navigation.js.map +1 -1
- package/dist/examples/store.js +1 -1
- package/dist/examples/store.js.map +1 -1
- package/dist/examples/submodule-diff.d.ts +2 -0
- package/dist/examples/submodule-diff.d.ts.map +1 -0
- package/dist/examples/submodule-diff.js +99 -0
- package/dist/examples/submodule-diff.js.map +1 -0
- package/dist/examples/tanstack-demo.js +1 -1
- package/dist/examples/tanstack-demo.js.map +1 -1
- package/dist/examples/use-promise-demo.js +1 -1
- package/dist/examples/use-promise-demo.js.map +1 -1
- package/dist/extensions/dev.d.ts +8 -0
- package/dist/extensions/dev.d.ts.map +1 -1
- package/dist/extensions/dev.js +53 -28
- package/dist/extensions/dev.js.map +1 -1
- package/dist/extensions/home.d.ts.map +1 -1
- package/dist/extensions/home.js +16 -91
- package/dist/extensions/home.js.map +1 -1
- package/dist/globals.js +1 -1
- package/dist/globals.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/date-picker-widget.d.ts.map +1 -1
- package/dist/internal/date-picker-widget.js +56 -48
- package/dist/internal/date-picker-widget.js.map +1 -1
- package/dist/internal/dialog.d.ts.map +1 -1
- package/dist/internal/dialog.js +7 -7
- package/dist/internal/dialog.js.map +1 -1
- package/dist/internal/scrollbox.d.ts +2 -1
- package/dist/internal/scrollbox.d.ts.map +1 -1
- package/dist/internal/scrollbox.js +13 -7
- package/dist/internal/scrollbox.js.map +1 -1
- package/dist/release.d.ts +11 -0
- package/dist/release.d.ts.map +1 -0
- package/dist/release.js +113 -0
- package/dist/release.js.map +1 -0
- package/dist/state.d.ts +1 -0
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +1 -0
- package/dist/state.js.map +1 -1
- package/dist/theme.d.ts +11 -8
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +31 -8
- package/dist/theme.js.map +1 -1
- package/dist/utils/run-command.d.ts +22 -0
- package/dist/utils/run-command.d.ts.map +1 -0
- package/dist/utils/run-command.js +127 -0
- package/dist/utils/run-command.js.map +1 -0
- package/dist/utils.d.ts +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +5 -3
- package/dist/utils.js.map +1 -1
- package/package.json +10 -7
- package/src/apis/toast.tsx +38 -17
- package/src/build.tsx +5 -3
- package/src/cli.tsx +46 -0
- package/src/colors.tsx +22 -1
- package/src/compile.tsx +219 -0
- package/src/components/actions.tsx +7 -1
- package/src/components/command-arguments.tsx +81 -0
- package/src/components/detail.tsx +26 -58
- package/src/components/dropdown.tsx +108 -23
- package/src/components/extension-preferences.tsx +55 -35
- package/src/components/form/checkbox.tsx +13 -5
- package/src/components/form/date-picker.tsx +24 -29
- package/src/components/form/description.tsx +35 -7
- package/src/components/form/dropdown.tsx +16 -30
- package/src/components/form/file-autocomplete.tsx +87 -77
- package/src/components/form/file-picker.tsx +69 -57
- package/src/components/form/form-ref.tsx +68 -0
- package/src/components/form/index.tsx +152 -41
- package/src/components/form/password-field.tsx +35 -22
- package/src/components/form/text-area.tsx +78 -58
- package/src/components/form/text-field.tsx +82 -61
- package/src/components/form/use-form-navigation.tsx +43 -23
- package/src/components/form/with-left-border.tsx +2 -1
- package/src/components/icon.tsx +497 -469
- package/src/components/list.tsx +279 -112
- package/src/components/loading-bar.tsx +1 -1
- package/src/descendants.tsx +15 -5
- package/src/examples/action-show-in-finder.tsx +1 -1
- package/src/examples/environment-test.tsx +1 -1
- package/src/examples/error-boundary.tsx +1 -1
- package/src/examples/file-autocomplete.vitest.tsx +245 -0
- package/src/examples/form-basic.tsx +12 -12
- package/src/examples/form-basic.vitest.tsx +297 -671
- package/src/examples/form-dropdown.tsx +1 -1
- package/src/examples/form-dropdown.vitest.tsx +353 -221
- package/src/examples/form-scroll.tsx +56 -0
- package/src/examples/form-scroll.vitest.tsx +228 -0
- package/src/examples/form-tagpicker.tsx +1 -1
- package/src/examples/form-tagpicker.vitest.tsx +438 -193
- package/src/examples/internal/descendants-filtering.tsx +13 -5
- package/src/examples/internal/descendants.tsx +17 -5
- package/src/examples/internal/rhf-custom-ref.tsx +152 -0
- package/src/examples/internal/scrollbox-demo.tsx +15 -7
- package/src/examples/internal/scrollbox-with-descendants.tsx +94 -0
- package/src/examples/internal/simple-dialog.tsx +1 -1
- package/src/examples/internal/simple-scrollbox.tsx +8 -5
- package/src/examples/internal/simple-scrollbox.vitest.tsx +47 -37
- package/src/examples/internal/text-stacking.tsx +4 -2
- package/src/examples/list-dropdown-default.tsx +1 -1
- package/src/examples/list-dropdown-default.vitest.tsx +136 -71
- package/src/examples/list-fetch-data.tsx +1 -1
- package/src/examples/list-fetch-data.vitest.tsx +42 -33
- package/src/examples/list-scrollbox.tsx +46 -0
- package/src/examples/list-scrollbox.vitest.tsx +103 -0
- package/src/examples/list-with-detail.tsx +1 -1
- package/src/examples/list-with-detail.vitest.tsx +290 -294
- package/src/examples/list-with-dropdown.tsx +1 -1
- package/src/examples/list-with-dropdown.vitest.tsx +190 -150
- package/src/examples/list-with-sections.tsx +12 -1
- package/src/examples/list-with-sections.vitest.tsx +390 -218
- package/src/examples/miscellaneous.tsx +1 -1
- package/src/examples/nested-navigation.tsx +4 -2
- package/src/examples/preferences-test.tsx +1 -1
- package/src/examples/simple-dropdown.tsx +1 -1
- package/src/examples/simple-file-picker.tsx +1 -1
- package/src/examples/simple-file-picker.vitest.tsx +538 -132
- package/src/examples/simple-grid.tsx +1 -1
- package/src/examples/simple-grid.vitest.tsx +258 -234
- package/src/examples/simple-hud.tsx +1 -1
- package/src/examples/simple-list-search.tsx +1 -1
- package/src/examples/simple-list.tsx +1 -1
- package/src/examples/simple-navigation.tsx +4 -2
- package/src/examples/simple-navigation.vitest.tsx +434 -238
- package/src/examples/store.tsx +1 -1
- package/src/examples/store.vitest.tsx +31 -10
- package/src/examples/submodule-diff.tsx +153 -0
- package/src/examples/tanstack-demo.tsx +1 -1
- package/src/examples/use-promise-demo.tsx +1 -1
- package/src/extensions/dev.tsx +74 -36
- package/src/extensions/dev.vitest.tsx +220 -0
- package/src/extensions/home.tsx +17 -118
- package/src/globals.ts +1 -1
- package/src/index.tsx +7 -1
- package/src/internal/date-picker-widget.tsx +56 -46
- package/src/internal/dialog.tsx +6 -6
- package/src/internal/scrollbox.tsx +15 -5
- package/src/release.tsx +159 -0
- package/src/state.tsx +2 -0
- package/src/store-api/search.test.tsx +4 -13
- package/src/theme.tsx +33 -8
- package/src/utils/run-command.tsx +204 -0
- package/src/utils.tsx +5 -3
- package/dist/ai.d.ts +0 -104
- package/dist/ai.d.ts.map +0 -1
- package/dist/ai.js +0 -135
- package/dist/ai.js.map +0 -1
- package/dist/apis/cache.test.d.ts +0 -2
- package/dist/apis/cache.test.d.ts.map +0 -1
- package/dist/apis/cache.test.js +0 -246
- package/dist/apis/cache.test.js.map +0 -1
- package/dist/apis/localstorage.test.d.ts +0 -2
- package/dist/apis/localstorage.test.d.ts.map +0 -1
- package/dist/apis/localstorage.test.js +0 -131
- package/dist/apis/localstorage.test.js.map +0 -1
- package/dist/apis/toast.test.d.ts +0 -2
- package/dist/apis/toast.test.d.ts.map +0 -1
- package/dist/apis/toast.test.js +0 -67
- package/dist/apis/toast.test.js.map +0 -1
- package/dist/build.test.d.ts +0 -2
- package/dist/build.test.d.ts.map +0 -1
- package/dist/build.test.js +0 -73
- package/dist/build.test.js.map +0 -1
- package/dist/cache.d.ts +0 -32
- package/dist/cache.d.ts.map +0 -1
- package/dist/cache.js +0 -205
- package/dist/cache.js.map +0 -1
- package/dist/cache.test.d.ts +0 -2
- package/dist/cache.test.d.ts.map +0 -1
- package/dist/cache.test.js +0 -246
- package/dist/cache.test.js.map +0 -1
- package/dist/clipboard.d.ts +0 -36
- package/dist/clipboard.d.ts.map +0 -1
- package/dist/clipboard.js +0 -154
- package/dist/clipboard.js.map +0 -1
- package/dist/components/form/form-type-only.d.ts +0 -174
- package/dist/components/form/form-type-only.d.ts.map +0 -1
- package/dist/components/form/form-type-only.js +0 -2
- package/dist/components/form/form-type-only.js.map +0 -1
- package/dist/components/form/use-form-handling.d.ts +0 -4
- package/dist/components/form/use-form-handling.d.ts.map +0 -1
- package/dist/components/form/use-form-handling.js +0 -37
- package/dist/components/form/use-form-handling.js.map +0 -1
- package/dist/dev-ui.d.ts +0 -7
- package/dist/dev-ui.d.ts.map +0 -1
- package/dist/dev-ui.js +0 -118
- package/dist/dev-ui.js.map +0 -1
- package/dist/environment.d.ts +0 -63
- package/dist/environment.d.ts.map +0 -1
- package/dist/environment.js +0 -189
- package/dist/environment.js.map +0 -1
- package/dist/examples/datepicker.d.ts +0 -2
- package/dist/examples/datepicker.d.ts.map +0 -1
- package/dist/examples/datepicker.js +0 -344
- package/dist/examples/datepicker.js.map +0 -1
- package/dist/examples/form-basic-arrow-keys.vitest.d.ts +0 -2
- package/dist/examples/form-basic-arrow-keys.vitest.d.ts.map +0 -1
- package/dist/examples/form-basic-arrow-keys.vitest.js +0 -46
- package/dist/examples/form-basic-arrow-keys.vitest.js.map +0 -1
- package/dist/examples/form-basic.vitest.d.ts +0 -2
- package/dist/examples/form-basic.vitest.d.ts.map +0 -1
- package/dist/examples/form-basic.vitest.js +0 -995
- package/dist/examples/form-basic.vitest.js.map +0 -1
- package/dist/examples/form-dropdown-with-sections.d.ts +0 -2
- package/dist/examples/form-dropdown-with-sections.d.ts.map +0 -1
- package/dist/examples/form-dropdown-with-sections.js +0 -13
- package/dist/examples/form-dropdown-with-sections.js.map +0 -1
- package/dist/examples/form-dropdown-with-sections.vitest.d.ts +0 -2
- package/dist/examples/form-dropdown-with-sections.vitest.d.ts.map +0 -1
- package/dist/examples/form-dropdown-with-sections.vitest.js +0 -75
- package/dist/examples/form-dropdown-with-sections.vitest.js.map +0 -1
- package/dist/examples/form-dropdown.vitest.d.ts +0 -2
- package/dist/examples/form-dropdown.vitest.d.ts.map +0 -1
- package/dist/examples/form-dropdown.vitest.js +0 -722
- package/dist/examples/form-dropdown.vitest.js.map +0 -1
- package/dist/examples/form-multiselect-dropdown.d.ts +0 -2
- package/dist/examples/form-multiselect-dropdown.d.ts.map +0 -1
- package/dist/examples/form-multiselect-dropdown.js +0 -13
- package/dist/examples/form-multiselect-dropdown.js.map +0 -1
- package/dist/examples/form-tagpicker.vitest.d.ts +0 -2
- package/dist/examples/form-tagpicker.vitest.d.ts.map +0 -1
- package/dist/examples/form-tagpicker.vitest.js +0 -491
- package/dist/examples/form-tagpicker.vitest.js.map +0 -1
- package/dist/examples/internal/nested-boxes.d.ts +0 -2
- package/dist/examples/internal/nested-boxes.d.ts.map +0 -1
- package/dist/examples/internal/nested-boxes.js +0 -7
- package/dist/examples/internal/nested-boxes.js.map +0 -1
- package/dist/examples/internal/simple-scrollbox.vitest.d.ts +0 -2
- package/dist/examples/internal/simple-scrollbox.vitest.d.ts.map +0 -1
- package/dist/examples/internal/simple-scrollbox.vitest.js +0 -88
- package/dist/examples/internal/simple-scrollbox.vitest.js.map +0 -1
- package/dist/examples/internal/unicode-square-repro.d.ts +0 -2
- package/dist/examples/internal/unicode-square-repro.d.ts.map +0 -1
- package/dist/examples/internal/unicode-square-repro.js +0 -7
- package/dist/examples/internal/unicode-square-repro.js.map +0 -1
- package/dist/examples/list-dropdown-default.vitest.d.ts +0 -2
- package/dist/examples/list-dropdown-default.vitest.d.ts.map +0 -1
- package/dist/examples/list-dropdown-default.vitest.js +0 -164
- package/dist/examples/list-dropdown-default.vitest.js.map +0 -1
- package/dist/examples/list-fetch-data.vitest.d.ts +0 -2
- package/dist/examples/list-fetch-data.vitest.d.ts.map +0 -1
- package/dist/examples/list-fetch-data.vitest.js +0 -103
- package/dist/examples/list-fetch-data.vitest.js.map +0 -1
- package/dist/examples/list-filter-navigation.d.ts +0 -2
- package/dist/examples/list-filter-navigation.d.ts.map +0 -1
- package/dist/examples/list-filter-navigation.js +0 -8
- package/dist/examples/list-filter-navigation.js.map +0 -1
- package/dist/examples/list-with-detail.vitest.d.ts +0 -2
- package/dist/examples/list-with-detail.vitest.d.ts.map +0 -1
- package/dist/examples/list-with-detail.vitest.js +0 -438
- package/dist/examples/list-with-detail.vitest.js.map +0 -1
- package/dist/examples/list-with-dropdown.vitest.d.ts +0 -2
- package/dist/examples/list-with-dropdown.vitest.d.ts.map +0 -1
- package/dist/examples/list-with-dropdown.vitest.js +0 -297
- package/dist/examples/list-with-dropdown.vitest.js.map +0 -1
- package/dist/examples/list-with-sections.vitest.d.ts +0 -2
- package/dist/examples/list-with-sections.vitest.d.ts.map +0 -1
- package/dist/examples/list-with-sections.vitest.js +0 -441
- package/dist/examples/list-with-sections.vitest.js.map +0 -1
- package/dist/examples/simple-file-picker.vitest.d.ts +0 -2
- package/dist/examples/simple-file-picker.vitest.d.ts.map +0 -1
- package/dist/examples/simple-file-picker.vitest.js +0 -277
- package/dist/examples/simple-file-picker.vitest.js.map +0 -1
- package/dist/examples/simple-grid.vitest.d.ts +0 -2
- package/dist/examples/simple-grid.vitest.d.ts.map +0 -1
- package/dist/examples/simple-grid.vitest.js +0 -498
- package/dist/examples/simple-grid.vitest.js.map +0 -1
- package/dist/examples/simple-navigation.vitest.d.ts +0 -2
- package/dist/examples/simple-navigation.vitest.d.ts.map +0 -1
- package/dist/examples/simple-navigation.vitest.js +0 -522
- package/dist/examples/simple-navigation.vitest.js.map +0 -1
- package/dist/examples/store.vitest.d.ts +0 -2
- package/dist/examples/store.vitest.d.ts.map +0 -1
- package/dist/examples/store.vitest.js +0 -48
- package/dist/examples/store.vitest.js.map +0 -1
- package/dist/home-command.d.ts +0 -8
- package/dist/home-command.d.ts.map +0 -1
- package/dist/home-command.js +0 -181
- package/dist/home-command.js.map +0 -1
- package/dist/hooks/hooks.test.d.ts +0 -2
- package/dist/hooks/hooks.test.d.ts.map +0 -1
- package/dist/hooks/hooks.test.js +0 -37
- package/dist/hooks/hooks.test.js.map +0 -1
- package/dist/hover-repro.d.ts +0 -2
- package/dist/hover-repro.d.ts.map +0 -1
- package/dist/hover-repro.js +0 -20
- package/dist/hover-repro.js.map +0 -1
- package/dist/localstorage.d.ts +0 -13
- package/dist/localstorage.d.ts.map +0 -1
- package/dist/localstorage.js +0 -190
- package/dist/localstorage.js.map +0 -1
- package/dist/localstorage.test.d.ts +0 -2
- package/dist/localstorage.test.d.ts.map +0 -1
- package/dist/localstorage.test.js +0 -131
- package/dist/localstorage.test.js.map +0 -1
- package/dist/oauth.d.ts +0 -142
- package/dist/oauth.d.ts.map +0 -1
- package/dist/oauth.js +0 -551
- package/dist/oauth.js.map +0 -1
- package/dist/preferences.d.ts +0 -23
- package/dist/preferences.d.ts.map +0 -1
- package/dist/preferences.js +0 -105
- package/dist/preferences.js.map +0 -1
- package/dist/store-api/download.test.d.ts +0 -2
- package/dist/store-api/download.test.d.ts.map +0 -1
- package/dist/store-api/download.test.js +0 -36
- package/dist/store-api/download.test.js.map +0 -1
- package/dist/store-api/extension.test.d.ts +0 -2
- package/dist/store-api/extension.test.d.ts.map +0 -1
- package/dist/store-api/extension.test.js +0 -22
- package/dist/store-api/extension.test.js.map +0 -1
- package/dist/store-api/search.test.d.ts +0 -2
- package/dist/store-api/search.test.d.ts.map +0 -1
- package/dist/store-api/search.test.js +0 -45
- package/dist/store-api/search.test.js.map +0 -1
- package/dist/store.d.ts +0 -21
- package/dist/store.d.ts.map +0 -1
- package/dist/store.js +0 -84
- package/dist/store.js.map +0 -1
- package/dist/toast.d.ts +0 -44
- package/dist/toast.d.ts.map +0 -1
- package/dist/toast.js +0 -221
- package/dist/toast.js.map +0 -1
- package/dist/utils.test.d.ts +0 -2
- package/dist/utils.test.d.ts.map +0 -1
- package/dist/utils.test.js +0 -152
- package/dist/utils.test.js.map +0 -1
- package/dist/window.d.ts +0 -12
- package/dist/window.d.ts.map +0 -1
- package/dist/window.js +0 -48
- package/dist/window.js.map +0 -1
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import React from 'react'
|
|
1
|
+
import React, { useRef } from 'react'
|
|
2
2
|
import { Theme } from 'termcast/src/theme'
|
|
3
|
+
import { BoxRenderable, TextareaRenderable } from '@opentui/core'
|
|
3
4
|
import { WithLeftBorder } from './with-left-border'
|
|
4
5
|
import { FormItemProps, FormItemRef } from './types'
|
|
5
6
|
import { useFormContext, Controller } from 'react-hook-form'
|
|
6
|
-
import { useFocusContext } from './index'
|
|
7
|
+
import { useFocusContext, useFormFieldDescendant } from './index'
|
|
7
8
|
import { useKeyboard } from '@opentui/react'
|
|
8
9
|
import { useIsInFocus } from 'termcast/src/internal/focus-context'
|
|
9
10
|
import { FileAutocomplete } from './file-autocomplete'
|
|
11
|
+
import { useFormNavigationHelpers } from './use-form-navigation'
|
|
10
12
|
|
|
11
13
|
export interface FilePickerProps extends FormItemProps<string[]> {
|
|
12
14
|
/**
|
|
@@ -34,6 +36,11 @@ export interface FilePickerProps extends FormItemProps<string[]> {
|
|
|
34
36
|
* Placeholder text for the input field
|
|
35
37
|
*/
|
|
36
38
|
placeholder?: string
|
|
39
|
+
/**
|
|
40
|
+
* The initial directory to start browsing from.
|
|
41
|
+
* @defaultValue current working directory
|
|
42
|
+
*/
|
|
43
|
+
initialDirectory?: string
|
|
37
44
|
}
|
|
38
45
|
|
|
39
46
|
export type FilePickerRef = FormItemRef
|
|
@@ -54,28 +61,34 @@ const FilePickerField = ({
|
|
|
54
61
|
setFocusedField: (id: string) => void
|
|
55
62
|
}): any => {
|
|
56
63
|
const isInFocus = useIsInFocus()
|
|
57
|
-
const [inputValue, setInputValue] = React.useState('')
|
|
58
64
|
const [showAutocomplete, setShowAutocomplete] = React.useState(false)
|
|
59
|
-
const
|
|
65
|
+
const [searchTrigger, setSearchTrigger] = React.useState(0)
|
|
66
|
+
const inputRef = React.useRef<TextareaRenderable>(null)
|
|
60
67
|
const anchorRef = React.useRef<any>(null)
|
|
61
68
|
|
|
62
|
-
//
|
|
63
|
-
React.useEffect(() => {
|
|
64
|
-
if (inputValue && isFocused) {
|
|
65
|
-
setShowAutocomplete(true)
|
|
66
|
-
} else if (!inputValue) {
|
|
67
|
-
setShowAutocomplete(false)
|
|
68
|
-
}
|
|
69
|
-
}, [inputValue, isFocused])
|
|
70
|
-
|
|
71
|
-
// Handle Enter key
|
|
69
|
+
// Handle Enter key and left arrow for removing last file
|
|
72
70
|
useKeyboard((evt) => {
|
|
73
71
|
if (!isFocused || !isInFocus) return
|
|
74
72
|
|
|
73
|
+
// Left arrow removes last selected file when input is empty
|
|
74
|
+
if (evt.name === 'left') {
|
|
75
|
+
const inputValue = inputRef.current?.plainText || ''
|
|
76
|
+
if (!inputValue && selectedFiles.length > 0) {
|
|
77
|
+
const newFiles = selectedFiles.slice(0, -1)
|
|
78
|
+
field.onChange(newFiles)
|
|
79
|
+
if (props.onChange) {
|
|
80
|
+
props.onChange(newFiles)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
|
|
75
86
|
if (evt.name === 'return') {
|
|
87
|
+
const inputValue = inputRef.current?.plainText || ''
|
|
76
88
|
// If input is empty, show files in current directory
|
|
77
89
|
if (!inputValue && !showAutocomplete) {
|
|
78
90
|
setShowAutocomplete(true)
|
|
91
|
+
setSearchTrigger((n) => n + 1)
|
|
79
92
|
}
|
|
80
93
|
// If autocomplete is not visible and input has value, add the path
|
|
81
94
|
else if (inputValue.trim() && !showAutocomplete) {
|
|
@@ -88,7 +101,7 @@ const FilePickerField = ({
|
|
|
88
101
|
if (props.onChange) {
|
|
89
102
|
props.onChange(newFiles)
|
|
90
103
|
}
|
|
91
|
-
|
|
104
|
+
inputRef.current?.setText('')
|
|
92
105
|
}
|
|
93
106
|
}
|
|
94
107
|
})
|
|
@@ -101,7 +114,7 @@ const FilePickerField = ({
|
|
|
101
114
|
if (props.onChange) {
|
|
102
115
|
props.onChange(newFiles)
|
|
103
116
|
}
|
|
104
|
-
|
|
117
|
+
inputRef.current?.setText('')
|
|
105
118
|
setShowAutocomplete(false)
|
|
106
119
|
}
|
|
107
120
|
|
|
@@ -111,7 +124,7 @@ const FilePickerField = ({
|
|
|
111
124
|
<box flexDirection='column'>
|
|
112
125
|
<WithLeftBorder withDiamond isFocused={isFocused}>
|
|
113
126
|
<text
|
|
114
|
-
fg={Theme.text}
|
|
127
|
+
fg={isFocused ? Theme.primary : Theme.text}
|
|
115
128
|
onMouseDown={() => {
|
|
116
129
|
setFocusedField(props.id)
|
|
117
130
|
}}
|
|
@@ -121,13 +134,26 @@ const FilePickerField = ({
|
|
|
121
134
|
</WithLeftBorder>
|
|
122
135
|
<WithLeftBorder isFocused={isFocused}>
|
|
123
136
|
<box flexDirection='column' ref={anchorRef}>
|
|
124
|
-
<
|
|
137
|
+
<textarea
|
|
125
138
|
ref={inputRef}
|
|
126
|
-
|
|
139
|
+
height={1}
|
|
140
|
+
keyBindings={[
|
|
141
|
+
{ name: 'return', action: 'submit' },
|
|
142
|
+
{ name: 'linefeed', action: 'submit' },
|
|
143
|
+
]}
|
|
144
|
+
initialValue=""
|
|
127
145
|
placeholder={props.placeholder || 'Enter file path...'}
|
|
128
146
|
focused={isFocused}
|
|
129
147
|
onMouseDown={() => setFocusedField(props.id)}
|
|
130
|
-
|
|
148
|
+
onContentChange={() => {
|
|
149
|
+
const value = inputRef.current?.plainText || ''
|
|
150
|
+
if (value && isFocused) {
|
|
151
|
+
setShowAutocomplete(true)
|
|
152
|
+
setSearchTrigger((n) => n + 1)
|
|
153
|
+
} else if (!value) {
|
|
154
|
+
setShowAutocomplete(false)
|
|
155
|
+
}
|
|
156
|
+
}}
|
|
131
157
|
/>
|
|
132
158
|
{selectedFiles.length > 0 && (
|
|
133
159
|
<box flexDirection='column' marginTop={1}>
|
|
@@ -154,51 +180,35 @@ const FilePickerField = ({
|
|
|
154
180
|
</WithLeftBorder>
|
|
155
181
|
)}
|
|
156
182
|
<FileAutocomplete
|
|
157
|
-
value={inputValue}
|
|
158
|
-
onChange={(newValue) => {
|
|
159
|
-
setInputValue(newValue)
|
|
160
|
-
// Move cursor to end when value changes
|
|
161
|
-
if (inputRef.current) {
|
|
162
|
-
inputRef.current.cursorPosition = newValue.length
|
|
163
|
-
}
|
|
164
|
-
}}
|
|
165
183
|
onSelect={handleSelectFile}
|
|
166
184
|
visible={showAutocomplete}
|
|
167
185
|
onVisibilityChange={setShowAutocomplete}
|
|
168
186
|
inputRef={inputRef}
|
|
169
187
|
anchorRef={anchorRef}
|
|
188
|
+
searchTrigger={searchTrigger}
|
|
189
|
+
canChooseFiles={props.canChooseFiles}
|
|
190
|
+
canChooseDirectories={props.canChooseDirectories}
|
|
191
|
+
initialDirectory={props.initialDirectory}
|
|
170
192
|
/>
|
|
171
193
|
</box>
|
|
172
194
|
) as React.ReactElement
|
|
173
195
|
}
|
|
174
196
|
|
|
175
197
|
export const FilePicker = (props: FilePickerProps): any => {
|
|
176
|
-
const { control
|
|
198
|
+
const { control } = useFormContext()
|
|
177
199
|
const { focusedField, setFocusedField } = useFocusContext()
|
|
178
200
|
const isFocused = focusedField === props.id
|
|
179
201
|
const isInFocus = useIsInFocus()
|
|
180
202
|
|
|
181
|
-
const
|
|
182
|
-
// Find previous field and focus it
|
|
183
|
-
const fieldNames = Object.keys(getValues())
|
|
184
|
-
const currentIndex = fieldNames.indexOf(props.id)
|
|
185
|
-
if (currentIndex > 0) {
|
|
186
|
-
setFocusedField(fieldNames[currentIndex - 1])
|
|
187
|
-
} else {
|
|
188
|
-
setFocusedField(fieldNames[fieldNames.length - 1])
|
|
189
|
-
}
|
|
190
|
-
}
|
|
203
|
+
const elementRef = useRef<BoxRenderable>(null)
|
|
191
204
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
setFocusedField(fieldNames[0])
|
|
200
|
-
}
|
|
201
|
-
}
|
|
205
|
+
// Register as form field descendant for scroll support
|
|
206
|
+
useFormFieldDescendant({
|
|
207
|
+
id: props.id,
|
|
208
|
+
elementRef: elementRef.current,
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
const { navigateToPrevious, navigateToNext } = useFormNavigationHelpers(props.id)
|
|
202
212
|
|
|
203
213
|
// Handle keyboard navigation
|
|
204
214
|
useKeyboard((evt) => {
|
|
@@ -206,9 +216,9 @@ export const FilePicker = (props: FilePickerProps): any => {
|
|
|
206
216
|
|
|
207
217
|
if (evt.name === 'tab') {
|
|
208
218
|
if (evt.shift) {
|
|
209
|
-
|
|
219
|
+
navigateToPrevious()
|
|
210
220
|
} else {
|
|
211
|
-
|
|
221
|
+
navigateToNext()
|
|
212
222
|
}
|
|
213
223
|
}
|
|
214
224
|
})
|
|
@@ -220,12 +230,14 @@ export const FilePicker = (props: FilePickerProps): any => {
|
|
|
220
230
|
defaultValue={props.defaultValue || props.value || []}
|
|
221
231
|
render={(renderProps) => {
|
|
222
232
|
return (
|
|
223
|
-
<
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
233
|
+
<box ref={elementRef} flexDirection='column'>
|
|
234
|
+
<FilePickerField
|
|
235
|
+
{...renderProps}
|
|
236
|
+
props={props}
|
|
237
|
+
isFocused={isFocused}
|
|
238
|
+
setFocusedField={setFocusedField}
|
|
239
|
+
/>
|
|
240
|
+
</box>
|
|
229
241
|
) as React.ReactElement
|
|
230
242
|
}}
|
|
231
243
|
/>
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { TextareaRenderable } from '@opentui/core'
|
|
3
|
+
import { UseFormRegisterReturn } from 'react-hook-form'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Creates a fake ref object that satisfies react-hook-form's register() requirements.
|
|
7
|
+
* RHF expects a ref with:
|
|
8
|
+
* - `name`: field name
|
|
9
|
+
* - `value`: getter/setter for reading/writing the field value
|
|
10
|
+
* - `focus()`: optional, for focusing on validation errors
|
|
11
|
+
*
|
|
12
|
+
* This allows using register() directly with non-HTML inputs like opentui's textarea.
|
|
13
|
+
*/
|
|
14
|
+
export function createReactHookFormRef(options: {
|
|
15
|
+
name: string
|
|
16
|
+
getValue: () => string
|
|
17
|
+
setValue: (value: string) => void
|
|
18
|
+
focus?: () => void
|
|
19
|
+
}) {
|
|
20
|
+
return {
|
|
21
|
+
name: options.name,
|
|
22
|
+
get value() {
|
|
23
|
+
return options.getValue()
|
|
24
|
+
},
|
|
25
|
+
set value(v: string) {
|
|
26
|
+
options.setValue(v)
|
|
27
|
+
},
|
|
28
|
+
focus: options.focus || (() => {}),
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Creates a react-hook-form adapter for opentui's TextareaRenderable.
|
|
34
|
+
*
|
|
35
|
+
* Returns:
|
|
36
|
+
* - `formRef`: A fake ref object that RHF can use to get/set values
|
|
37
|
+
* - `onContentChange`: Handler to trigger RHF's onChange
|
|
38
|
+
* - `refCallback`: A memoized ref callback to pass to the textarea's ref prop
|
|
39
|
+
*
|
|
40
|
+
* IMPORTANT: The refCallback must be memoized (via useCallback) at the call site
|
|
41
|
+
* because register() returns a new object on every render. Passing register().ref
|
|
42
|
+
* directly to the textarea ref prop causes unnecessary re-renders.
|
|
43
|
+
*/
|
|
44
|
+
export function createTextareaFormRef(
|
|
45
|
+
name: string,
|
|
46
|
+
textareaRef: React.RefObject<TextareaRenderable | null>,
|
|
47
|
+
registration: UseFormRegisterReturn<string>,
|
|
48
|
+
) {
|
|
49
|
+
const formRef = createReactHookFormRef({
|
|
50
|
+
name,
|
|
51
|
+
getValue: () => textareaRef.current?.plainText || '',
|
|
52
|
+
setValue: (v) => {
|
|
53
|
+
textareaRef.current?.setText(v)
|
|
54
|
+
},
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
const onContentChange = () => {
|
|
58
|
+
registration.onChange({
|
|
59
|
+
target: formRef,
|
|
60
|
+
type: 'change',
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
formRef,
|
|
66
|
+
onContentChange,
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, {
|
|
2
|
+
useState,
|
|
3
|
+
createContext,
|
|
4
|
+
useContext,
|
|
5
|
+
useLayoutEffect,
|
|
6
|
+
useRef,
|
|
7
|
+
} from 'react'
|
|
2
8
|
import { useKeyboard } from '@opentui/react'
|
|
3
9
|
import { useForm, FormProvider } from 'react-hook-form'
|
|
4
10
|
import { ActionPanel } from 'termcast/src/components/actions'
|
|
@@ -6,8 +12,16 @@ import { logger } from 'termcast/src/logger'
|
|
|
6
12
|
import { InFocus, useIsInFocus } from 'termcast/src/internal/focus-context'
|
|
7
13
|
import { useDialog } from 'termcast/src/internal/dialog'
|
|
8
14
|
import { Theme } from 'termcast/src/theme'
|
|
9
|
-
import {
|
|
10
|
-
|
|
15
|
+
import {
|
|
16
|
+
TextAttributes,
|
|
17
|
+
ScrollBoxRenderable,
|
|
18
|
+
BoxRenderable,
|
|
19
|
+
} from '@opentui/core'
|
|
20
|
+
|
|
21
|
+
import {
|
|
22
|
+
createDescendants,
|
|
23
|
+
DescendantContextType,
|
|
24
|
+
} from 'termcast/src/descendants'
|
|
11
25
|
import {
|
|
12
26
|
FormValues,
|
|
13
27
|
FormProps,
|
|
@@ -20,10 +34,39 @@ import {
|
|
|
20
34
|
FormProps_2,
|
|
21
35
|
FormItemProps_2,
|
|
22
36
|
} from './types'
|
|
37
|
+
import { FORM_MAX_WIDTH } from './description'
|
|
38
|
+
import { ScrollBox } from 'termcast/src/internal/scrollbox'
|
|
23
39
|
|
|
24
40
|
export * from './types'
|
|
25
41
|
export { useFormContext } from 'react-hook-form'
|
|
26
42
|
|
|
43
|
+
// Form field descendant type - stores element ref for scrolling
|
|
44
|
+
interface FormFieldDescendant {
|
|
45
|
+
id: string
|
|
46
|
+
elementRef?: BoxRenderable | null
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Create descendants for form fields
|
|
50
|
+
const {
|
|
51
|
+
DescendantsProvider: FormFieldDescendantsProvider,
|
|
52
|
+
useDescendants: useFormFieldDescendants,
|
|
53
|
+
useDescendant: useFormFieldDescendant,
|
|
54
|
+
} = createDescendants<FormFieldDescendant>()
|
|
55
|
+
|
|
56
|
+
export { useFormFieldDescendant }
|
|
57
|
+
|
|
58
|
+
// Context to provide scrollbox ref and descendants to form fields
|
|
59
|
+
interface FormScrollContextValue {
|
|
60
|
+
scrollBoxRef: React.RefObject<ScrollBoxRenderable | null>
|
|
61
|
+
descendantsContext: DescendantContextType<FormFieldDescendant>
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const FormScrollContext = createContext<FormScrollContextValue | null>(null)
|
|
65
|
+
|
|
66
|
+
export const useFormScrollContext = () => {
|
|
67
|
+
return useContext(FormScrollContext)
|
|
68
|
+
}
|
|
69
|
+
|
|
27
70
|
// Context for managing focused field
|
|
28
71
|
interface FocusContextValue {
|
|
29
72
|
focusedField: string | null
|
|
@@ -52,26 +95,7 @@ export const useFormSubmit = () => {
|
|
|
52
95
|
return context // Can be null if not in a form
|
|
53
96
|
}
|
|
54
97
|
|
|
55
|
-
// Footer component to show keyboard shortcuts
|
|
56
98
|
function FormFooter(): any {
|
|
57
|
-
const toast = useStore((state) => state.toast)
|
|
58
|
-
|
|
59
|
-
if (toast) {
|
|
60
|
-
return (
|
|
61
|
-
<box
|
|
62
|
-
border={false}
|
|
63
|
-
style={{
|
|
64
|
-
paddingLeft: 1,
|
|
65
|
-
paddingRight: 1,
|
|
66
|
-
paddingTop: 1,
|
|
67
|
-
marginTop: 1,
|
|
68
|
-
}}
|
|
69
|
-
>
|
|
70
|
-
{toast}
|
|
71
|
-
</box>
|
|
72
|
-
)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
99
|
return (
|
|
76
100
|
<box
|
|
77
101
|
border={false}
|
|
@@ -83,16 +107,14 @@ function FormFooter(): any {
|
|
|
83
107
|
flexDirection: 'row',
|
|
84
108
|
}}
|
|
85
109
|
>
|
|
86
|
-
<text fg={Theme.text} attributes={TextAttributes.BOLD}>
|
|
87
|
-
↵
|
|
88
|
-
</text>
|
|
110
|
+
<text fg={Theme.text} attributes={TextAttributes.BOLD}>ctrl ↵</text>
|
|
89
111
|
<text fg={Theme.textMuted}> submit</text>
|
|
90
112
|
<text fg={Theme.text} attributes={TextAttributes.BOLD}>
|
|
91
113
|
{' '}↑↓
|
|
92
114
|
</text>
|
|
93
115
|
<text fg={Theme.textMuted}> navigate</text>
|
|
94
116
|
<text fg={Theme.text} attributes={TextAttributes.BOLD}>
|
|
95
|
-
|
|
117
|
+
{' '}^k
|
|
96
118
|
</text>
|
|
97
119
|
<text fg={Theme.textMuted}> actions</text>
|
|
98
120
|
</box>
|
|
@@ -157,14 +179,54 @@ export const Form: FormType = ((props) => {
|
|
|
157
179
|
// mode: 'onChange',
|
|
158
180
|
})
|
|
159
181
|
|
|
160
|
-
const [focusedField,
|
|
182
|
+
const [focusedField, setFocusedFieldRaw] = useState<string | null>(null)
|
|
183
|
+
|
|
184
|
+
const scrollBoxRef = useRef<ScrollBoxRenderable>(null)
|
|
185
|
+
const descendantsContext = useFormFieldDescendants()
|
|
186
|
+
|
|
187
|
+
const scrollToField = (fieldId: string) => {
|
|
188
|
+
const scrollBox = scrollBoxRef.current
|
|
189
|
+
if (!scrollBox) return
|
|
190
|
+
|
|
191
|
+
// Find field in descendants map by matching props.id
|
|
192
|
+
const field = Object.values(descendantsContext.map.current).find(
|
|
193
|
+
(item) => item.props?.id === fieldId,
|
|
194
|
+
)
|
|
195
|
+
const elementRef = field?.props?.elementRef
|
|
196
|
+
if (!elementRef) return
|
|
161
197
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
198
|
+
const contentY = scrollBox.content?.y || 0
|
|
199
|
+
const viewportHeight = scrollBox.viewport?.height || 10
|
|
200
|
+
const currentScrollTop = scrollBox.scrollTop || 0
|
|
201
|
+
|
|
202
|
+
// Access current position from the BoxRenderable ref
|
|
203
|
+
const itemTop = elementRef.y - contentY
|
|
204
|
+
const itemBottom = itemTop + elementRef.height
|
|
205
|
+
|
|
206
|
+
if (itemTop < currentScrollTop) {
|
|
207
|
+
scrollBox.scrollTo(itemTop)
|
|
208
|
+
} else if (itemBottom > currentScrollTop + viewportHeight) {
|
|
209
|
+
scrollBox.scrollTo(itemBottom - viewportHeight)
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const setFocusedField = (id: string | null) => {
|
|
214
|
+
setFocusedFieldRaw(id)
|
|
215
|
+
if (id) {
|
|
216
|
+
scrollToField(id)
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Auto-focus first field after children have registered as descendants
|
|
221
|
+
useLayoutEffect(() => {
|
|
222
|
+
const descendants = Object.values(descendantsContext.map.current)
|
|
223
|
+
.filter((item) => item.index !== -1 && item.props?.id)
|
|
224
|
+
.sort((a, b) => a.index - b.index)
|
|
225
|
+
|
|
226
|
+
if (descendants.length > 0) {
|
|
227
|
+
const firstId = descendants[0].props!.id
|
|
228
|
+
logger.log(`focusing `, firstId)
|
|
229
|
+
setFocusedFieldRaw(firstId)
|
|
168
230
|
} else {
|
|
169
231
|
logger.log(`no fields to focus in form`)
|
|
170
232
|
}
|
|
@@ -174,11 +236,28 @@ export const Form: FormType = ((props) => {
|
|
|
174
236
|
const inFocus = useIsInFocus()
|
|
175
237
|
const dialog = useDialog()
|
|
176
238
|
|
|
177
|
-
// Handle action
|
|
239
|
+
// Handle action keys and page scrolling
|
|
178
240
|
useKeyboard((evt) => {
|
|
179
241
|
// Only handle keyboard events when form is in focus
|
|
180
242
|
if (!inFocus) return
|
|
181
243
|
|
|
244
|
+
// Page up/down scrolling
|
|
245
|
+
if (evt.name === 'pageup' || evt.name === 'pagedown') {
|
|
246
|
+
const scrollBox = scrollBoxRef.current
|
|
247
|
+
if (!scrollBox) return
|
|
248
|
+
|
|
249
|
+
const viewportHeight = scrollBox.viewport?.height || 10
|
|
250
|
+
const currentScrollTop = scrollBox.scrollTop || 0
|
|
251
|
+
const scrollAmount = viewportHeight - 2 // Leave some overlap
|
|
252
|
+
|
|
253
|
+
if (evt.name === 'pageup') {
|
|
254
|
+
scrollBox.scrollTo(Math.max(0, currentScrollTop - scrollAmount))
|
|
255
|
+
} else {
|
|
256
|
+
scrollBox.scrollTo(currentScrollTop + scrollAmount)
|
|
257
|
+
}
|
|
258
|
+
return
|
|
259
|
+
}
|
|
260
|
+
|
|
182
261
|
if (evt.name === 'k' && evt.ctrl && props.actions) {
|
|
183
262
|
// Ctrl+K shows actions
|
|
184
263
|
dialog.push(
|
|
@@ -187,6 +266,14 @@ export const Form: FormType = ((props) => {
|
|
|
187
266
|
</FormSubmitContext.Provider>,
|
|
188
267
|
'bottom-right',
|
|
189
268
|
)
|
|
269
|
+
} else if (evt.name === 'return' && evt.ctrl && props.actions) {
|
|
270
|
+
// Ctrl+Return shows actions (form submit shortcut)
|
|
271
|
+
dialog.push(
|
|
272
|
+
<FormSubmitContext.Provider value={submitContextValue}>
|
|
273
|
+
{props.actions}
|
|
274
|
+
</FormSubmitContext.Provider>,
|
|
275
|
+
'bottom-right',
|
|
276
|
+
)
|
|
190
277
|
} else if (evt.name === 'return' && evt.meta && props.actions) {
|
|
191
278
|
// Cmd+Return also shows actions (consistent with List)
|
|
192
279
|
dialog.push(
|
|
@@ -202,16 +289,40 @@ export const Form: FormType = ((props) => {
|
|
|
202
289
|
getFormValues: () => methods.getValues(),
|
|
203
290
|
}
|
|
204
291
|
|
|
292
|
+
const scrollContextValue: FormScrollContextValue = {
|
|
293
|
+
scrollBoxRef,
|
|
294
|
+
descendantsContext,
|
|
295
|
+
}
|
|
296
|
+
|
|
205
297
|
return (
|
|
206
298
|
<FormProvider {...methods}>
|
|
207
299
|
<FormSubmitContext.Provider value={submitContextValue}>
|
|
208
|
-
<
|
|
209
|
-
<
|
|
210
|
-
{
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
300
|
+
<FormScrollContext.Provider value={scrollContextValue}>
|
|
301
|
+
<FocusContext.Provider value={{ focusedField, setFocusedField }}>
|
|
302
|
+
<box flexDirection='row' flexGrow={1} justifyContent='center'>
|
|
303
|
+
<box flexDirection='column'>
|
|
304
|
+
<ScrollBox
|
|
305
|
+
ref={scrollBoxRef}
|
|
306
|
+
flexGrow={1}
|
|
307
|
+
style={{
|
|
308
|
+
rootOptions: {
|
|
309
|
+
maxWidth: FORM_MAX_WIDTH,
|
|
310
|
+
},
|
|
311
|
+
contentOptions: {
|
|
312
|
+
justifyContent: 'center',
|
|
313
|
+
},
|
|
314
|
+
}}
|
|
315
|
+
>
|
|
316
|
+
<FormFieldDescendantsProvider value={descendantsContext}>
|
|
317
|
+
{props.children}
|
|
318
|
+
<FormEnd />
|
|
319
|
+
</FormFieldDescendantsProvider>
|
|
320
|
+
</ScrollBox>
|
|
321
|
+
<FormFooter />
|
|
322
|
+
</box>
|
|
323
|
+
</box>
|
|
324
|
+
</FocusContext.Provider>
|
|
325
|
+
</FormScrollContext.Provider>
|
|
215
326
|
</FormSubmitContext.Provider>
|
|
216
327
|
</FormProvider>
|
|
217
328
|
)
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import React, { useState } from 'react'
|
|
2
|
-
import {
|
|
1
|
+
import React, { useRef, useState } from 'react'
|
|
2
|
+
import { BoxRenderable } from '@opentui/core'
|
|
3
3
|
import { useFormContext, Controller } from 'react-hook-form'
|
|
4
|
-
import { useFocusContext } from './index'
|
|
4
|
+
import { useFocusContext, useFormFieldDescendant } from './index'
|
|
5
5
|
import { FormItemProps, FormItemRef } from './types'
|
|
6
|
-
import { logger } from 'termcast/src/logger'
|
|
7
6
|
import { Theme } from 'termcast/src/theme'
|
|
8
7
|
import { WithLeftBorder } from './with-left-border'
|
|
9
8
|
import { useFormNavigation } from './use-form-navigation'
|
|
@@ -18,8 +17,15 @@ export const PasswordField = (props: PasswordFieldProps): any => {
|
|
|
18
17
|
const { control } = useFormContext()
|
|
19
18
|
const { focusedField, setFocusedField } = useFocusContext()
|
|
20
19
|
const isFocused = focusedField === props.id
|
|
20
|
+
const elementRef = useRef<BoxRenderable>(null)
|
|
21
|
+
const realValueRef = useRef(props.defaultValue || props.value || '')
|
|
22
|
+
const [displayLength, setDisplayLength] = useState(realValueRef.current.length)
|
|
23
|
+
|
|
24
|
+
useFormFieldDescendant({
|
|
25
|
+
id: props.id,
|
|
26
|
+
elementRef: elementRef.current,
|
|
27
|
+
})
|
|
21
28
|
|
|
22
|
-
// Use form navigation hook
|
|
23
29
|
useFormNavigation(props.id)
|
|
24
30
|
|
|
25
31
|
return (
|
|
@@ -27,17 +33,14 @@ export const PasswordField = (props: PasswordFieldProps): any => {
|
|
|
27
33
|
name={props.id}
|
|
28
34
|
control={control}
|
|
29
35
|
defaultValue={props.defaultValue || props.value || ''}
|
|
30
|
-
render={({ field, fieldState
|
|
31
|
-
|
|
32
|
-
const displayValue = isFocused
|
|
33
|
-
? field.value
|
|
34
|
-
: '*'.repeat(field.value.length)
|
|
36
|
+
render={({ field, fieldState }) => {
|
|
37
|
+
const displayValue = '*'.repeat(displayLength)
|
|
35
38
|
|
|
36
39
|
return (
|
|
37
|
-
<box flexDirection=
|
|
40
|
+
<box ref={elementRef} flexDirection="column">
|
|
38
41
|
<WithLeftBorder withDiamond isFocused={isFocused}>
|
|
39
42
|
<text
|
|
40
|
-
fg={Theme.text}
|
|
43
|
+
fg={isFocused ? Theme.primary : Theme.text}
|
|
41
44
|
onMouseDown={() => {
|
|
42
45
|
setFocusedField(props.id)
|
|
43
46
|
}}
|
|
@@ -48,17 +51,27 @@ export const PasswordField = (props: PasswordFieldProps): any => {
|
|
|
48
51
|
<WithLeftBorder isFocused={isFocused}>
|
|
49
52
|
<input
|
|
50
53
|
value={displayValue}
|
|
51
|
-
onInput={(
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
54
|
+
onInput={(newDisplay: string) => {
|
|
55
|
+
if (!isFocused) return
|
|
56
|
+
|
|
57
|
+
const currentValue = realValueRef.current
|
|
58
|
+
const oldLen = currentValue.length
|
|
59
|
+
const newLen = newDisplay.length
|
|
60
|
+
|
|
61
|
+
let newValue: string
|
|
62
|
+
if (newLen > oldLen) {
|
|
63
|
+
const addedChars = newDisplay.replace(/\*/g, '')
|
|
64
|
+
newValue = currentValue + addedChars
|
|
65
|
+
} else if (newLen < oldLen) {
|
|
66
|
+
newValue = currentValue.slice(0, newLen)
|
|
67
|
+
} else {
|
|
68
|
+
return
|
|
61
69
|
}
|
|
70
|
+
|
|
71
|
+
realValueRef.current = newValue
|
|
72
|
+
setDisplayLength(newValue.length)
|
|
73
|
+
field.onChange(newValue)
|
|
74
|
+
props.onChange?.(newValue)
|
|
62
75
|
}}
|
|
63
76
|
placeholder={props.placeholder}
|
|
64
77
|
focused={isFocused}
|