termcast 1.3.21 → 1.3.24
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/ai.d.ts +104 -0
- package/dist/ai.d.ts.map +1 -0
- package/dist/ai.js +135 -0
- package/dist/ai.js.map +1 -0
- package/dist/apis/browser-extension.d.ts +18 -0
- package/dist/apis/browser-extension.d.ts.map +1 -0
- package/dist/apis/browser-extension.js +14 -0
- package/dist/apis/browser-extension.js.map +1 -0
- package/dist/apis/localstorage.d.ts.map +1 -1
- package/dist/apis/localstorage.js +4 -7
- package/dist/apis/localstorage.js.map +1 -1
- package/dist/apis/oauth.d.ts.map +1 -1
- package/dist/apis/oauth.js +5 -1
- package/dist/apis/oauth.js.map +1 -1
- package/dist/apis/preferences.d.ts.map +1 -1
- package/dist/apis/preferences.js +38 -19
- package/dist/apis/preferences.js.map +1 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +2 -1
- package/dist/build.js.map +1 -1
- package/dist/cache.d.ts +32 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +205 -0
- package/dist/cache.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +67 -31
- package/dist/cli.js.map +1 -1
- package/dist/clipboard.d.ts +36 -0
- package/dist/clipboard.d.ts.map +1 -0
- package/dist/clipboard.js +154 -0
- package/dist/clipboard.js.map +1 -0
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +22 -5
- package/dist/compile.js.map +1 -1
- package/dist/components/actions.d.ts.map +1 -1
- package/dist/components/actions.js +56 -30
- package/dist/components/actions.js.map +1 -1
- package/dist/components/detail.d.ts.map +1 -1
- package/dist/components/detail.js +4 -0
- package/dist/components/detail.js.map +1 -1
- package/dist/components/dropdown.d.ts.map +1 -1
- package/dist/components/dropdown.js +38 -15
- package/dist/components/dropdown.js.map +1 -1
- package/dist/components/extension-preferences.d.ts.map +1 -1
- package/dist/components/extension-preferences.js +40 -13
- 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 +5 -3
- 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 +5 -3
- package/dist/components/form/date-picker.js.map +1 -1
- package/dist/components/form/description.d.ts.map +1 -1
- package/dist/components/form/description.js +2 -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 +84 -80
- package/dist/components/form/dropdown.js.map +1 -1
- package/dist/components/form/file-autocomplete.d.ts +3 -6
- package/dist/components/form/file-autocomplete.d.ts.map +1 -1
- package/dist/components/form/file-autocomplete.js +61 -66
- package/dist/components/form/file-autocomplete.js.map +1 -1
- package/dist/components/form/file-picker.d.ts.map +1 -1
- package/dist/components/form/file-picker.js +33 -30
- package/dist/components/form/file-picker.js.map +1 -1
- package/dist/components/form/form-end.d.ts.map +1 -1
- package/dist/components/form/form-end.js +21 -1
- package/dist/components/form/form-end.js.map +1 -1
- package/dist/components/form/form-type-only.d.ts +174 -0
- package/dist/components/form/form-type-only.d.ts.map +1 -0
- package/dist/components/form/form-type-only.js +2 -0
- package/dist/components/form/form-type-only.js.map +1 -0
- package/dist/components/form/index.d.ts +3 -1
- package/dist/components/form/index.d.ts.map +1 -1
- package/dist/components/form/index.js +100 -28
- 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 +5 -3
- 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 +5 -3
- 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 +6 -4
- package/dist/components/form/text-field.js.map +1 -1
- package/dist/components/form/types.d.ts +5 -0
- package/dist/components/form/types.d.ts.map +1 -1
- package/dist/components/form/use-form-handling.d.ts +4 -0
- package/dist/components/form/use-form-handling.d.ts.map +1 -0
- package/dist/components/form/use-form-handling.js +37 -0
- package/dist/components/form/use-form-handling.js.map +1 -0
- package/dist/components/form/with-left-border.d.ts +2 -1
- package/dist/components/form/with-left-border.d.ts.map +1 -1
- package/dist/components/form/with-left-border.js +27 -3
- package/dist/components/form/with-left-border.js.map +1 -1
- package/dist/components/icon.d.ts +1 -0
- package/dist/components/icon.d.ts.map +1 -1
- package/dist/components/icon.js +24 -8
- package/dist/components/icon.js.map +1 -1
- package/dist/components/list.d.ts +2 -2
- package/dist/components/list.d.ts.map +1 -1
- package/dist/components/list.js +140 -62
- package/dist/components/list.js.map +1 -1
- package/dist/components/loading-bar.d.ts.map +1 -1
- package/dist/components/loading-bar.js +2 -2
- package/dist/components/loading-bar.js.map +1 -1
- package/dist/components/loading-text.d.ts +8 -0
- package/dist/components/loading-text.d.ts.map +1 -0
- package/dist/components/loading-text.js +58 -0
- package/dist/components/loading-text.js.map +1 -0
- package/dist/descendants.js +1 -1
- package/dist/descendants.js.map +1 -1
- package/dist/dev-ui.d.ts +7 -0
- package/dist/dev-ui.d.ts.map +1 -0
- package/dist/dev-ui.js +118 -0
- package/dist/dev-ui.js.map +1 -0
- package/dist/environment.d.ts +63 -0
- package/dist/environment.d.ts.map +1 -0
- package/dist/environment.js +189 -0
- package/dist/environment.js.map +1 -0
- package/dist/examples/datepicker.d.ts +2 -0
- package/dist/examples/datepicker.d.ts.map +1 -0
- package/dist/examples/datepicker.js +344 -0
- package/dist/examples/datepicker.js.map +1 -0
- package/dist/examples/file-autocomplete.vitest.d.ts +2 -0
- package/dist/examples/file-autocomplete.vitest.d.ts.map +1 -0
- package/dist/examples/file-autocomplete.vitest.js +223 -0
- package/dist/examples/file-autocomplete.vitest.js.map +1 -0
- package/dist/examples/form-basic-arrow-keys.vitest.d.ts +2 -0
- package/dist/examples/form-basic-arrow-keys.vitest.d.ts.map +1 -0
- package/dist/examples/form-basic-arrow-keys.vitest.js +46 -0
- package/dist/examples/form-basic-arrow-keys.vitest.js.map +1 -0
- package/dist/examples/form-basic.vitest.d.ts +2 -0
- package/dist/examples/form-basic.vitest.d.ts.map +1 -0
- package/dist/examples/form-basic.vitest.js +630 -0
- package/dist/examples/form-basic.vitest.js.map +1 -0
- package/dist/examples/form-dropdown-with-sections.d.ts +2 -0
- package/dist/examples/form-dropdown-with-sections.d.ts.map +1 -0
- package/dist/examples/form-dropdown-with-sections.js +13 -0
- package/dist/examples/form-dropdown-with-sections.js.map +1 -0
- package/dist/examples/form-dropdown-with-sections.vitest.d.ts +2 -0
- package/dist/examples/form-dropdown-with-sections.vitest.d.ts.map +1 -0
- package/dist/examples/form-dropdown-with-sections.vitest.js +75 -0
- package/dist/examples/form-dropdown-with-sections.vitest.js.map +1 -0
- package/dist/examples/form-dropdown.vitest.d.ts +2 -0
- package/dist/examples/form-dropdown.vitest.d.ts.map +1 -0
- package/dist/examples/form-dropdown.vitest.js +854 -0
- package/dist/examples/form-dropdown.vitest.js.map +1 -0
- package/dist/examples/form-multiselect-dropdown.d.ts +2 -0
- package/dist/examples/form-multiselect-dropdown.d.ts.map +1 -0
- package/dist/examples/form-multiselect-dropdown.js +13 -0
- package/dist/examples/form-multiselect-dropdown.js.map +1 -0
- package/dist/examples/form-scroll.d.ts.map +1 -1
- package/dist/examples/form-scroll.js +7 -1
- package/dist/examples/form-scroll.js.map +1 -1
- package/dist/examples/form-scroll.vitest.d.ts +2 -0
- package/dist/examples/form-scroll.vitest.d.ts.map +1 -0
- package/dist/examples/form-scroll.vitest.js +211 -0
- package/dist/examples/form-scroll.vitest.js.map +1 -0
- package/dist/examples/form-tagpicker.vitest.d.ts +2 -0
- package/dist/examples/form-tagpicker.vitest.d.ts.map +1 -0
- package/dist/examples/form-tagpicker.vitest.js +736 -0
- package/dist/examples/form-tagpicker.vitest.js.map +1 -0
- package/dist/examples/internal/descendants-filtering.js +1 -1
- package/dist/examples/internal/descendants-filtering.js.map +1 -1
- package/dist/examples/internal/descendants.js +1 -1
- package/dist/examples/internal/descendants.js.map +1 -1
- package/dist/examples/internal/nested-boxes.d.ts +2 -0
- package/dist/examples/internal/nested-boxes.d.ts.map +1 -0
- package/dist/examples/internal/nested-boxes.js +7 -0
- package/dist/examples/internal/nested-boxes.js.map +1 -0
- package/dist/examples/internal/rhf-custom-ref.js +2 -2
- package/dist/examples/internal/rhf-custom-ref.js.map +1 -1
- package/dist/examples/internal/scrollbox-demo.js +3 -22
- package/dist/examples/internal/scrollbox-demo.js.map +1 -1
- package/dist/examples/internal/scrollbox-descendants.d.ts +2 -0
- package/dist/examples/internal/scrollbox-descendants.d.ts.map +1 -0
- package/dist/examples/internal/scrollbox-descendants.js +83 -0
- package/dist/examples/internal/scrollbox-descendants.js.map +1 -0
- package/dist/examples/internal/scrollbox-with-descendants.js +4 -8
- package/dist/examples/internal/scrollbox-with-descendants.js.map +1 -1
- package/dist/examples/internal/simple-scrollbox.vitest.d.ts +2 -0
- package/dist/examples/internal/simple-scrollbox.vitest.d.ts.map +1 -0
- package/dist/examples/internal/simple-scrollbox.vitest.js +96 -0
- package/dist/examples/internal/simple-scrollbox.vitest.js.map +1 -0
- package/dist/examples/internal/unicode-square-repro.d.ts +2 -0
- package/dist/examples/internal/unicode-square-repro.d.ts.map +1 -0
- package/dist/examples/internal/unicode-square-repro.js +7 -0
- package/dist/examples/internal/unicode-square-repro.js.map +1 -0
- package/dist/examples/list-detail-metadata.d.ts +2 -0
- package/dist/examples/list-detail-metadata.d.ts.map +1 -0
- package/dist/examples/list-detail-metadata.js +8 -0
- package/dist/examples/list-detail-metadata.js.map +1 -0
- package/dist/examples/list-dropdown-default.vitest.d.ts +2 -0
- package/dist/examples/list-dropdown-default.vitest.d.ts.map +1 -0
- package/dist/examples/list-dropdown-default.vitest.js +234 -0
- package/dist/examples/list-dropdown-default.vitest.js.map +1 -0
- package/dist/examples/list-fetch-data.vitest.d.ts +2 -0
- package/dist/examples/list-fetch-data.vitest.d.ts.map +1 -0
- package/dist/examples/list-fetch-data.vitest.js +111 -0
- package/dist/examples/list-fetch-data.vitest.js.map +1 -0
- package/dist/examples/list-filter-navigation.d.ts +2 -0
- package/dist/examples/list-filter-navigation.d.ts.map +1 -0
- package/dist/examples/list-filter-navigation.js +8 -0
- package/dist/examples/list-filter-navigation.js.map +1 -0
- package/dist/examples/list-scrollbox.vitest.d.ts +2 -0
- package/dist/examples/list-scrollbox.vitest.d.ts.map +1 -0
- package/dist/examples/list-scrollbox.vitest.js +93 -0
- package/dist/examples/list-scrollbox.vitest.js.map +1 -0
- package/dist/examples/list-with-detail-long.d.ts +2 -0
- package/dist/examples/list-with-detail-long.d.ts.map +1 -0
- package/dist/examples/list-with-detail-long.js +53 -0
- package/dist/examples/list-with-detail-long.js.map +1 -0
- package/dist/examples/list-with-detail.vitest.d.ts +2 -0
- package/dist/examples/list-with-detail.vitest.d.ts.map +1 -0
- package/dist/examples/list-with-detail.vitest.js +434 -0
- package/dist/examples/list-with-detail.vitest.js.map +1 -0
- package/dist/examples/list-with-dropdown.vitest.d.ts +2 -0
- package/dist/examples/list-with-dropdown.vitest.d.ts.map +1 -0
- package/dist/examples/list-with-dropdown.vitest.js +337 -0
- package/dist/examples/list-with-dropdown.vitest.js.map +1 -0
- package/dist/examples/list-with-sections.js +5 -1
- package/dist/examples/list-with-sections.js.map +1 -1
- package/dist/examples/list-with-sections.vitest.d.ts +2 -0
- package/dist/examples/list-with-sections.vitest.d.ts.map +1 -0
- package/dist/examples/list-with-sections.vitest.js +601 -0
- package/dist/examples/list-with-sections.vitest.js.map +1 -0
- package/dist/examples/scrollbox-vertical-centering.d.ts +6 -0
- package/dist/examples/scrollbox-vertical-centering.d.ts.map +1 -0
- package/dist/examples/scrollbox-vertical-centering.js +17 -0
- package/dist/examples/scrollbox-vertical-centering.js.map +1 -0
- package/dist/examples/simple-file-picker.vitest.d.ts +2 -0
- package/dist/examples/simple-file-picker.vitest.d.ts.map +1 -0
- package/dist/examples/simple-file-picker.vitest.js +678 -0
- package/dist/examples/simple-file-picker.vitest.js.map +1 -0
- package/dist/examples/simple-grid.vitest.d.ts +2 -0
- package/dist/examples/simple-grid.vitest.d.ts.map +1 -0
- package/dist/examples/simple-grid.vitest.js +521 -0
- package/dist/examples/simple-grid.vitest.js.map +1 -0
- package/dist/examples/simple-navigation.js +10 -4
- package/dist/examples/simple-navigation.js.map +1 -1
- package/dist/examples/simple-navigation.vitest.d.ts +2 -0
- package/dist/examples/simple-navigation.vitest.d.ts.map +1 -0
- package/dist/examples/simple-navigation.vitest.js +718 -0
- package/dist/examples/simple-navigation.vitest.js.map +1 -0
- package/dist/examples/store.vitest.d.ts +2 -0
- package/dist/examples/store.vitest.d.ts.map +1 -0
- package/dist/examples/store.vitest.js +69 -0
- package/dist/examples/store.vitest.js.map +1 -0
- package/dist/extensions/dev.d.ts +4 -2
- package/dist/extensions/dev.d.ts.map +1 -1
- package/dist/extensions/dev.js +61 -10
- package/dist/extensions/dev.js.map +1 -1
- package/dist/extensions/dev.vitest.d.ts +2 -0
- package/dist/extensions/dev.vitest.d.ts.map +1 -0
- package/dist/extensions/dev.vitest.js +197 -0
- package/dist/extensions/dev.vitest.js.map +1 -0
- package/dist/extensions/home.d.ts.map +1 -1
- package/dist/extensions/home.js +3 -0
- package/dist/extensions/home.js.map +1 -1
- package/dist/home-command.d.ts +8 -0
- package/dist/home-command.d.ts.map +1 -0
- package/dist/home-command.js +181 -0
- package/dist/home-command.js.map +1 -0
- package/dist/hover-repro.d.ts +2 -0
- package/dist/hover-repro.d.ts.map +1 -0
- package/dist/hover-repro.js +20 -0
- package/dist/hover-repro.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/dialog.d.ts +1 -0
- package/dist/internal/dialog.d.ts.map +1 -1
- package/dist/internal/dialog.js +27 -18
- package/dist/internal/dialog.js.map +1 -1
- package/dist/internal/navigation.d.ts +9 -1
- package/dist/internal/navigation.d.ts.map +1 -1
- package/dist/internal/navigation.js +5 -5
- package/dist/internal/navigation.js.map +1 -1
- package/dist/internal/offscreen.d.ts +6 -0
- package/dist/internal/offscreen.d.ts.map +1 -0
- package/dist/internal/offscreen.js +10 -0
- package/dist/internal/offscreen.js.map +1 -0
- package/dist/internal/providers.d.ts.map +1 -1
- package/dist/internal/providers.js +2 -2
- package/dist/internal/providers.js.map +1 -1
- package/dist/internal/scrollbox.d.ts +1 -10
- package/dist/internal/scrollbox.d.ts.map +1 -1
- package/dist/internal/scrollbox.js +2 -1
- package/dist/internal/scrollbox.js.map +1 -1
- package/dist/localstorage.d.ts +13 -0
- package/dist/localstorage.d.ts.map +1 -0
- package/dist/localstorage.js +190 -0
- package/dist/localstorage.js.map +1 -0
- package/dist/oauth.d.ts +142 -0
- package/dist/oauth.d.ts.map +1 -0
- package/dist/oauth.js +551 -0
- package/dist/oauth.js.map +1 -0
- package/dist/preferences.d.ts +23 -0
- package/dist/preferences.d.ts.map +1 -0
- package/dist/preferences.js +105 -0
- package/dist/preferences.js.map +1 -0
- package/dist/state.d.ts +2 -0
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +3 -0
- package/dist/state.js.map +1 -1
- package/dist/store.d.ts +21 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +84 -0
- package/dist/store.js.map +1 -0
- package/dist/swift-loader.d.ts +3 -0
- package/dist/swift-loader.d.ts.map +1 -0
- package/dist/swift-loader.js +193 -0
- package/dist/swift-loader.js.map +1 -0
- package/dist/swift-runtime.d.ts +2 -0
- package/dist/swift-runtime.d.ts.map +1 -0
- package/dist/swift-runtime.js +27 -0
- package/dist/swift-runtime.js.map +1 -0
- package/dist/toast.d.ts +44 -0
- package/dist/toast.d.ts.map +1 -0
- package/dist/toast.js +221 -0
- package/dist/toast.js.map +1 -0
- package/dist/utils/file-system.d.ts +9 -0
- package/dist/utils/file-system.d.ts.map +1 -1
- package/dist/utils/file-system.js +49 -0
- package/dist/utils/file-system.js.map +1 -1
- package/dist/utils/run-command.d.ts +25 -1
- package/dist/utils/run-command.d.ts.map +1 -1
- package/dist/utils/run-command.js +47 -4
- package/dist/utils/run-command.js.map +1 -1
- package/dist/window.d.ts +12 -0
- package/dist/window.d.ts.map +1 -0
- package/dist/window.js +48 -0
- package/dist/window.js.map +1 -0
- package/package.json +12 -10
- package/src/apis/browser-extension.tsx +29 -0
- package/src/apis/localstorage.test.ts +14 -6
- package/src/apis/localstorage.tsx +8 -5
- package/src/apis/oauth.tsx +5 -1
- package/src/apis/preferences.tsx +48 -22
- package/src/build.test.tsx +52 -0
- package/src/build.tsx +2 -1
- package/src/cli.tsx +78 -37
- package/src/compile.tsx +22 -5
- package/src/components/actions.tsx +94 -32
- package/src/components/detail.tsx +4 -0
- package/src/components/dropdown.tsx +47 -14
- package/src/components/extension-preferences.tsx +44 -16
- package/src/components/form/checkbox.tsx +12 -6
- package/src/components/form/date-picker.tsx +12 -6
- package/src/components/form/description.tsx +7 -2
- package/src/components/form/dropdown.tsx +131 -119
- package/src/components/form/file-autocomplete.tsx +90 -108
- package/src/components/form/file-picker.tsx +54 -43
- package/src/components/form/form-end.tsx +23 -2
- package/src/components/form/index.tsx +152 -34
- package/src/components/form/password-field.tsx +12 -6
- package/src/components/form/text-area.tsx +13 -6
- package/src/components/form/text-field.tsx +13 -6
- package/src/components/form/types.tsx +6 -0
- package/src/components/form/with-left-border.tsx +41 -8
- package/src/components/icon.tsx +27 -8
- package/src/components/list.tsx +193 -74
- package/src/components/loading-bar.tsx +3 -2
- package/src/components/loading-text.tsx +79 -0
- package/src/descendants.tsx +1 -0
- package/src/examples/file-autocomplete.vitest.tsx +130 -125
- package/src/examples/form-basic.vitest.tsx +376 -176
- package/src/examples/form-dropdown.vitest.tsx +126 -126
- package/src/examples/form-scroll.tsx +2 -0
- package/src/examples/form-scroll.vitest.tsx +58 -58
- package/src/examples/form-tagpicker.vitest.tsx +99 -99
- package/src/examples/internal/descendants-filtering.tsx +1 -0
- package/src/examples/internal/descendants.tsx +1 -0
- package/src/examples/internal/rhf-custom-ref.tsx +2 -0
- package/src/examples/internal/scrollbox-demo.tsx +3 -27
- package/src/examples/internal/scrollbox-with-descendants.tsx +4 -7
- package/src/examples/internal/simple-scrollbox.vitest.tsx +7 -5
- package/src/examples/list-detail-metadata.tsx +49 -0
- package/src/examples/list-detail-metadata.vitest.tsx +88 -0
- package/src/examples/list-dropdown-default.vitest.tsx +51 -51
- package/src/examples/list-fetch-data.vitest.tsx +4 -4
- package/src/examples/list-scrollbox.vitest.tsx +73 -14
- package/src/examples/list-with-detail-long.tsx +70 -0
- package/src/examples/list-with-detail.vitest.tsx +198 -92
- package/src/examples/list-with-dropdown.vitest.tsx +53 -53
- package/src/examples/list-with-sections.tsx +1 -0
- package/src/examples/list-with-sections.vitest.tsx +213 -89
- package/src/examples/list-with-toast.vitest.tsx +4 -4
- package/src/examples/simple-file-picker.vitest.tsx +61 -471
- package/src/examples/simple-grid.vitest.tsx +238 -233
- package/src/examples/simple-navigation.tsx +15 -7
- package/src/examples/simple-navigation.vitest.tsx +121 -210
- package/src/examples/store.vitest.tsx +1 -1
- package/src/examples/swift-extension.vitest.tsx +148 -0
- package/src/examples/synonyms.vitest.tsx +159 -0
- package/src/extensions/dev.tsx +74 -7
- package/src/extensions/dev.vitest.tsx +97 -31
- package/src/extensions/home.tsx +6 -0
- package/src/index.tsx +3 -0
- package/src/internal/dialog.tsx +43 -30
- package/src/internal/navigation.tsx +3 -1
- package/src/internal/offscreen.tsx +15 -0
- package/src/internal/providers.tsx +4 -2
- package/src/internal/scrollbox.tsx +4 -8
- package/src/keyboard.test.tsx +69 -0
- package/src/state.tsx +7 -0
- package/src/swift-loader.tsx +239 -0
- package/src/swift-runtime.tsx +36 -0
- package/src/utils/file-system.ts +61 -0
- package/src/utils/run-command.tsx +75 -6
|
@@ -7,8 +7,10 @@ import { useFormContext, Controller } from 'react-hook-form'
|
|
|
7
7
|
import { useFocusContext, useFormFieldDescendant } from './index'
|
|
8
8
|
import { useKeyboard } from '@opentui/react'
|
|
9
9
|
import { useIsInFocus } from 'termcast/src/internal/focus-context'
|
|
10
|
-
import {
|
|
10
|
+
import { FileAutocompleteDialog } from './file-autocomplete'
|
|
11
11
|
import { useFormNavigationHelpers } from './use-form-navigation'
|
|
12
|
+
import { useDialog } from 'termcast/src/internal/dialog'
|
|
13
|
+
import { LoadingText } from 'termcast/src/components/loading-text'
|
|
12
14
|
|
|
13
15
|
export interface FilePickerProps extends FormItemProps<string[]> {
|
|
14
16
|
/**
|
|
@@ -52,6 +54,7 @@ const FilePickerField = ({
|
|
|
52
54
|
props,
|
|
53
55
|
isFocused,
|
|
54
56
|
setFocusedField,
|
|
57
|
+
isFormLoading,
|
|
55
58
|
}: {
|
|
56
59
|
field: any
|
|
57
60
|
fieldState: any
|
|
@@ -59,12 +62,40 @@ const FilePickerField = ({
|
|
|
59
62
|
props: FilePickerProps
|
|
60
63
|
isFocused: boolean
|
|
61
64
|
setFocusedField: (id: string) => void
|
|
65
|
+
isFormLoading: boolean
|
|
62
66
|
}): any => {
|
|
63
67
|
const isInFocus = useIsInFocus()
|
|
64
|
-
const [showAutocomplete, setShowAutocomplete] = React.useState(false)
|
|
65
|
-
const [searchTrigger, setSearchTrigger] = React.useState(0)
|
|
66
68
|
const inputRef = React.useRef<TextareaRenderable>(null)
|
|
67
|
-
const
|
|
69
|
+
const dialog = useDialog()
|
|
70
|
+
|
|
71
|
+
const showAutocomplete = () => {
|
|
72
|
+
if (dialog.stack.length > 0) return
|
|
73
|
+
|
|
74
|
+
const handleSelect = (path: string) => {
|
|
75
|
+
const currentFiles = field.value || []
|
|
76
|
+
const newFiles =
|
|
77
|
+
props.allowMultipleSelection !== false ? [...currentFiles, path] : [path]
|
|
78
|
+
field.onChange(newFiles)
|
|
79
|
+
if (props.onChange) {
|
|
80
|
+
props.onChange(newFiles)
|
|
81
|
+
}
|
|
82
|
+
inputRef.current?.setText('')
|
|
83
|
+
dialog.clear()
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
dialog.push(
|
|
87
|
+
<FileAutocompleteDialog
|
|
88
|
+
onSelect={handleSelect}
|
|
89
|
+
onClose={() => {
|
|
90
|
+
dialog.clear()
|
|
91
|
+
}}
|
|
92
|
+
inputRef={inputRef}
|
|
93
|
+
canChooseFiles={props.canChooseFiles}
|
|
94
|
+
canChooseDirectories={props.canChooseDirectories}
|
|
95
|
+
initialDirectory={props.initialDirectory}
|
|
96
|
+
/>
|
|
97
|
+
)
|
|
98
|
+
}
|
|
68
99
|
|
|
69
100
|
// Handle Enter key and left arrow for removing last file
|
|
70
101
|
useKeyboard((evt) => {
|
|
@@ -86,12 +117,11 @@ const FilePickerField = ({
|
|
|
86
117
|
if (evt.name === 'return') {
|
|
87
118
|
const inputValue = inputRef.current?.plainText || ''
|
|
88
119
|
// If input is empty, show files in current directory
|
|
89
|
-
if (!inputValue
|
|
90
|
-
|
|
91
|
-
setSearchTrigger((n) => n + 1)
|
|
120
|
+
if (!inputValue) {
|
|
121
|
+
showAutocomplete()
|
|
92
122
|
}
|
|
93
|
-
// If
|
|
94
|
-
else if (inputValue.trim()
|
|
123
|
+
// If input has value, add the path directly
|
|
124
|
+
else if (inputValue.trim()) {
|
|
95
125
|
const currentFiles = field.value || []
|
|
96
126
|
const newFiles =
|
|
97
127
|
props.allowMultipleSelection !== false
|
|
@@ -106,37 +136,30 @@ const FilePickerField = ({
|
|
|
106
136
|
}
|
|
107
137
|
})
|
|
108
138
|
|
|
109
|
-
const handleSelectFile = (path: string) => {
|
|
110
|
-
const currentFiles = field.value || []
|
|
111
|
-
const newFiles =
|
|
112
|
-
props.allowMultipleSelection !== false ? [...currentFiles, path] : [path]
|
|
113
|
-
field.onChange(newFiles)
|
|
114
|
-
if (props.onChange) {
|
|
115
|
-
props.onChange(newFiles)
|
|
116
|
-
}
|
|
117
|
-
inputRef.current?.setText('')
|
|
118
|
-
setShowAutocomplete(false)
|
|
119
|
-
}
|
|
120
|
-
|
|
121
139
|
const selectedFiles = field.value || []
|
|
122
140
|
|
|
123
141
|
return (
|
|
124
142
|
<box flexDirection='column'>
|
|
125
|
-
<WithLeftBorder withDiamond isFocused={isFocused}>
|
|
126
|
-
<
|
|
127
|
-
fg={isFocused ? Theme.primary : Theme.text}
|
|
143
|
+
<WithLeftBorder withDiamond isFocused={isFocused} isLoading={isFormLoading}>
|
|
144
|
+
<box
|
|
128
145
|
onMouseDown={() => {
|
|
129
146
|
setFocusedField(props.id)
|
|
130
147
|
}}
|
|
131
148
|
>
|
|
132
|
-
|
|
133
|
-
|
|
149
|
+
<LoadingText
|
|
150
|
+
isLoading={isFocused && isFormLoading}
|
|
151
|
+
color={isFocused ? Theme.primary : Theme.text}
|
|
152
|
+
>
|
|
153
|
+
{props.title || 'File Path'}
|
|
154
|
+
</LoadingText>
|
|
155
|
+
</box>
|
|
134
156
|
</WithLeftBorder>
|
|
135
157
|
<WithLeftBorder isFocused={isFocused}>
|
|
136
|
-
<box flexDirection='column'
|
|
158
|
+
<box flexDirection='column'>
|
|
137
159
|
<textarea
|
|
138
160
|
ref={inputRef}
|
|
139
161
|
height={1}
|
|
162
|
+
wrapMode='none'
|
|
140
163
|
keyBindings={[
|
|
141
164
|
{ name: 'return', action: 'submit' },
|
|
142
165
|
{ name: 'linefeed', action: 'submit' },
|
|
@@ -148,10 +171,7 @@ const FilePickerField = ({
|
|
|
148
171
|
onContentChange={() => {
|
|
149
172
|
const value = inputRef.current?.plainText || ''
|
|
150
173
|
if (value && isFocused) {
|
|
151
|
-
|
|
152
|
-
setSearchTrigger((n) => n + 1)
|
|
153
|
-
} else if (!value) {
|
|
154
|
-
setShowAutocomplete(false)
|
|
174
|
+
showAutocomplete()
|
|
155
175
|
}
|
|
156
176
|
}}
|
|
157
177
|
/>
|
|
@@ -179,24 +199,14 @@ const FilePickerField = ({
|
|
|
179
199
|
<text fg={Theme.textMuted}>{props.info}</text>
|
|
180
200
|
</WithLeftBorder>
|
|
181
201
|
)}
|
|
182
|
-
<FileAutocomplete
|
|
183
|
-
onSelect={handleSelectFile}
|
|
184
|
-
visible={showAutocomplete}
|
|
185
|
-
onVisibilityChange={setShowAutocomplete}
|
|
186
|
-
inputRef={inputRef}
|
|
187
|
-
anchorRef={anchorRef}
|
|
188
|
-
searchTrigger={searchTrigger}
|
|
189
|
-
canChooseFiles={props.canChooseFiles}
|
|
190
|
-
canChooseDirectories={props.canChooseDirectories}
|
|
191
|
-
initialDirectory={props.initialDirectory}
|
|
192
|
-
/>
|
|
193
202
|
</box>
|
|
194
203
|
) as React.ReactElement
|
|
195
204
|
}
|
|
196
205
|
|
|
197
206
|
export const FilePicker = (props: FilePickerProps): any => {
|
|
198
207
|
const { control } = useFormContext()
|
|
199
|
-
const
|
|
208
|
+
const focusContext = useFocusContext()
|
|
209
|
+
const { focusedField, setFocusedField } = focusContext
|
|
200
210
|
const isFocused = focusedField === props.id
|
|
201
211
|
const isInFocus = useIsInFocus()
|
|
202
212
|
|
|
@@ -236,6 +246,7 @@ export const FilePicker = (props: FilePickerProps): any => {
|
|
|
236
246
|
props={props}
|
|
237
247
|
isFocused={isFocused}
|
|
238
248
|
setFocusedField={setFocusedField}
|
|
249
|
+
isFormLoading={focusContext.isLoading}
|
|
239
250
|
/>
|
|
240
251
|
</box>
|
|
241
252
|
) as React.ReactElement
|
|
@@ -1,6 +1,27 @@
|
|
|
1
|
-
import React from 'react'
|
|
1
|
+
import React, { useState, useLayoutEffect } from 'react'
|
|
2
2
|
import { Theme } from 'termcast/src/theme'
|
|
3
|
+
import { useFocusContext, useFormScrollContext } from './index'
|
|
3
4
|
|
|
4
5
|
export const FormEnd = (): any => {
|
|
5
|
-
|
|
6
|
+
const { focusedField } = useFocusContext()
|
|
7
|
+
const scrollContext = useFormScrollContext()
|
|
8
|
+
const [isLastFieldFocused, setIsLastFieldFocused] = useState(false)
|
|
9
|
+
|
|
10
|
+
useLayoutEffect(() => {
|
|
11
|
+
if (!scrollContext || !focusedField) {
|
|
12
|
+
setIsLastFieldFocused(false)
|
|
13
|
+
return
|
|
14
|
+
}
|
|
15
|
+
const descendants = Object.values(scrollContext.descendantsContext.map.current)
|
|
16
|
+
.filter((item) => item.index !== -1 && item.props?.id)
|
|
17
|
+
.sort((a, b) => a.index - b.index)
|
|
18
|
+
if (descendants.length === 0) {
|
|
19
|
+
setIsLastFieldFocused(false)
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
const lastField = descendants[descendants.length - 1]
|
|
23
|
+
setIsLastFieldFocused(lastField.props?.id === focusedField)
|
|
24
|
+
}, [focusedField])
|
|
25
|
+
|
|
26
|
+
return <text fg={isLastFieldFocused ? Theme.accent : Theme.text}>└</text>
|
|
6
27
|
}
|
|
@@ -4,6 +4,7 @@ import React, {
|
|
|
4
4
|
useContext,
|
|
5
5
|
useLayoutEffect,
|
|
6
6
|
useRef,
|
|
7
|
+
useEffect,
|
|
7
8
|
} from 'react'
|
|
8
9
|
import { useKeyboard } from '@opentui/react'
|
|
9
10
|
import { useForm, FormProvider } from 'react-hook-form'
|
|
@@ -12,6 +13,7 @@ import { logger } from 'termcast/src/logger'
|
|
|
12
13
|
import { InFocus, useIsInFocus } from 'termcast/src/internal/focus-context'
|
|
13
14
|
import { useDialog } from 'termcast/src/internal/dialog'
|
|
14
15
|
import { Theme } from 'termcast/src/theme'
|
|
16
|
+
import { useStore } from 'termcast/src/state'
|
|
15
17
|
import {
|
|
16
18
|
TextAttributes,
|
|
17
19
|
ScrollBoxRenderable,
|
|
@@ -33,7 +35,10 @@ import {
|
|
|
33
35
|
FormValues_2,
|
|
34
36
|
FormProps_2,
|
|
35
37
|
FormItemProps_2,
|
|
38
|
+
LinkAccessoryProps,
|
|
36
39
|
} from './types'
|
|
40
|
+
import { LoadingBar } from 'termcast/src/components/loading-bar'
|
|
41
|
+
import { useNavigationPending } from 'termcast/src/internal/navigation'
|
|
37
42
|
import { FORM_MAX_WIDTH } from './description'
|
|
38
43
|
import { ScrollBox } from 'termcast/src/internal/scrollbox'
|
|
39
44
|
|
|
@@ -67,10 +72,11 @@ export const useFormScrollContext = () => {
|
|
|
67
72
|
return useContext(FormScrollContext)
|
|
68
73
|
}
|
|
69
74
|
|
|
70
|
-
// Context for managing focused field
|
|
75
|
+
// Context for managing focused field and loading state
|
|
71
76
|
interface FocusContextValue {
|
|
72
77
|
focusedField: string | null
|
|
73
78
|
setFocusedField: (id: string | null) => void
|
|
79
|
+
isLoading: boolean
|
|
74
80
|
}
|
|
75
81
|
|
|
76
82
|
const FocusContext = createContext<FocusContextValue | null>(null)
|
|
@@ -107,14 +113,16 @@ function FormFooter(): any {
|
|
|
107
113
|
flexDirection: 'row',
|
|
108
114
|
}}
|
|
109
115
|
>
|
|
110
|
-
<text fg={Theme.text} attributes={TextAttributes.BOLD}>
|
|
116
|
+
<text fg={Theme.text} attributes={TextAttributes.BOLD}>
|
|
117
|
+
ctrl ↵
|
|
118
|
+
</text>
|
|
111
119
|
<text fg={Theme.textMuted}> submit</text>
|
|
112
120
|
<text fg={Theme.text} attributes={TextAttributes.BOLD}>
|
|
113
|
-
{' '}
|
|
121
|
+
{' '}tab
|
|
114
122
|
</text>
|
|
115
123
|
<text fg={Theme.textMuted}> navigate</text>
|
|
116
124
|
<text fg={Theme.text} attributes={TextAttributes.BOLD}>
|
|
117
|
-
|
|
125
|
+
{' '}^k
|
|
118
126
|
</text>
|
|
119
127
|
<text fg={Theme.textMuted}> actions</text>
|
|
120
128
|
</box>
|
|
@@ -171,19 +179,30 @@ interface FormType {
|
|
|
171
179
|
FilePicker: (props: FilePickerProps) => any
|
|
172
180
|
Separator: () => any
|
|
173
181
|
Description: (props: DescriptionProps) => any
|
|
182
|
+
LinkAccessory: (props: LinkAccessoryProps) => any
|
|
174
183
|
}
|
|
175
184
|
|
|
176
185
|
export const Form: FormType = ((props) => {
|
|
186
|
+
const { navigationTitle, isLoading, searchBarAccessory } = props
|
|
177
187
|
const methods = useForm<FormValues>({
|
|
178
188
|
// defaultValues: {},
|
|
179
189
|
// mode: 'onChange',
|
|
180
190
|
})
|
|
181
191
|
|
|
182
192
|
const [focusedField, setFocusedFieldRaw] = useState<string | null>(null)
|
|
193
|
+
const navigationPending = useNavigationPending()
|
|
183
194
|
|
|
184
195
|
const scrollBoxRef = useRef<ScrollBoxRenderable>(null)
|
|
185
196
|
const descendantsContext = useFormFieldDescendants()
|
|
186
197
|
|
|
198
|
+
// Helper to get sorted field IDs
|
|
199
|
+
const getFieldIds = () => {
|
|
200
|
+
return Object.values(descendantsContext.map.current)
|
|
201
|
+
.filter((item) => item.index !== -1 && item.props?.id)
|
|
202
|
+
.sort((a, b) => a.index - b.index)
|
|
203
|
+
.map((item) => item.props!.id)
|
|
204
|
+
}
|
|
205
|
+
|
|
187
206
|
const scrollToField = (fieldId: string) => {
|
|
188
207
|
const scrollBox = scrollBoxRef.current
|
|
189
208
|
if (!scrollBox) return
|
|
@@ -197,17 +216,13 @@ export const Form: FormType = ((props) => {
|
|
|
197
216
|
|
|
198
217
|
const contentY = scrollBox.content?.y || 0
|
|
199
218
|
const viewportHeight = scrollBox.viewport?.height || 10
|
|
200
|
-
const currentScrollTop = scrollBox.scrollTop || 0
|
|
201
219
|
|
|
202
220
|
// Access current position from the BoxRenderable ref
|
|
203
221
|
const itemTop = elementRef.y - contentY
|
|
204
|
-
const itemBottom = itemTop + elementRef.height
|
|
205
222
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
scrollBox.scrollTo(itemBottom - viewportHeight)
|
|
210
|
-
}
|
|
223
|
+
// Scroll so the top of the item is centered in the viewport
|
|
224
|
+
const targetScrollTop = itemTop - viewportHeight / 2
|
|
225
|
+
scrollBox.scrollTo(Math.max(0, targetScrollTop))
|
|
211
226
|
}
|
|
212
227
|
|
|
213
228
|
const setFocusedField = (id: string | null) => {
|
|
@@ -217,30 +232,82 @@ export const Form: FormType = ((props) => {
|
|
|
217
232
|
}
|
|
218
233
|
}
|
|
219
234
|
|
|
220
|
-
//
|
|
221
|
-
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
.
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
const firstId = descendants[0].props!.id
|
|
228
|
-
logger.log(`focusing `, firstId)
|
|
229
|
-
setFocusedFieldRaw(firstId)
|
|
230
|
-
} else {
|
|
231
|
-
logger.log(`no fields to focus in form`)
|
|
235
|
+
// Focus first field helper
|
|
236
|
+
const focusFirstField = () => {
|
|
237
|
+
const fieldIds = getFieldIds()
|
|
238
|
+
if (fieldIds.length > 0) {
|
|
239
|
+
logger.log(`focusing first field:`, fieldIds[0])
|
|
240
|
+
setFocusedField(fieldIds[0])
|
|
241
|
+
return true
|
|
232
242
|
}
|
|
233
|
-
|
|
243
|
+
return false
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Focus last field helper
|
|
247
|
+
const focusLastField = () => {
|
|
248
|
+
const fieldIds = getFieldIds()
|
|
249
|
+
if (fieldIds.length > 0) {
|
|
250
|
+
logger.log(`focusing last field:`, fieldIds[fieldIds.length - 1])
|
|
251
|
+
setFocusedField(fieldIds[fieldIds.length - 1])
|
|
252
|
+
return true
|
|
253
|
+
}
|
|
254
|
+
return false
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Auto-focus first field when descendants become available
|
|
258
|
+
// Runs on every render until a field is focused (handles async loading)
|
|
259
|
+
useEffect(() => {
|
|
260
|
+
if (focusedField) return
|
|
261
|
+
|
|
262
|
+
const fieldIds = getFieldIds()
|
|
263
|
+
if (fieldIds.length > 0) {
|
|
264
|
+
logger.log(`auto-focusing first field:`, fieldIds[0])
|
|
265
|
+
setFocusedFieldRaw(fieldIds[0])
|
|
266
|
+
}
|
|
267
|
+
})
|
|
234
268
|
|
|
235
269
|
// Get focus state and dialog
|
|
236
270
|
const inFocus = useIsInFocus()
|
|
237
271
|
const dialog = useDialog()
|
|
238
272
|
|
|
239
|
-
// Handle action keys and page scrolling
|
|
273
|
+
// Handle action keys, tab navigation, and page scrolling
|
|
240
274
|
useKeyboard((evt) => {
|
|
241
275
|
// Only handle keyboard events when form is in focus
|
|
242
276
|
if (!inFocus) return
|
|
243
277
|
|
|
278
|
+
// Tab navigation at Form level - handles case when no field is focused
|
|
279
|
+
// or navigates between fields
|
|
280
|
+
if (evt.name === 'tab') {
|
|
281
|
+
if (!focusedField) {
|
|
282
|
+
// No field focused yet - focus first or last based on shift
|
|
283
|
+
if (evt.shift) {
|
|
284
|
+
focusLastField()
|
|
285
|
+
} else {
|
|
286
|
+
focusFirstField()
|
|
287
|
+
}
|
|
288
|
+
} else {
|
|
289
|
+
// A field is focused - navigate to next/previous
|
|
290
|
+
const fieldIds = getFieldIds()
|
|
291
|
+
const currentIndex = fieldIds.indexOf(focusedField)
|
|
292
|
+
if (evt.shift) {
|
|
293
|
+
// Shift+Tab - go to previous
|
|
294
|
+
if (currentIndex > 0) {
|
|
295
|
+
setFocusedField(fieldIds[currentIndex - 1])
|
|
296
|
+
} else {
|
|
297
|
+
setFocusedField(fieldIds[fieldIds.length - 1])
|
|
298
|
+
}
|
|
299
|
+
} else {
|
|
300
|
+
// Tab - go to next
|
|
301
|
+
if (currentIndex < fieldIds.length - 1) {
|
|
302
|
+
setFocusedField(fieldIds[currentIndex + 1])
|
|
303
|
+
} else {
|
|
304
|
+
setFocusedField(fieldIds[0])
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return
|
|
309
|
+
}
|
|
310
|
+
|
|
244
311
|
// Page up/down scrolling
|
|
245
312
|
if (evt.name === 'pageup' || evt.name === 'pagedown') {
|
|
246
313
|
const scrollBox = scrollBoxRef.current
|
|
@@ -259,7 +326,7 @@ export const Form: FormType = ((props) => {
|
|
|
259
326
|
}
|
|
260
327
|
|
|
261
328
|
if (evt.name === 'k' && evt.ctrl && props.actions) {
|
|
262
|
-
// Ctrl+K shows actions
|
|
329
|
+
// Ctrl+K shows actions (always show sheet)
|
|
263
330
|
dialog.push(
|
|
264
331
|
<FormSubmitContext.Provider value={submitContextValue}>
|
|
265
332
|
{props.actions}
|
|
@@ -267,7 +334,8 @@ export const Form: FormType = ((props) => {
|
|
|
267
334
|
'bottom-right',
|
|
268
335
|
)
|
|
269
336
|
} else if (evt.name === 'return' && evt.ctrl && props.actions) {
|
|
270
|
-
// Ctrl+Return
|
|
337
|
+
// Ctrl+Return executes first action directly
|
|
338
|
+
useStore.setState({ shouldAutoExecuteFirstAction: true })
|
|
271
339
|
dialog.push(
|
|
272
340
|
<FormSubmitContext.Provider value={submitContextValue}>
|
|
273
341
|
{props.actions}
|
|
@@ -275,7 +343,8 @@ export const Form: FormType = ((props) => {
|
|
|
275
343
|
'bottom-right',
|
|
276
344
|
)
|
|
277
345
|
} else if (evt.name === 'return' && evt.meta && props.actions) {
|
|
278
|
-
// Cmd+Return also
|
|
346
|
+
// Cmd+Return also executes first action directly
|
|
347
|
+
useStore.setState({ shouldAutoExecuteFirstAction: true })
|
|
279
348
|
dialog.push(
|
|
280
349
|
<FormSubmitContext.Provider value={submitContextValue}>
|
|
281
350
|
{props.actions}
|
|
@@ -298,9 +367,9 @@ export const Form: FormType = ((props) => {
|
|
|
298
367
|
<FormProvider {...methods}>
|
|
299
368
|
<FormSubmitContext.Provider value={submitContextValue}>
|
|
300
369
|
<FormScrollContext.Provider value={scrollContextValue}>
|
|
301
|
-
<FocusContext.Provider value={{ focusedField, setFocusedField }}>
|
|
370
|
+
<FocusContext.Provider value={{ focusedField, setFocusedField, isLoading: isLoading || false }}>
|
|
302
371
|
<box flexDirection='row' flexGrow={1} justifyContent='center'>
|
|
303
|
-
<box flexDirection='column'>
|
|
372
|
+
<box flexGrow={0} flexDirection='column'>
|
|
304
373
|
<ScrollBox
|
|
305
374
|
ref={scrollBoxRef}
|
|
306
375
|
flexGrow={1}
|
|
@@ -308,15 +377,40 @@ export const Form: FormType = ((props) => {
|
|
|
308
377
|
rootOptions: {
|
|
309
378
|
maxWidth: FORM_MAX_WIDTH,
|
|
310
379
|
},
|
|
380
|
+
|
|
311
381
|
contentOptions: {
|
|
312
382
|
justifyContent: 'center',
|
|
313
383
|
},
|
|
314
384
|
}}
|
|
315
385
|
>
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
386
|
+
{/* Navigation header with title, loading bar, and accessory */}
|
|
387
|
+
|
|
388
|
+
{/*<box
|
|
389
|
+
border={false}
|
|
390
|
+
style={{
|
|
391
|
+
flexShrink: 0,
|
|
392
|
+
flexDirection: 'row',
|
|
393
|
+
paddingBottom: 1,
|
|
394
|
+
justifyContent: 'space-between',
|
|
395
|
+
alignItems: 'center',
|
|
396
|
+
gap: 3,
|
|
397
|
+
}}
|
|
398
|
+
>
|
|
399
|
+
<box overflow='hidden'>
|
|
400
|
+
<LoadingBar
|
|
401
|
+
title={navigationTitle || ''}
|
|
402
|
+
isLoading={isLoading || navigationPending}
|
|
403
|
+
/>
|
|
404
|
+
</box>
|
|
405
|
+
{searchBarAccessory}
|
|
406
|
+
</box>*/}
|
|
407
|
+
|
|
408
|
+
<box>
|
|
409
|
+
<FormFieldDescendantsProvider value={descendantsContext}>
|
|
410
|
+
{props.children}
|
|
411
|
+
<FormEnd />
|
|
412
|
+
</FormFieldDescendantsProvider>
|
|
413
|
+
</box>
|
|
320
414
|
</ScrollBox>
|
|
321
415
|
<FormFooter />
|
|
322
416
|
</box>
|
|
@@ -351,3 +445,27 @@ Form.TagPicker = TagPicker
|
|
|
351
445
|
Form.FilePicker = FilePicker
|
|
352
446
|
Form.Separator = Separator
|
|
353
447
|
Form.Description = Description
|
|
448
|
+
|
|
449
|
+
// LinkAccessory component - shows a link in the navigation bar
|
|
450
|
+
function LinkAccessory(props: LinkAccessoryProps): any {
|
|
451
|
+
return (
|
|
452
|
+
<box
|
|
453
|
+
style={{
|
|
454
|
+
flexShrink: 0,
|
|
455
|
+
maxWidth: '50%',
|
|
456
|
+
overflow: 'hidden',
|
|
457
|
+
paddingRight: 1,
|
|
458
|
+
}}
|
|
459
|
+
>
|
|
460
|
+
<text
|
|
461
|
+
fg={Theme.textMuted}
|
|
462
|
+
attributes={TextAttributes.UNDERLINE}
|
|
463
|
+
wrapMode='none'
|
|
464
|
+
>
|
|
465
|
+
{props.text}
|
|
466
|
+
</text>
|
|
467
|
+
</box>
|
|
468
|
+
)
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
Form.LinkAccessory = LinkAccessory
|
|
@@ -6,6 +6,7 @@ import { FormItemProps, FormItemRef } from './types'
|
|
|
6
6
|
import { Theme } from 'termcast/src/theme'
|
|
7
7
|
import { WithLeftBorder } from './with-left-border'
|
|
8
8
|
import { useFormNavigation } from './use-form-navigation'
|
|
9
|
+
import { LoadingText } from 'termcast/src/components/loading-text'
|
|
9
10
|
|
|
10
11
|
export interface PasswordFieldProps extends FormItemProps<string> {
|
|
11
12
|
placeholder?: string
|
|
@@ -15,7 +16,8 @@ export type PasswordFieldRef = FormItemRef
|
|
|
15
16
|
|
|
16
17
|
export const PasswordField = (props: PasswordFieldProps): any => {
|
|
17
18
|
const { control } = useFormContext()
|
|
18
|
-
const
|
|
19
|
+
const focusContext = useFocusContext()
|
|
20
|
+
const { focusedField, setFocusedField } = focusContext
|
|
19
21
|
const isFocused = focusedField === props.id
|
|
20
22
|
const elementRef = useRef<BoxRenderable>(null)
|
|
21
23
|
const realValueRef = useRef(props.defaultValue || props.value || '')
|
|
@@ -38,15 +40,19 @@ export const PasswordField = (props: PasswordFieldProps): any => {
|
|
|
38
40
|
|
|
39
41
|
return (
|
|
40
42
|
<box ref={elementRef} flexDirection="column">
|
|
41
|
-
<WithLeftBorder withDiamond isFocused={isFocused}>
|
|
42
|
-
<
|
|
43
|
-
fg={isFocused ? Theme.primary : Theme.text}
|
|
43
|
+
<WithLeftBorder withDiamond isFocused={isFocused} isLoading={focusContext.isLoading}>
|
|
44
|
+
<box
|
|
44
45
|
onMouseDown={() => {
|
|
45
46
|
setFocusedField(props.id)
|
|
46
47
|
}}
|
|
47
48
|
>
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
<LoadingText
|
|
50
|
+
isLoading={isFocused && focusContext.isLoading}
|
|
51
|
+
color={isFocused ? Theme.primary : Theme.text}
|
|
52
|
+
>
|
|
53
|
+
{props.title || ''}
|
|
54
|
+
</LoadingText>
|
|
55
|
+
</box>
|
|
50
56
|
</WithLeftBorder>
|
|
51
57
|
<WithLeftBorder isFocused={isFocused}>
|
|
52
58
|
<input
|
|
@@ -7,6 +7,7 @@ import { Theme } from 'termcast/src/theme'
|
|
|
7
7
|
import { WithLeftBorder } from './with-left-border'
|
|
8
8
|
import { useFormNavigation } from './use-form-navigation'
|
|
9
9
|
import { createTextareaFormRef } from './form-ref'
|
|
10
|
+
import { LoadingText } from 'termcast/src/components/loading-text'
|
|
10
11
|
|
|
11
12
|
export interface TextAreaProps extends FormItemProps<string> {
|
|
12
13
|
placeholder?: string
|
|
@@ -17,7 +18,8 @@ export type TextAreaRef = FormItemRef
|
|
|
17
18
|
|
|
18
19
|
export const TextArea = (props: TextAreaProps): any => {
|
|
19
20
|
const { register, formState } = useFormContext()
|
|
20
|
-
const
|
|
21
|
+
const focusContext = useFocusContext()
|
|
22
|
+
const { focusedField, setFocusedField } = focusContext
|
|
21
23
|
const isFocused = focusedField === props.id
|
|
22
24
|
|
|
23
25
|
const elementRef = useRef<BoxRenderable>(null)
|
|
@@ -58,21 +60,26 @@ export const TextArea = (props: TextAreaProps): any => {
|
|
|
58
60
|
|
|
59
61
|
return (
|
|
60
62
|
<box ref={elementRef} flexDirection="column">
|
|
61
|
-
<WithLeftBorder withDiamond isFocused={isFocused}>
|
|
62
|
-
<
|
|
63
|
-
fg={isFocused ? Theme.primary : Theme.text}
|
|
63
|
+
<WithLeftBorder withDiamond isFocused={isFocused} isLoading={focusContext.isLoading}>
|
|
64
|
+
<box
|
|
64
65
|
onMouseDown={() => {
|
|
65
66
|
setFocusedField(props.id)
|
|
66
67
|
}}
|
|
67
68
|
>
|
|
68
|
-
|
|
69
|
-
|
|
69
|
+
<LoadingText
|
|
70
|
+
isLoading={isFocused && focusContext.isLoading}
|
|
71
|
+
color={isFocused ? Theme.primary : Theme.text}
|
|
72
|
+
>
|
|
73
|
+
{props.title || ''}
|
|
74
|
+
</LoadingText>
|
|
75
|
+
</box>
|
|
70
76
|
</WithLeftBorder>
|
|
71
77
|
|
|
72
78
|
<WithLeftBorder isFocused={isFocused}>
|
|
73
79
|
<box flexGrow={1}>
|
|
74
80
|
<textarea
|
|
75
81
|
ref={handleRef}
|
|
82
|
+
wrapMode='none'
|
|
76
83
|
initialValue={props.defaultValue || props.value || ''}
|
|
77
84
|
onContentChange={handleContentChange}
|
|
78
85
|
minHeight={4}
|
|
@@ -7,6 +7,7 @@ import { Theme } from 'termcast/src/theme'
|
|
|
7
7
|
import { WithLeftBorder } from './with-left-border'
|
|
8
8
|
import { useFormNavigation } from './use-form-navigation'
|
|
9
9
|
import { createTextareaFormRef } from './form-ref'
|
|
10
|
+
import { LoadingText } from 'termcast/src/components/loading-text'
|
|
10
11
|
|
|
11
12
|
export interface TextFieldProps extends FormItemProps<string> {
|
|
12
13
|
placeholder?: string
|
|
@@ -16,7 +17,8 @@ export type TextFieldRef = FormItemRef
|
|
|
16
17
|
|
|
17
18
|
export const TextField = (props: TextFieldProps): any => {
|
|
18
19
|
const { register, formState } = useFormContext()
|
|
19
|
-
const
|
|
20
|
+
const focusContext = useFocusContext()
|
|
21
|
+
const { focusedField, setFocusedField } = focusContext
|
|
20
22
|
const isFocused = focusedField === props.id
|
|
21
23
|
|
|
22
24
|
const elementRef = useRef<BoxRenderable>(null)
|
|
@@ -59,15 +61,19 @@ export const TextField = (props: TextFieldProps): any => {
|
|
|
59
61
|
|
|
60
62
|
return (
|
|
61
63
|
<box ref={elementRef} flexDirection="column">
|
|
62
|
-
<WithLeftBorder withDiamond isFocused={isFocused}>
|
|
63
|
-
<
|
|
64
|
-
fg={isFocused ? Theme.primary : Theme.text}
|
|
64
|
+
<WithLeftBorder withDiamond isFocused={isFocused} isLoading={focusContext.isLoading}>
|
|
65
|
+
<box
|
|
65
66
|
onMouseDown={() => {
|
|
66
67
|
setFocusedField(props.id)
|
|
67
68
|
}}
|
|
68
69
|
>
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
<LoadingText
|
|
71
|
+
isLoading={isFocused && focusContext.isLoading}
|
|
72
|
+
color={isFocused ? Theme.primary : Theme.text}
|
|
73
|
+
>
|
|
74
|
+
{props.title || ''}
|
|
75
|
+
</LoadingText>
|
|
76
|
+
</box>
|
|
71
77
|
</WithLeftBorder>
|
|
72
78
|
<WithLeftBorder isFocused={isFocused}>
|
|
73
79
|
<textarea
|
|
@@ -77,6 +83,7 @@ export const TextField = (props: TextFieldProps): any => {
|
|
|
77
83
|
{ name: 'return', action: 'submit' },
|
|
78
84
|
{ name: 'linefeed', action: 'submit' },
|
|
79
85
|
]}
|
|
86
|
+
wrapMode='none'
|
|
80
87
|
initialValue={props.defaultValue || props.value || ''}
|
|
81
88
|
onContentChange={handleContentChange}
|
|
82
89
|
placeholder={props.placeholder}
|
|
@@ -6,12 +6,18 @@ export interface FormValues {
|
|
|
6
6
|
[key: string]: FormValue
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
export interface LinkAccessoryProps {
|
|
10
|
+
target: string
|
|
11
|
+
text: string
|
|
12
|
+
}
|
|
13
|
+
|
|
9
14
|
export interface FormProps {
|
|
10
15
|
actions?: React.ReactNode
|
|
11
16
|
children?: React.ReactNode
|
|
12
17
|
navigationTitle?: string
|
|
13
18
|
isLoading?: boolean
|
|
14
19
|
enableDrafts?: boolean
|
|
20
|
+
searchBarAccessory?: React.ReactElement<LinkAccessoryProps> | null
|
|
15
21
|
}
|
|
16
22
|
|
|
17
23
|
export interface FormItemProps<T> {
|