bitboss-ui 2.1.135 → 3.0.0-alpha.4
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/LICENSE +21 -0
- package/README.md +274 -24
- package/bin/bitboss-ui-mcp.mjs +662 -0
- package/bin/bitboss-ui.mjs +710 -0
- package/bin/bitboss-ui.node.test.mjs +113 -0
- package/dist/ai/BbAccordion.md +528 -113
- package/dist/ai/BbAlert.md +380 -197
- package/dist/ai/BbAsterisk.md +46 -0
- package/dist/ai/BbAvatar.md +458 -178
- package/dist/ai/BbBadge.md +697 -225
- package/dist/ai/BbBadgeButton.md +54 -0
- package/dist/ai/BbBaseButton.md +491 -0
- package/dist/ai/BbBaseCheckbox.md +49 -0
- package/dist/ai/BbBaseCheckboxGroup.md +64 -0
- package/dist/ai/BbBaseCheckboxIcon.md +176 -0
- package/dist/ai/BbBaseColorInput.md +64 -0
- package/dist/ai/BbBaseColorPalette.md +39 -0
- package/dist/ai/BbBaseDatePicker.md +42 -0
- package/dist/ai/BbBaseDatePickerInput.md +73 -0
- package/dist/ai/BbBaseInputContainer.md +48 -0
- package/dist/ai/BbBaseNumberInput.md +71 -0
- package/dist/ai/BbBaseRadio.md +46 -0
- package/dist/ai/BbBaseRadioGroup.md +64 -0
- package/dist/ai/BbBaseRadioIcon.md +191 -0
- package/dist/ai/BbBaseRating.md +51 -0
- package/dist/ai/BbBaseSelect.md +88 -0
- package/dist/ai/BbBaseSlider.md +56 -0
- package/dist/ai/BbBaseSwitch.md +49 -0
- package/dist/ai/BbBaseSwitchGroup.md +64 -0
- package/dist/ai/BbBaseSwitchIcon.md +186 -0
- package/dist/ai/BbBaseTag.md +68 -0
- package/dist/ai/BbBaseTextInput.md +75 -0
- package/dist/ai/BbBaseTextarea.md +62 -0
- package/dist/ai/BbBaseTimePickerInput.md +62 -0
- package/dist/ai/BbBreadcrumbs.md +553 -503
- package/dist/ai/BbButton.md +841 -636
- package/dist/ai/BbCheckbox.md +585 -254
- package/dist/ai/BbCheckboxGroup.md +573 -361
- package/dist/ai/BbCollapsible.md +425 -103
- package/dist/ai/BbColorInput.md +536 -323
- package/dist/ai/BbColorPalette.md +569 -330
- package/dist/ai/BbConfirm.md +659 -136
- package/dist/ai/BbConfirmPortal.md +28 -0
- package/dist/ai/BbDatePicker.md +244 -0
- package/dist/ai/BbDatePickerInput.md +720 -388
- package/dist/ai/BbDialog.md +778 -99
- package/dist/ai/BbDropdown.md +1072 -763
- package/dist/ai/BbDropdownButton.md +525 -614
- package/dist/ai/BbDropdownGroup.md +34 -0
- package/dist/ai/BbDropzone.md +1206 -436
- package/dist/ai/BbForm.md +239 -0
- package/dist/ai/BbIcon.md +382 -212
- package/dist/ai/BbIndicator.md +283 -0
- package/dist/ai/BbNumberInput.md +717 -355
- package/dist/ai/BbOffCanvas.md +897 -498
- package/dist/ai/BbPagination.md +489 -535
- package/dist/ai/BbPopover.md +591 -546
- package/dist/ai/BbProgress.md +462 -69
- package/dist/ai/BbRadio.md +352 -229
- package/dist/ai/BbRadioGroup.md +437 -353
- package/dist/ai/BbRating.md +372 -225
- package/dist/ai/BbSelect.md +1399 -536
- package/dist/ai/BbSelectPopover.md +1255 -1578
- package/dist/ai/BbSlider.md +500 -256
- package/dist/ai/BbSmoothHeight.md +485 -139
- package/dist/ai/BbSpinner.md +718 -131
- package/dist/ai/BbSwitch.md +573 -119
- package/dist/ai/BbSwitchGroup.md +521 -214
- package/dist/ai/BbTable.md +1996 -2365
- package/dist/ai/BbTabs.md +1147 -0
- package/dist/ai/BbTabsList.md +29 -0
- package/dist/ai/BbTabsPanes.md +27 -0
- package/dist/ai/BbTabsRoot.md +57 -0
- package/dist/ai/BbTag.md +416 -292
- package/dist/ai/BbTextInput.md +878 -333
- package/dist/ai/BbTextarea.md +850 -255
- package/dist/ai/BbTimePicker.md +231 -0
- package/dist/ai/BbTimePickerInput.md +200 -0
- package/dist/ai/BbToast.md +669 -196
- package/dist/ai/BbToastPortal.md +28 -0
- package/dist/ai/BbTooltip.md +453 -346
- package/dist/ai/BbTree.md +993 -234
- package/dist/ai/ChipsBox.md +27 -199
- package/dist/ai/ClearableButton.md +21 -55
- package/dist/ai/CommaBox.md +25 -200
- package/dist/ai/CommonInputWrapper.md +42 -0
- package/dist/ai/CommonPopover.md +30 -510
- package/dist/ai/ErrorIcon.md +19 -56
- package/dist/ai/FlatListBox.md +35 -379
- package/dist/ai/GroupedListBox.md +37 -535
- package/dist/ai/GuidesComponent.md +21 -0
- package/dist/ai/ListBox.md +38 -231
- package/dist/ai/OptionsContainer.md +34 -253
- package/dist/ai/WarningIcon.md +21 -0
- package/dist/ai/components.json +15897 -0
- package/dist/ai/composables/useBbConfig.md +80 -0
- package/dist/ai/composables/useBbFormContext.md +110 -0
- package/dist/ai/composables/useBbTableContext.md +167 -0
- package/dist/ai/composables/useBbTabsContext.md +118 -0
- package/dist/ai/composables/useBroadcastChannelInstance.md +97 -0
- package/dist/ai/composables/useConfirm.md +313 -0
- package/dist/ai/composables/useCountdown.md +86 -0
- package/dist/ai/composables/useId.md +60 -0
- package/dist/ai/composables/useMobile.md +85 -0
- package/dist/ai/composables/useToast.md +238 -0
- package/dist/ai/guides/agent-contract.md +165 -0
- package/dist/ai/guides/ai-router.md +113 -0
- package/dist/ai/guides/app-layout.md +318 -0
- package/dist/ai/guides/coherence-playbook.md +136 -0
- package/dist/ai/guides/component-picker.md +236 -0
- package/dist/ai/guides/design-language.md +478 -0
- package/dist/ai/guides/design-tokens.md +450 -0
- package/dist/ai/guides/fetch-items-playbook.md +161 -0
- package/dist/ai/guides/floating-bottom-panel.md +130 -0
- package/dist/ai/guides/icons-policy.md +257 -0
- package/dist/ai/guides/inertia-helpers.md +1443 -0
- package/dist/ai/guides/inline-edit-playbook.md +267 -0
- package/dist/ai/guides/installation-and-plugin-setup.md +440 -0
- package/dist/ai/guides/migration/components/bb-alert.md +37 -0
- package/dist/ai/guides/migration/components/bb-avatar.md +24 -0
- package/dist/ai/guides/migration/components/bb-badge.md +110 -0
- package/dist/ai/guides/migration/components/bb-breadcrumbs.md +26 -0
- package/dist/ai/guides/migration/components/bb-button.md +55 -0
- package/dist/ai/guides/migration/components/bb-checkbox-group.md +54 -0
- package/dist/ai/guides/migration/components/bb-checkbox.md +45 -0
- package/dist/ai/guides/migration/components/bb-chip.md +35 -0
- package/dist/ai/guides/migration/components/bb-collapsible.md +26 -0
- package/dist/ai/guides/migration/components/bb-color-input.md +23 -0
- package/dist/ai/guides/migration/components/bb-color-palette.md +31 -0
- package/dist/ai/guides/migration/components/bb-date-picker-input.md +84 -0
- package/dist/ai/guides/migration/components/bb-dialog.md +46 -0
- package/dist/ai/guides/migration/components/bb-dropdown-button.md +31 -0
- package/dist/ai/guides/migration/components/bb-dropdown.md +36 -0
- package/dist/ai/guides/migration/components/bb-dropzone.md +27 -0
- package/dist/ai/guides/migration/components/bb-icon.md +27 -0
- package/dist/ai/guides/migration/components/bb-offcanvas.md +63 -0
- package/dist/ai/guides/migration/components/bb-pagination.md +24 -0
- package/dist/ai/guides/migration/components/bb-popover.md +52 -0
- package/dist/ai/guides/migration/components/bb-rating.md +24 -0
- package/dist/ai/guides/migration/components/bb-select-popover.md +59 -0
- package/dist/ai/guides/migration/components/bb-select.md +69 -0
- package/dist/ai/guides/migration/components/bb-slider.md +25 -0
- package/dist/ai/guides/migration/components/bb-smooth-height.md +45 -0
- package/dist/ai/guides/migration/components/bb-spinner.md +23 -0
- package/dist/ai/guides/migration/components/bb-table.md +66 -0
- package/dist/ai/guides/migration/components/bb-tabs.md +47 -0
- package/dist/ai/guides/migration/components/bb-tag.md +29 -0
- package/dist/ai/guides/migration/components/bb-text-input.md +37 -0
- package/dist/ai/guides/migration/components/bb-toast.md +47 -0
- package/dist/ai/guides/migration/components/bb-tooltip.md +34 -0
- package/dist/ai/guides/migration/components/bb-tree.md +77 -0
- package/dist/ai/guides/migration/components/use-confirm.md +46 -0
- package/dist/ai/guides/migration/v2-to-v3.md +649 -0
- package/dist/ai/guides/options-items-playbook.md +222 -0
- package/dist/ai/guides/page-shell.md +113 -0
- package/dist/ai/guides/validated-forms.md +177 -0
- package/dist/ai/index.md +158 -32
- package/dist/ai/recipes/inertia/approvals-inbox.md +1635 -0
- package/dist/ai/recipes/inertia/inline-edit-workspace.md +1765 -0
- package/dist/ai/recipes/inertia/layout-scaffold.md +385 -0
- package/dist/ai/recipes/inertia/onboarding.md +1791 -0
- package/dist/ai/recipes/inertia/ownership-atlas.md +1327 -0
- package/dist/ai/recipes/inertia/record-form.md +1489 -0
- package/dist/ai/recipes/inertia/records-workspace.md +1416 -0
- package/dist/ai/recipes/inertia/upload-center.md +2003 -0
- package/dist/ai/recipes/inertia/wizard-form.md +1500 -0
- package/dist/ai/recipes/nuxt/approvals-inbox.md +1578 -0
- package/dist/ai/recipes/nuxt/inline-edit-workspace.md +1703 -0
- package/dist/ai/recipes/nuxt/layout-scaffold.md +385 -0
- package/dist/ai/recipes/nuxt/onboarding.md +1724 -0
- package/dist/ai/recipes/nuxt/record-form.md +1502 -0
- package/dist/ai/recipes/nuxt/records-workspace.md +1329 -0
- package/dist/ai/recipes/nuxt/upload-center.md +1886 -0
- package/dist/ai/recipes/nuxt/wizard-form.md +1509 -0
- package/dist/ai/recipes/vue/approvals-inbox.md +1496 -0
- package/dist/ai/recipes/vue/inline-edit-workspace.md +1594 -0
- package/dist/ai/recipes/vue/layout-scaffold.md +385 -0
- package/dist/ai/recipes/vue/onboarding.md +1679 -0
- package/dist/ai/recipes/vue/record-form.md +1586 -0
- package/dist/ai/recipes/vue/records-workspace.md +1245 -0
- package/dist/ai/recipes/vue/upload-center.md +1845 -0
- package/dist/ai/recipes/vue/wizard-form.md +1441 -0
- package/dist/ai/source/BbAccordion.md +123 -0
- package/dist/ai/source/BbAlert.md +401 -0
- package/dist/ai/source/BbAsterisk.md +32 -0
- package/dist/ai/source/BbAvatar.md +258 -0
- package/dist/ai/source/BbBadge.md +560 -0
- package/dist/ai/source/BbBadgeButton.md +490 -0
- package/dist/ai/source/BbBaseButton.md +682 -0
- package/dist/ai/source/BbBaseCheckbox.md +461 -0
- package/dist/ai/source/BbBaseCheckboxGroup.md +554 -0
- package/dist/ai/source/BbBaseCheckboxIcon.md +201 -0
- package/dist/ai/source/BbBaseColorInput.md +416 -0
- package/dist/ai/source/BbBaseColorPalette.md +934 -0
- package/dist/ai/source/BbBaseDatePicker.md +1001 -0
- package/dist/ai/source/BbBaseDatePickerInput.md +1785 -0
- package/dist/ai/source/BbBaseInputContainer.md +1210 -0
- package/dist/ai/source/BbBaseNumberInput.md +617 -0
- package/dist/ai/source/BbBaseRadio.md +377 -0
- package/dist/ai/source/BbBaseRadioGroup.md +533 -0
- package/dist/ai/source/BbBaseRadioIcon.md +153 -0
- package/dist/ai/source/BbBaseRating.md +573 -0
- package/dist/ai/source/BbBaseSelect.md +898 -0
- package/dist/ai/source/BbBaseSlider.md +1085 -0
- package/dist/ai/source/BbBaseSwitch.md +246 -0
- package/dist/ai/source/BbBaseSwitchGroup.md +302 -0
- package/dist/ai/source/BbBaseSwitchIcon.md +206 -0
- package/dist/ai/source/BbBaseTag.md +694 -0
- package/dist/ai/source/BbBaseTextInput.md +511 -0
- package/dist/ai/source/BbBaseTextarea.md +480 -0
- package/dist/ai/source/BbBaseTimePickerInput.md +1031 -0
- package/dist/ai/source/BbBreadcrumbs.md +815 -0
- package/dist/ai/source/BbButton.md +1006 -0
- package/dist/ai/source/BbCheckbox.md +326 -0
- package/dist/ai/source/BbCheckboxGroup.md +419 -0
- package/dist/ai/source/BbCollapsible.md +137 -0
- package/dist/ai/source/BbColorInput.md +322 -0
- package/dist/ai/source/BbColorPalette.md +371 -0
- package/dist/ai/source/BbConfirm.md +193 -0
- package/dist/ai/source/BbConfirmPortal.md +74 -0
- package/dist/ai/source/BbDatePicker.md +729 -0
- package/dist/ai/source/BbDatePickerInput.md +525 -0
- package/dist/ai/source/BbDialog.md +818 -0
- package/dist/ai/source/BbDropdown.md +2387 -0
- package/dist/ai/source/BbDropdownButton.md +315 -0
- package/dist/ai/source/BbDropdownGroup.md +1259 -0
- package/dist/ai/source/BbDropzone.md +864 -0
- package/dist/ai/source/BbForm.md +100 -0
- package/dist/ai/source/BbIcon.md +214 -0
- package/dist/ai/source/BbIndicator.md +298 -0
- package/dist/ai/source/BbNumberInput.md +374 -0
- package/dist/ai/source/BbOffCanvas.md +1266 -0
- package/dist/ai/source/BbPagination.md +835 -0
- package/dist/ai/source/BbPopover.md +860 -0
- package/dist/ai/source/BbProgress.md +140 -0
- package/dist/ai/source/BbRadio.md +303 -0
- package/dist/ai/source/BbRadioGroup.md +420 -0
- package/dist/ai/source/BbRating.md +369 -0
- package/dist/ai/source/BbSelect.md +688 -0
- package/dist/ai/source/BbSelectPopover.md +2234 -0
- package/dist/ai/source/BbSlider.md +378 -0
- package/dist/ai/source/BbSmoothHeight.md +265 -0
- package/dist/ai/source/BbSpinner.md +127 -0
- package/dist/ai/source/BbSwitch.md +175 -0
- package/dist/ai/source/BbSwitchGroup.md +240 -0
- package/dist/ai/source/BbTable.md +3433 -0
- package/dist/ai/source/BbTabs.md +403 -0
- package/dist/ai/source/BbTabsList.md +578 -0
- package/dist/ai/source/BbTabsPanes.md +368 -0
- package/dist/ai/source/BbTabsRoot.md +372 -0
- package/dist/ai/source/BbTag.md +390 -0
- package/dist/ai/source/BbTextInput.md +352 -0
- package/dist/ai/source/BbTextarea.md +337 -0
- package/dist/ai/source/BbTimePicker.md +595 -0
- package/dist/ai/source/BbTimePickerInput.md +376 -0
- package/dist/ai/source/BbToast.md +525 -0
- package/dist/ai/source/BbToastPortal.md +482 -0
- package/dist/ai/source/BbTooltip.md +522 -0
- package/dist/ai/source/BbTree.md +323 -0
- package/dist/ai/source/ChipsBox.md +201 -0
- package/dist/ai/source/ClearableButton.md +70 -0
- package/dist/ai/source/CommaBox.md +202 -0
- package/dist/ai/source/CommonInputWrapper.md +554 -0
- package/dist/ai/source/CommonPopover.md +616 -0
- package/dist/ai/source/ErrorIcon.md +43 -0
- package/dist/ai/source/FlatListBox.md +396 -0
- package/dist/ai/source/GroupedListBox.md +540 -0
- package/dist/ai/source/GuidesComponent.md +383 -0
- package/dist/ai/source/ListBox.md +238 -0
- package/dist/ai/source/OptionsContainer.md +191 -0
- package/dist/ai/source/WarningIcon.md +47 -0
- package/dist/alert-variants.d.ts +19 -0
- package/dist/assets/css/reset.css_inline.js +4 -0
- package/dist/assets/svgs/alert.svg_raw.js +4 -0
- package/dist/assets/svgs/calendar.svg_raw.js +4 -0
- package/dist/assets/svgs/check_stroke.svg_raw.js +4 -0
- package/dist/assets/svgs/chevron_down.svg_raw.js +4 -0
- package/dist/assets/svgs/chevron_right.svg_raw.js +4 -0
- package/dist/assets/svgs/clear.svg_raw.js +4 -0
- package/dist/assets/svgs/clock.svg_raw.js +4 -0
- package/dist/assets/svgs/error.svg_raw.js +4 -0
- package/dist/assets/svgs/search.svg_raw.js +4 -0
- package/dist/assets/svgs/sort.svg_raw.js +4 -0
- package/dist/assets/svgs/spinner.svg_raw.js +4 -0
- package/dist/assets/svgs/user.svg_raw.js +4 -0
- package/dist/badge-variants.d.ts +19 -0
- package/dist/button-variants.d.ts +21 -0
- package/dist/components/BbAccordion/BbAccordion.vue.d.ts +9 -3
- package/dist/components/BbAccordion/BbAccordion.vue.js +6 -0
- package/dist/components/BbAccordion/BbAccordion.vue_vue_type_script_setup_true_lang.js +66 -0
- package/dist/components/BbAccordion/types.d.ts +12 -8
- package/dist/components/BbAlert/BbAlert.vue.d.ts +12 -3
- package/dist/components/BbAlert/BbAlert.vue.js +6 -0
- package/dist/components/BbAlert/BbAlert.vue_vue_type_script_setup_true_lang.js +65 -0
- package/dist/components/BbAlert/types.d.ts +56 -12
- package/dist/components/BbAsterisk/BbAsterisk.vue.d.ts +3 -0
- package/dist/components/BbAsterisk/BbAsterisk.vue.js +6 -0
- package/dist/components/BbAsterisk/BbAsterisk.vue_vue_type_script_setup_true_lang.js +13 -0
- package/dist/components/BbAvatar/BbAvatar.vue.d.ts +5 -0
- package/dist/components/BbAvatar/BbAvatar.vue.js +6 -0
- package/dist/components/BbAvatar/BbAvatar.vue_vue_type_script_setup_true_lang.js +78 -0
- package/dist/components/BbAvatar/types.d.ts +6 -5
- package/dist/components/BbBadge/BadgeAppendIcon.vue.d.ts +3 -0
- package/dist/components/BbBadge/BadgeAppendIcon.vue.js +5 -0
- package/dist/components/BbBadge/BadgeAppendIcon.vue_vue_type_script_setup_true_lang.js +22 -0
- package/dist/components/BbBadge/BadgeBodyContent.vue.d.ts +15 -0
- package/dist/components/BbBadge/BadgeBodyContent.vue.js +5 -0
- package/dist/components/BbBadge/BadgeBodyContent.vue_vue_type_script_setup_true_lang.js +34 -0
- package/dist/components/BbBadge/BbBadge.vue.d.ts +8 -1
- package/dist/components/BbBadge/BbBadge.vue.js +6 -0
- package/dist/components/BbBadge/BbBadge.vue_vue_type_script_setup_true_lang.js +83 -0
- package/dist/components/BbBadge/BbBadgeButton.vue.d.ts +12 -0
- package/dist/components/BbBadge/BbBadgeButton.vue.js +5 -0
- package/dist/components/BbBadge/BbBadgeButton.vue_vue_type_script_setup_true_lang.js +54 -0
- package/dist/components/BbBadge/badgeButtonContext.d.ts +27 -0
- package/dist/components/BbBadge/badgeButtonContext.js +5 -0
- package/dist/components/BbBadge/types.d.ts +61 -16
- package/dist/components/BbBaseButton/BbBaseButton.vue.d.ts +13 -0
- package/dist/components/BbBaseButton/BbBaseButton.vue.js +6 -0
- package/dist/components/BbBaseButton/BbBaseButton.vue_vue_type_script_setup_true_lang.js +171 -0
- package/dist/components/BbBaseButton/RouterComponent.vue.js +5 -0
- package/dist/components/BbBaseButton/RouterComponent.vue_vue_type_script_setup_true_lang.js +29 -0
- package/dist/components/BbBaseButton/types.d.ts +191 -0
- package/dist/components/BbBaseButton/types.js +33 -0
- package/dist/components/BbBaseCheckbox/BbBaseCheckbox.vue.d.ts +40 -0
- package/dist/components/BbBaseCheckbox/BbBaseCheckbox.vue.js +6 -0
- package/dist/components/BbBaseCheckbox/BbBaseCheckbox.vue_vue_type_script_setup_true_lang.js +145 -0
- package/dist/components/BbBaseCheckbox/types.d.ts +224 -0
- package/dist/components/BbBaseCheckboxGroup/BbBaseCheckboxGroup.vue.d.ts +19 -0
- package/dist/components/BbBaseCheckboxGroup/BbBaseCheckboxGroup.vue.js +6 -0
- package/dist/components/BbBaseCheckboxGroup/BbBaseCheckboxGroup.vue_vue_type_script_setup_true_lang.js +190 -0
- package/dist/components/BbBaseCheckboxGroup/types.d.ts +257 -0
- package/dist/components/BbBaseCheckboxIcon/BbBaseCheckboxIcon.vue.d.ts +4 -0
- package/dist/components/BbBaseCheckboxIcon/BbBaseCheckboxIcon.vue.js +6 -0
- package/dist/components/BbBaseCheckboxIcon/BbBaseCheckboxIcon.vue_vue_type_script_setup_true_lang.js +43 -0
- package/dist/components/BbBaseCheckboxIcon/types.d.ts +47 -0
- package/dist/components/BbBaseColorInput/BbBaseColorInput.vue.d.ts +42 -0
- package/dist/components/BbBaseColorInput/BbBaseColorInput.vue.js +6 -0
- package/dist/components/BbBaseColorInput/BbBaseColorInput.vue_vue_type_script_setup_true_lang.js +178 -0
- package/dist/components/BbBaseColorInput/types.d.ts +210 -0
- package/dist/components/BbBaseColorPalette/BbBaseColorPalette.vue.d.ts +13 -0
- package/dist/components/BbBaseColorPalette/BbBaseColorPalette.vue.js +6 -0
- package/dist/components/BbBaseColorPalette/BbBaseColorPalette.vue_vue_type_script_setup_true_lang.js +395 -0
- package/dist/components/BbBaseColorPalette/types.d.ts +40 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePicker.vue.js +6 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePicker.vue_vue_type_script_setup_true_lang.js +300 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePickerInputDaySelector.vue.d.ts +44 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePickerInputDaySelector.vue.js +5 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePickerInputDaySelector.vue_vue_type_script_setup_true_lang.js +310 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePickerMonthSelector.vue.d.ts +5 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePickerMonthSelector.vue.js +5 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePickerMonthSelector.vue_vue_type_script_setup_true_lang.js +63 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePickerYearSelector.vue.d.ts +5 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePickerYearSelector.vue.js +5 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePickerYearSelector.vue_vue_type_script_setup_true_lang.js +75 -0
- package/dist/components/BbBaseDatePicker/typeahead.d.ts +112 -0
- package/dist/components/BbBaseDatePicker/typeahead.js +37 -0
- package/dist/components/BbBaseDatePicker/types.d.ts +97 -0
- package/dist/components/BbBaseDatePicker/useDatePickerContext.d.ts +58 -0
- package/dist/components/BbBaseDatePicker/useDatePickerContext.js +11 -0
- package/dist/components/BbBaseDatePickerInput/BbBaseDatePickerInput.vue.d.ts +30 -0
- package/dist/components/BbBaseDatePickerInput/BbBaseDatePickerInput.vue.js +6 -0
- package/dist/components/BbBaseDatePickerInput/BbBaseDatePickerInput.vue_vue_type_script_setup_true_lang.js +716 -0
- package/dist/components/BbBaseDatePickerInput/BbBaseDatePickerTimeSelector.vue.d.ts +30 -0
- package/dist/components/BbBaseDatePickerInput/BbBaseDatePickerTimeSelector.vue.js +6 -0
- package/dist/components/BbBaseDatePickerInput/BbBaseDatePickerTimeSelector.vue_vue_type_script_setup_true_lang.js +204 -0
- package/dist/components/BbBaseDatePickerInput/types.d.ts +311 -0
- package/dist/components/BbBaseInputContainer/BbBaseInputContainer.vue.d.ts +17 -0
- package/dist/components/BbBaseInputContainer/BbBaseInputContainer.vue.js +6 -0
- package/dist/components/BbBaseInputContainer/BbBaseInputContainer.vue_vue_type_script_setup_true_lang.js +191 -0
- package/dist/components/BbBaseInputContainer/types.d.ts +197 -0
- package/dist/components/BbBaseNumberInput/BbBaseNumberInput.vue.d.ts +52 -0
- package/dist/components/BbBaseNumberInput/BbBaseNumberInput.vue.js +6 -0
- package/dist/components/BbBaseNumberInput/BbBaseNumberInput.vue_vue_type_script_setup_true_lang.js +215 -0
- package/dist/components/BbBaseNumberInput/types.d.ts +211 -0
- package/dist/components/BbBaseRadio/BbBaseRadio.vue.d.ts +33 -0
- package/dist/components/BbBaseRadio/BbBaseRadio.vue.js +6 -0
- package/dist/components/BbBaseRadio/BbBaseRadio.vue_vue_type_script_setup_true_lang.js +114 -0
- package/dist/components/BbBaseRadio/types.d.ts +179 -0
- package/dist/components/BbBaseRadioGroup/BbBaseRadioGroup.vue.d.ts +19 -0
- package/dist/components/BbBaseRadioGroup/BbBaseRadioGroup.vue.js +6 -0
- package/dist/components/BbBaseRadioGroup/BbBaseRadioGroup.vue_vue_type_script_setup_true_lang.js +177 -0
- package/dist/components/BbBaseRadioGroup/types.d.ts +223 -0
- package/dist/components/BbBaseRadioIcon/BbBaseRadioIcon.vue.d.ts +4 -0
- package/dist/components/BbBaseRadioIcon/BbBaseRadioIcon.vue.js +6 -0
- package/dist/components/BbBaseRadioIcon/BbBaseRadioIcon.vue_vue_type_script_setup_true_lang.js +28 -0
- package/dist/components/BbBaseRadioIcon/types.d.ts +41 -0
- package/dist/components/BbBaseRating/BbBaseRating.vue.d.ts +38 -0
- package/dist/components/BbBaseRating/BbBaseRating.vue.js +6 -0
- package/dist/components/BbBaseRating/BbBaseRating.vue_vue_type_script_setup_true_lang.js +184 -0
- package/dist/components/BbBaseRating/types.d.ts +168 -0
- package/dist/components/BbBaseSelect/BbBaseSelect.vue.d.ts +19 -0
- package/dist/components/BbBaseSelect/BbBaseSelect.vue.js +6 -0
- package/dist/components/BbBaseSelect/BbBaseSelect.vue_vue_type_script_setup_true_lang.js +307 -0
- package/dist/components/BbBaseSelect/types.d.ts +297 -0
- package/dist/components/BbBaseSlider/BbBaseSlider.vue.d.ts +35 -0
- package/dist/components/BbBaseSlider/BbBaseSlider.vue.js +6 -0
- package/dist/components/BbBaseSlider/BbBaseSlider.vue_vue_type_script_setup_true_lang.js +324 -0
- package/dist/components/BbBaseSlider/types.d.ts +200 -0
- package/dist/components/BbBaseSwitch/BbBaseSwitch.vue.d.ts +35 -0
- package/dist/components/BbBaseSwitch/BbBaseSwitch.vue.js +6 -0
- package/dist/components/BbBaseSwitch/BbBaseSwitch.vue_vue_type_script_setup_true_lang.js +146 -0
- package/dist/components/BbBaseSwitch/types.d.ts +10 -0
- package/dist/components/BbBaseSwitchGroup/BbBaseSwitchGroup.vue.d.ts +19 -0
- package/dist/components/BbBaseSwitchGroup/BbBaseSwitchGroup.vue.js +6 -0
- package/dist/components/BbBaseSwitchGroup/BbBaseSwitchGroup.vue_vue_type_script_setup_true_lang.js +190 -0
- package/dist/components/BbBaseSwitchGroup/types.d.ts +8 -0
- package/dist/components/BbBaseSwitchIcon/BbBaseSwitchIcon.vue.d.ts +4 -0
- package/dist/components/BbBaseSwitchIcon/BbBaseSwitchIcon.vue.js +6 -0
- package/dist/components/BbBaseSwitchIcon/BbBaseSwitchIcon.vue_vue_type_script_setup_true_lang.js +30 -0
- package/dist/components/BbBaseSwitchIcon/types.d.ts +48 -0
- package/dist/components/BbBaseTag/BbBaseTag.vue.d.ts +50 -0
- package/dist/components/BbBaseTag/BbBaseTag.vue.js +6 -0
- package/dist/components/BbBaseTag/BbBaseTag.vue_vue_type_script_setup_true_lang.js +266 -0
- package/dist/components/BbBaseTag/types.d.ts +160 -0
- package/dist/components/BbBaseTextInput/BbBaseTextInput.vue.d.ts +45 -0
- package/dist/components/BbBaseTextInput/BbBaseTextInput.vue.js +6 -0
- package/dist/components/BbBaseTextInput/BbBaseTextInput.vue_vue_type_script_setup_true_lang.js +200 -0
- package/dist/components/BbBaseTextInput/types.d.ts +209 -0
- package/dist/components/BbBaseTextarea/BbBaseTextarea.vue.d.ts +39 -0
- package/dist/components/BbBaseTextarea/BbBaseTextarea.vue.js +6 -0
- package/dist/components/BbBaseTextarea/BbBaseTextarea.vue_vue_type_script_setup_true_lang.js +175 -0
- package/dist/components/BbBaseTextarea/types.d.ts +170 -0
- package/dist/components/BbBaseTimePickerInput/BbBaseTimePickerInput.vue.d.ts +27 -0
- package/dist/components/BbBaseTimePickerInput/BbBaseTimePickerInput.vue.js +6 -0
- package/dist/components/BbBaseTimePickerInput/BbBaseTimePickerInput.vue_vue_type_script_setup_true_lang.js +522 -0
- package/dist/components/BbBaseTimePickerInput/types.d.ts +151 -0
- package/dist/components/BbBreadcrumbs/BbBreadcrumbs.vue.d.ts +0 -1
- package/dist/components/BbBreadcrumbs/BbBreadcrumbs.vue.js +6 -0
- package/dist/components/BbBreadcrumbs/BbBreadcrumbs.vue_vue_type_script_setup_true_lang.js +224 -0
- package/dist/components/BbBreadcrumbs/estimateBreadcrumbItemWidth.js +47 -0
- package/dist/components/BbBreadcrumbs/types.d.ts +37 -22
- package/dist/components/BbButton/BbButton.vue.d.ts +6 -5
- package/dist/components/BbButton/BbButton.vue.js +6 -0
- package/dist/components/BbButton/BbButton.vue_vue_type_script_setup_true_lang.js +181 -0
- package/dist/components/BbButton/types.d.ts +48 -47
- package/dist/components/BbCheckbox/BbCheckbox.vue.d.ts +4 -3
- package/dist/components/BbCheckbox/BbCheckbox.vue.js +6 -0
- package/dist/components/BbCheckbox/BbCheckbox.vue_vue_type_script_setup_true_lang.js +146 -0
- package/dist/components/BbCheckbox/types.d.ts +31 -10
- package/dist/components/BbCheckboxGroup/BbCheckboxGroup.vue.d.ts +2 -13
- package/dist/components/BbCheckboxGroup/BbCheckboxGroup.vue.js +6 -0
- package/dist/components/BbCheckboxGroup/BbCheckboxGroup.vue_vue_type_script_setup_true_lang.js +190 -0
- package/dist/components/BbCheckboxGroup/types.d.ts +51 -23
- package/dist/components/BbCollapsible/BbCollapsible.vue.d.ts +2 -1
- package/dist/components/BbCollapsible/BbCollapsible.vue.js +6 -0
- package/dist/components/BbCollapsible/BbCollapsible.vue_vue_type_script_setup_true_lang.js +31 -0
- package/dist/components/BbCollapsible/types.d.ts +13 -8
- package/dist/components/BbColorInput/BbColorInput.vue.d.ts +10 -1
- package/dist/components/BbColorInput/BbColorInput.vue.js +6 -0
- package/dist/components/BbColorInput/BbColorInput.vue_vue_type_script_setup_true_lang.js +177 -0
- package/dist/components/BbColorInput/types.d.ts +44 -11
- package/dist/components/BbColorPalette/BbColorPalette.vue.d.ts +5 -8
- package/dist/components/BbColorPalette/BbColorPalette.vue.js +5 -0
- package/dist/components/BbColorPalette/BbColorPalette.vue_vue_type_script_setup_true_lang.js +126 -0
- package/dist/components/BbColorPalette/types.d.ts +71 -52
- package/dist/components/BbConfirm/BbConfirm.vue.js +6 -0
- package/dist/components/BbConfirm/BbConfirm.vue_vue_type_script_setup_true_lang.js +138 -0
- package/dist/components/BbConfirm/BbConfirmPortal.vue.d.ts +5 -6
- package/dist/components/BbConfirm/BbConfirmPortal.vue.js +5 -0
- package/dist/components/BbConfirm/BbConfirmPortal.vue_vue_type_script_setup_true_lang.js +19 -0
- package/dist/components/BbDatePicker/BbDatePicker.vue.d.ts +37 -0
- package/dist/components/BbDatePicker/BbDatePicker.vue.js +6 -0
- package/dist/components/BbDatePicker/BbDatePicker.vue_vue_type_script_setup_true_lang.js +319 -0
- package/dist/components/BbDatePicker/types.d.ts +159 -0
- package/dist/components/BbDatePickerInput/BbDatePickerInput.vue.d.ts +3 -1
- package/dist/components/BbDatePickerInput/BbDatePickerInput.vue.js +6 -0
- package/dist/components/BbDatePickerInput/BbDatePickerInput.vue_vue_type_script_setup_true_lang.js +179 -0
- package/dist/components/BbDatePickerInput/types.d.ts +134 -16
- package/dist/components/BbDialog/BbDialog.vue.d.ts +14 -6
- package/dist/components/BbDialog/BbDialog.vue.js +6 -0
- package/dist/components/BbDialog/BbDialog.vue_vue_type_script_setup_true_lang.js +214 -0
- package/dist/components/BbDialog/types.d.ts +146 -1
- package/dist/components/BbDialog/useDialogStack.d.ts +5 -0
- package/dist/components/BbDialog/useDialogStack.js +20 -0
- package/dist/components/BbDropdown/AdaptiveDropdown.vue.d.ts +24 -0
- package/dist/components/BbDropdown/AdaptiveDropdown.vue.js +6 -0
- package/dist/components/BbDropdown/AdaptiveDropdown.vue_vue_type_script_setup_true_lang.js +116 -0
- package/dist/components/BbDropdown/BbDropdown.vue.d.ts +14 -7
- package/dist/components/BbDropdown/BbDropdown.vue.js +6 -0
- package/dist/components/BbDropdown/BbDropdown.vue_vue_type_script_setup_true_lang.js +561 -0
- package/dist/components/BbDropdown/BbDropdownGroup.vue.d.ts +44 -0
- package/dist/components/BbDropdown/BbDropdownGroup.vue.js +5 -0
- package/dist/components/BbDropdown/BbDropdownGroup.vue_vue_type_script_setup_true_lang.js +36 -0
- package/dist/components/BbDropdown/BbDropdownList.vue.d.ts +31 -0
- package/dist/components/BbDropdown/BbDropdownList.vue.js +6 -0
- package/dist/components/BbDropdown/BbDropdownList.vue_vue_type_script_setup_true_lang.js +470 -0
- package/dist/components/BbDropdown/DropdownPipelineResolver.vue.d.ts +16 -0
- package/dist/components/BbDropdown/DropdownPipelineResolver.vue.js +5 -0
- package/dist/components/BbDropdown/DropdownPipelineResolver.vue_vue_type_script_setup_true_lang.js +168 -0
- package/dist/components/BbDropdown/normalizeGroups.d.ts +18 -0
- package/dist/components/BbDropdown/normalizeGroups.js +46 -0
- package/dist/components/BbDropdown/types.d.ts +468 -46
- package/dist/components/BbDropdown/types.js +4 -0
- package/dist/components/BbDropdown/useDropdownContext.d.ts +89 -0
- package/dist/components/BbDropdown/useDropdownContext.js +9 -0
- package/dist/components/BbDropdown/useDropdownGroupRegistry.d.ts +108 -0
- package/dist/components/BbDropdown/useDropdownGroupRegistry.js +5 -0
- package/dist/components/BbDropdownButton/BbDropdownButton.vue.d.ts +3 -3
- package/dist/components/BbDropdownButton/BbDropdownButton.vue.js +6 -0
- package/dist/components/BbDropdownButton/BbDropdownButton.vue_vue_type_script_setup_true_lang.js +119 -0
- package/dist/components/BbDropdownButton/types.d.ts +47 -52
- package/dist/components/BbDropzone/BbDropzone.vue.d.ts +5 -4
- package/dist/components/BbDropzone/BbDropzone.vue.js +6 -0
- package/dist/components/BbDropzone/BbDropzone.vue_vue_type_script_setup_true_lang.js +308 -0
- package/dist/components/BbDropzone/types.d.ts +77 -8
- package/dist/components/BbIcon/BbIcon.vue.d.ts +3 -2
- package/dist/components/BbIcon/BbIcon.vue.js +6 -0
- package/dist/components/BbIcon/BbIcon.vue_vue_type_script_setup_true_lang.js +65 -0
- package/dist/components/BbIcon/types.d.ts +6 -19
- package/dist/components/BbIndicator/BbIndicator.vue.d.ts +14 -0
- package/dist/components/BbIndicator/BbIndicator.vue.js +6 -0
- package/dist/components/BbIndicator/BbIndicator.vue_vue_type_script_setup_true_lang.js +56 -0
- package/dist/components/BbIndicator/types.d.ts +49 -0
- package/dist/components/BbNumberInput/BbNumberInput.vue.d.ts +12 -6
- package/dist/components/BbNumberInput/BbNumberInput.vue.js +6 -0
- package/dist/components/BbNumberInput/BbNumberInput.vue_vue_type_script_setup_true_lang.js +194 -0
- package/dist/components/BbNumberInput/types.d.ts +40 -4
- package/dist/components/BbOffCanvas/BbOffCanvas.vue.d.ts +16 -10
- package/dist/components/BbOffCanvas/BbOffCanvas.vue.js +6 -0
- package/dist/components/BbOffCanvas/BbOffCanvas.vue_vue_type_script_setup_true_lang.js +284 -0
- package/dist/components/BbOffCanvas/types.d.ts +71 -39
- package/dist/components/BbOffCanvas/useOffCanvasStack.d.ts +25 -0
- package/dist/components/BbOffCanvas/useOffCanvasStack.js +68 -0
- package/dist/components/BbPagination/BbPagination.vue.d.ts +9 -4
- package/dist/components/BbPagination/BbPagination.vue.js +6 -0
- package/dist/components/BbPagination/BbPagination.vue_vue_type_script_setup_true_lang.js +244 -0
- package/dist/components/BbPagination/paginationFit.d.ts +38 -0
- package/dist/components/BbPagination/paginationFit.js +13 -0
- package/dist/components/BbPagination/types.d.ts +51 -16
- package/dist/components/BbPopover/BbPopover.vue.d.ts +16 -15
- package/dist/components/BbPopover/BbPopover.vue.js +6 -0
- package/dist/components/BbPopover/BbPopover.vue_vue_type_script_setup_true_lang.js +309 -0
- package/dist/components/BbPopover/escapeStack.d.ts +6 -0
- package/dist/components/BbPopover/escapeStack.js +10 -0
- package/dist/components/BbPopover/types.d.ts +90 -31
- package/dist/components/BbProgress/BbProgress.vue.d.ts +1 -0
- package/dist/components/BbProgress/BbProgress.vue.js +6 -0
- package/dist/components/BbProgress/BbProgress.vue_vue_type_script_setup_true_lang.js +36 -0
- package/dist/components/BbProgress/types.d.ts +15 -1
- package/dist/components/BbRadio/BbRadio.vue.d.ts +5 -3
- package/dist/components/BbRadio/BbRadio.vue.js +6 -0
- package/dist/components/BbRadio/BbRadio.vue_vue_type_script_setup_true_lang.js +133 -0
- package/dist/components/BbRadio/types.d.ts +32 -9
- package/dist/components/BbRadioGroup/BbRadioGroup.vue.d.ts +4 -14
- package/dist/components/BbRadioGroup/BbRadioGroup.vue.js +6 -0
- package/dist/components/BbRadioGroup/BbRadioGroup.vue_vue_type_script_setup_true_lang.js +186 -0
- package/dist/components/BbRadioGroup/types.d.ts +48 -22
- package/dist/components/BbRating/BbRating.vue.d.ts +4 -1
- package/dist/components/BbRating/BbRating.vue.js +6 -0
- package/dist/components/BbRating/BbRating.vue_vue_type_script_setup_true_lang.js +150 -0
- package/dist/components/BbRating/types.d.ts +44 -10
- package/dist/components/BbSelect/BbSelect.vue.d.ts +2 -11
- package/dist/components/BbSelect/BbSelect.vue.js +6 -0
- package/dist/components/BbSelect/BbSelect.vue_vue_type_script_setup_true_lang.js +241 -0
- package/dist/components/BbSelect/types.d.ts +112 -59
- package/dist/components/BbSelectPopover/BbSelectPopover.vue.d.ts +1 -10
- package/dist/components/BbSelectPopover/BbSelectPopover.vue.js +6 -0
- package/dist/components/BbSelectPopover/BbSelectPopover.vue_vue_type_script_setup_true_lang.js +639 -0
- package/dist/components/BbSelectPopover/types.d.ts +197 -105
- package/dist/components/BbSlider/BbSlider.vue.d.ts +3 -6
- package/dist/components/BbSlider/BbSlider.vue.js +6 -0
- package/dist/components/BbSlider/BbSlider.vue_vue_type_script_setup_true_lang.js +166 -0
- package/dist/components/BbSlider/types.d.ts +44 -13
- package/dist/components/BbSmoothHeight/BbSmoothHeight.vue.d.ts +3 -1
- package/dist/components/BbSmoothHeight/BbSmoothHeight.vue.js +6 -0
- package/dist/components/BbSmoothHeight/BbSmoothHeight.vue_vue_type_script_setup_true_lang.js +79 -0
- package/dist/components/BbSmoothHeight/types.d.ts +17 -2
- package/dist/components/BbSpinner/BbSpinner.vue.d.ts +1 -1
- package/dist/components/BbSpinner/BbSpinner.vue.js +6 -0
- package/dist/components/BbSpinner/BbSpinner.vue_vue_type_script_setup_true_lang.js +30 -0
- package/dist/components/BbSpinner/types.d.ts +10 -3
- package/dist/components/BbSwitch/BbSwitch.vue.d.ts +5 -3
- package/dist/components/BbSwitch/BbSwitch.vue.js +6 -0
- package/dist/components/BbSwitch/BbSwitch.vue_vue_type_script_setup_true_lang.js +142 -0
- package/dist/components/BbSwitch/types.d.ts +3 -1
- package/dist/components/BbSwitchGroup/BbSwitchGroup.vue.d.ts +4 -14
- package/dist/components/BbSwitchGroup/BbSwitchGroup.vue.js +6 -0
- package/dist/components/BbSwitchGroup/BbSwitchGroup.vue_vue_type_script_setup_true_lang.js +190 -0
- package/dist/components/BbSwitchGroup/types.d.ts +3 -22
- package/dist/components/BbTable/BbTable.vue.d.ts +58 -31
- package/dist/components/BbTable/BbTable.vue.js +6 -0
- package/dist/components/BbTable/BbTable.vue_vue_type_script_setup_true_lang.js +900 -0
- package/dist/components/BbTable/types.d.ts +213 -37
- package/dist/components/BbTable/utils.d.ts +9 -1
- package/dist/components/BbTable/utils.js +53 -0
- package/dist/components/BbTabs/BbTabs.vue.d.ts +20 -0
- package/dist/components/BbTabs/BbTabs.vue.js +6 -0
- package/dist/components/BbTabs/BbTabs.vue_vue_type_script_setup_true_lang.js +64 -0
- package/dist/components/BbTabs/BbTabsList.vue.d.ts +19 -0
- package/dist/components/BbTabs/BbTabsList.vue.js +6 -0
- package/dist/components/BbTabs/BbTabsList.vue_vue_type_script_setup_true_lang.js +123 -0
- package/dist/components/BbTabs/BbTabsPanes.vue.d.ts +16 -0
- package/dist/components/BbTabs/BbTabsPanes.vue.js +6 -0
- package/dist/components/BbTabs/BbTabsPanes.vue_vue_type_script_setup_true_lang.js +35 -0
- package/dist/components/BbTabs/BbTabsRoot.vue.d.ts +20 -0
- package/dist/components/BbTabs/BbTabsRoot.vue.js +5 -0
- package/dist/components/BbTabs/BbTabsRoot.vue_vue_type_script_setup_true_lang.js +55 -0
- package/dist/components/BbTabs/types.d.ts +264 -0
- package/dist/components/BbTag/BbTag.vue.d.ts +19 -6
- package/dist/components/BbTag/BbTag.vue.js +6 -0
- package/dist/components/BbTag/BbTag.vue_vue_type_script_setup_true_lang.js +187 -0
- package/dist/components/BbTag/types.d.ts +55 -17
- package/dist/components/BbTextInput/BbTextInput.vue.d.ts +10 -1
- package/dist/components/BbTextInput/BbTextInput.vue.js +6 -0
- package/dist/components/BbTextInput/BbTextInput.vue_vue_type_script_setup_true_lang.js +195 -0
- package/dist/components/BbTextInput/types.d.ts +31 -4
- package/dist/components/BbTextarea/BbTextarea.vue.d.ts +10 -1
- package/dist/components/BbTextarea/BbTextarea.vue.js +6 -0
- package/dist/components/BbTextarea/BbTextarea.vue_vue_type_script_setup_true_lang.js +178 -0
- package/dist/components/BbTextarea/types.d.ts +33 -2
- package/dist/components/BbTimePicker/BbTimePicker.vue.d.ts +33 -0
- package/dist/components/BbTimePicker/BbTimePicker.vue.js +6 -0
- package/dist/components/BbTimePicker/BbTimePicker.vue_vue_type_script_setup_true_lang.js +244 -0
- package/dist/components/BbTimePicker/types.d.ts +133 -0
- package/dist/components/BbTimePickerInput/BbTimePickerInput.vue.d.ts +25 -0
- package/dist/components/BbTimePickerInput/BbTimePickerInput.vue.js +6 -0
- package/dist/components/BbTimePickerInput/BbTimePickerInput.vue_vue_type_script_setup_true_lang.js +157 -0
- package/dist/components/BbTimePickerInput/types.d.ts +174 -0
- package/dist/components/BbToast/BbToast.vue.d.ts +2 -3
- package/dist/components/BbToast/BbToast.vue.js +6 -0
- package/dist/components/BbToast/BbToast.vue_vue_type_script_setup_true_lang.js +39 -0
- package/dist/components/BbToast/BbToastMessage.vue.d.ts +17 -8
- package/dist/components/BbToast/BbToastMessage.vue.js +5 -0
- package/dist/components/BbToast/BbToastMessage.vue_vue_type_script_setup_true_lang.js +170 -0
- package/dist/components/BbToast/BbToastPortal.vue.d.ts +30 -0
- package/dist/components/BbToast/BbToastPortal.vue.js +5 -0
- package/dist/components/BbToast/BbToastPortal.vue_vue_type_script_setup_true_lang.js +19 -0
- package/dist/components/BbToast/BbToastRegion.vue.d.ts +8 -0
- package/dist/components/BbToast/BbToastRegion.vue.js +5 -0
- package/dist/components/BbToast/BbToastRegion.vue_vue_type_script_setup_true_lang.js +100 -0
- package/dist/components/BbToast/types.d.ts +69 -5
- package/dist/components/BbTooltip/BbTooltip.vue.d.ts +10 -3
- package/dist/components/BbTooltip/BbTooltip.vue.js +6 -0
- package/dist/components/BbTooltip/BbTooltip.vue_vue_type_script_setup_true_lang.js +160 -0
- package/dist/components/BbTooltip/types.d.ts +69 -11
- package/dist/components/BbTree/BbTree.vue.d.ts +28 -13
- package/dist/components/BbTree/BbTree.vue.js +6 -0
- package/dist/components/BbTree/BbTree.vue_vue_type_script_setup_true_lang.js +55 -0
- package/dist/components/BbTree/BbTreeLevel.vue.d.ts +19 -0
- package/dist/components/BbTree/BbTreeLevel.vue.js +5 -0
- package/dist/components/BbTree/BbTreeLevel.vue_vue_type_script_setup_true_lang.js +94 -0
- package/dist/components/BbTree/context.d.ts +17 -0
- package/dist/components/BbTree/context.js +4 -0
- package/dist/components/BbTree/types.d.ts +94 -41
- package/dist/components/ChipsBox/ChipsBox.vue.d.ts +2 -1
- package/dist/components/ChipsBox/ChipsBox.vue.js +5 -0
- package/dist/components/ChipsBox/ChipsBox.vue_vue_type_script_setup_true_lang.js +98 -0
- package/dist/components/ChipsBox/types.d.ts +12 -5
- package/dist/components/ClearableButton/ClearableButton.vue.d.ts +2 -1
- package/dist/components/ClearableButton/ClearableButton.vue.js +6 -0
- package/dist/components/ClearableButton/ClearableButton.vue_vue_type_script_setup_true_lang.js +19 -0
- package/dist/components/ClearableButton/types.d.ts +7 -1
- package/dist/components/CloseIcon/CloseButton.vue.d.ts +5 -0
- package/dist/components/CloseIcon/CloseButton.vue.js +7 -0
- package/dist/components/CloseIcon/CloseButton.vue_vue_type_script_setup_true_lang.js +23 -0
- package/dist/components/CloseIcon/types.d.ts +2 -0
- package/dist/components/CommaBox/CommaBox.vue.d.ts +2 -1
- package/dist/components/CommaBox/CommaBox.vue.js +6 -0
- package/dist/components/CommaBox/CommaBox.vue_vue_type_script_setup_true_lang.js +81 -0
- package/dist/components/CommaBox/types.d.ts +6 -7
- package/dist/components/CommonInputWrapper/CommonInputWrapper.vue.d.ts +19 -0
- package/dist/components/CommonInputWrapper/CommonInputWrapper.vue.js +6 -0
- package/dist/components/CommonInputWrapper/CommonInputWrapper.vue_vue_type_script_setup_true_lang.js +107 -0
- package/dist/components/CommonInputWrapper/types.d.ts +96 -0
- package/dist/components/CommonPopover/CommonPopover.vue.d.ts +9 -3
- package/dist/components/CommonPopover/CommonPopover.vue.js +6 -0
- package/dist/components/CommonPopover/CommonPopover.vue_vue_type_script_setup_true_lang.js +160 -0
- package/dist/components/CommonPopover/types.d.ts +65 -16
- package/dist/components/ErrorIcon/ErrorIcon.vue.d.ts +2 -0
- package/dist/components/ErrorIcon/ErrorIcon.vue.js +6 -0
- package/dist/components/ErrorIcon/ErrorIcon.vue_vue_type_script_setup_true_lang.js +16 -0
- package/dist/components/FlatListBox/FlatListBox.vue.d.ts +2 -5
- package/dist/components/FlatListBox/FlatListBox.vue.js +5 -0
- package/dist/components/FlatListBox/FlatListBox.vue_vue_type_script_setup_true_lang.js +167 -0
- package/dist/components/FlatListBox/types.d.ts +1 -1
- package/dist/components/GroupedListBox/GroupedListBox.vue.d.ts +2 -5
- package/dist/components/GroupedListBox/GroupedListBox.vue.js +5 -0
- package/dist/components/GroupedListBox/GroupedListBox.vue_vue_type_script_setup_true_lang.js +218 -0
- package/dist/components/ListBox/ListBox.vue.d.ts +3 -6
- package/dist/components/ListBox/ListBox.vue.js +5 -0
- package/dist/components/ListBox/ListBox.vue_vue_type_script_setup_true_lang.js +112 -0
- package/dist/components/ListBox/types.d.ts +1 -1
- package/dist/components/OptionsContainer/OptionsContainer.vue.d.ts +1 -0
- package/dist/components/OptionsContainer/OptionsContainer.vue.js +5 -0
- package/dist/components/OptionsContainer/OptionsContainer.vue_vue_type_script_setup_true_lang.js +52 -0
- package/dist/components/OptionsContainer/types.d.ts +14 -24
- package/dist/components/Transitions/ScaleFade.vue.d.ts +2 -2
- package/dist/components/Transitions/ScaleFade.vue.js +5 -0
- package/dist/components/Transitions/ScaleFade.vue_vue_type_script_setup_true_lang.js +47 -0
- package/dist/components/Transitions/Slide.vue.d.ts +15 -0
- package/dist/components/Transitions/Slide.vue.js +7 -0
- package/dist/components/Transitions/Slide.vue_vue_type_script_setup_true_lang.js +28 -0
- package/dist/components/WarningIcon/WarningIcon.vue.d.ts +5 -0
- package/dist/components/WarningIcon/WarningIcon.vue.js +6 -0
- package/dist/components/WarningIcon/WarningIcon.vue_vue_type_script_setup_true_lang.js +16 -0
- package/dist/components/WarningIcon/types.d.ts +3 -0
- package/dist/composables/__testResets.d.ts +1 -0
- package/dist/composables/useActiveInactiveState.d.ts +4 -0
- package/dist/composables/useActiveInactiveState.js +21 -0
- package/dist/composables/useBaseOptions.d.ts +3 -3
- package/dist/composables/useBaseOptions.js +31 -0
- package/dist/composables/useBbConfig.d.ts +8 -37
- package/dist/composables/useBbConfig.js +5 -0
- package/dist/composables/useBbDropdownContext.d.ts +89 -0
- package/dist/composables/useBbDropdownContext.js +46 -0
- package/dist/composables/useBbFormContext.d.ts +90 -0
- package/dist/composables/useBbFormContext.js +69 -0
- package/dist/composables/useBbTableContext.d.ts +195 -0
- package/dist/composables/useBbTableContext.js +208 -0
- package/dist/composables/useBbTabsContext.d.ts +111 -0
- package/dist/composables/useBbTabsContext.js +225 -0
- package/dist/composables/useBreakpoint.d.ts +13 -0
- package/dist/composables/useBreakpoint.js +39 -0
- package/dist/composables/useBroadcastChannelInstance.d.ts +1 -3
- package/dist/composables/useBroadcastChannelInstance.js +39 -0
- package/dist/composables/useCancelableFunction.js +37 -0
- package/dist/composables/useClosingInert.d.ts +28 -0
- package/dist/composables/useClosingInert.js +23 -0
- package/dist/composables/useCoherence.js +16 -0
- package/dist/composables/useConfirm.d.ts +278 -68
- package/dist/composables/useConfirm.js +130 -0
- package/dist/composables/useCountdown.js +42 -0
- package/dist/composables/useHashedWatcher.d.ts +17 -0
- package/dist/composables/useHashedWatcher.js +14 -0
- package/dist/composables/useId.d.ts +3 -2
- package/dist/composables/useIndexById.js +16 -0
- package/dist/composables/useInertiaPage.d.ts +10 -0
- package/dist/composables/useInertiaPage.js +8 -0
- package/dist/composables/useItemText.js +17 -0
- package/dist/composables/useItemValue.js +9 -0
- package/dist/composables/useItemsGetter.d.ts +1 -1
- package/dist/composables/useItemsGetter.js +30 -0
- package/dist/composables/useListboxFocus.js +66 -0
- package/dist/composables/useLocale.d.ts +1 -1
- package/dist/composables/useLocale.js +11 -0
- package/dist/composables/useLogger.d.ts +1 -1
- package/dist/composables/useLogger.js +10 -0
- package/dist/composables/useMinimalQueue.d.ts +1 -1
- package/dist/composables/useMobile.js +7 -0
- package/dist/composables/useOnFalsy.d.ts +2 -0
- package/dist/composables/useOnFalsy.js +9 -0
- package/dist/composables/useOnTruthy.d.ts +7 -0
- package/dist/composables/useOnTruthy.js +9 -0
- package/dist/composables/usePrefill.d.ts +2 -1
- package/dist/composables/usePrefill.js +24 -0
- package/dist/composables/useQuery.d.ts +15 -3
- package/dist/composables/useQuery.js +42 -0
- package/dist/composables/useRoute.js +11 -0
- package/dist/composables/useRouter.js +8 -0
- package/dist/composables/useSafeArea.d.ts +51 -0
- package/dist/composables/useSafeArea.js +16 -0
- package/dist/composables/useSegmentedFields.d.ts +104 -0
- package/dist/composables/useSegmentedFields.js +159 -0
- package/dist/composables/useTableWidthContext.d.ts +5 -0
- package/dist/composables/useTableWidthContext.js +42 -0
- package/dist/composables/useToast.d.ts +40 -70
- package/dist/composables/useToast.js +58 -0
- package/dist/composables/useTopLayer.d.ts +9 -0
- package/dist/composables/useTopLayer.js +7 -0
- package/dist/composables/useUntil.js +13 -0
- package/dist/composables/useViewportWidth.d.ts +17 -0
- package/dist/composables/useViewportWidth.js +6 -0
- package/dist/confirm-variants.d.ts +17 -0
- package/dist/deprecation/ai-deprecations.json.d.ts +784 -0
- package/dist/deprecation/ai-deprecations.json.js +4 -0
- package/dist/deprecation/removedProps.d.ts +3 -0
- package/dist/deprecation/removedProps.js +13 -0
- package/dist/directives/bbColor.d.ts +65 -0
- package/dist/directives/bbColor.js +24 -0
- package/dist/directives/bbDate.d.ts +66 -0
- package/dist/directives/bbDate.js +24 -0
- package/dist/directives/bbDropdown.d.ts +15 -11
- package/dist/directives/bbDropdown.js +23 -0
- package/dist/directives/bbTime.d.ts +64 -0
- package/dist/directives/bbTime.js +24 -0
- package/dist/directives/bbTooltip.d.ts +16 -11
- package/dist/directives/bbTooltip.js +24 -0
- package/dist/directives/createPopoverDirective.js +54 -0
- package/dist/dropdown-item-variants.d.ts +17 -0
- package/dist/i18n/index.d.ts +51 -3
- package/dist/i18n/index.js +27 -0
- package/dist/i18n/locales/cs.json +115 -0
- package/dist/i18n/locales/da.json +115 -0
- package/dist/i18n/locales/de.json +115 -0
- package/dist/i18n/locales/el.json +115 -0
- package/dist/i18n/locales/en.json +115 -0
- package/dist/i18n/locales/es.json +115 -0
- package/dist/i18n/locales/fi.json +115 -0
- package/dist/i18n/locales/fr.json +115 -0
- package/dist/i18n/locales/hu.json +115 -0
- package/dist/i18n/locales/it.json +115 -0
- package/dist/i18n/locales/ja.json +115 -0
- package/dist/i18n/locales/ko.json +115 -0
- package/dist/i18n/locales/nb.json +115 -0
- package/dist/i18n/locales/nl.json +115 -0
- package/dist/i18n/locales/pl.json +115 -0
- package/dist/i18n/locales/pt.json +115 -0
- package/dist/i18n/locales/ro.json +115 -0
- package/dist/i18n/locales/ru.json +115 -0
- package/dist/i18n/locales/sv.json +115 -0
- package/dist/i18n/locales/tr.json +115 -0
- package/dist/i18n/locales/uk.json +115 -0
- package/dist/i18n/locales/zh-cn.json +115 -0
- package/dist/icons-whitelist.d.ts +8 -0
- package/dist/index.d.ts +96 -79
- package/dist/index.js +76 -82
- package/dist/indicator-variants.d.ts +20 -0
- package/dist/llms-full.txt +79703 -0
- package/dist/llms-medium.txt +1514 -0
- package/dist/llms.txt +105 -0
- package/dist/locale-blueprint.json +141 -0
- package/dist/locale-registry.d.ts +37 -0
- package/dist/nuxt-module.d.ts +3 -0
- package/dist/nuxt.js +19 -0
- package/dist/plugin.d.ts +4 -0
- package/dist/plugin.js +22 -0
- package/dist/reset.css +1 -0
- package/dist/reset.d.ts +0 -0
- package/dist/runtime/nuxt-plugin.d.ts +2 -0
- package/dist/runtime/nuxt-plugin.js +24 -0
- package/dist/styles.css +2 -0
- package/dist/styles.d.ts +0 -0
- package/dist/toast-variants.d.ts +20 -0
- package/dist/tooltip-variants.d.ts +17 -0
- package/dist/types/ActionButton.d.ts +32 -0
- package/dist/types/AlertVariant.d.ts +16 -0
- package/dist/types/BadgeVariant.d.ts +16 -0
- package/dist/types/ButtonVariant.d.ts +17 -0
- package/dist/types/Classes.d.ts +1 -1
- package/dist/types/ColumnName.d.ts +1 -1
- package/dist/types/CommonProps.d.ts +68 -277
- package/dist/types/Config.d.ts +84 -0
- package/dist/types/ConfirmVariant.d.ts +15 -0
- package/dist/types/DropdownItemVariant.d.ts +18 -0
- package/dist/types/Icon.d.ts +5 -7
- package/dist/types/IndicatorVariant.d.ts +16 -0
- package/dist/types/InputDirection.d.ts +15 -0
- package/dist/types/ItemAccessor.d.ts +21 -0
- package/dist/types/Placement.d.ts +6 -0
- package/dist/types/ResponsiveSize.d.ts +20 -0
- package/dist/types/SizeScale.d.ts +8 -0
- package/dist/types/ToastVariant.d.ts +17 -0
- package/dist/types/TooltipVariant.d.ts +18 -0
- package/dist/utilities/functions/bytesToUnit.d.ts +2 -0
- package/dist/utilities/functions/bytesToUnit.js +18 -0
- package/dist/utilities/functions/clampPrecision.js +10 -0
- package/dist/utilities/functions/convertDateValue.d.ts +23 -10
- package/dist/utilities/functions/convertDateValue.js +12 -0
- package/dist/utilities/functions/deburr.js +5 -0
- package/dist/utilities/functions/empty.js +5 -0
- package/dist/utilities/functions/flattenTree.d.ts +1 -1
- package/dist/utilities/functions/hash.js +5 -0
- package/dist/utilities/functions/isDateDisabled.d.ts +1 -0
- package/dist/utilities/functions/isDateDisabled.js +20 -0
- package/dist/utilities/functions/isEqual.js +5 -0
- package/dist/utilities/functions/isNotEmpty.js +5 -0
- package/dist/utilities/functions/matchAnyKey.d.ts +1 -1
- package/dist/utilities/functions/matchAnyKey.js +15 -0
- package/dist/utilities/functions/parseSize.d.ts +1 -1
- package/dist/utilities/functions/parseSize.js +15 -0
- package/dist/utilities/functions/percentage.js +11 -0
- package/dist/utilities/functions/pipe.d.ts +1 -1
- package/dist/utilities/functions/reject.d.ts +25 -0
- package/dist/utilities/functions/reject.js +28 -0
- package/dist/utilities/functions/resolveEnumProp.d.ts +19 -0
- package/dist/utilities/functions/resolveEnumProp.js +5 -0
- package/dist/utilities/functions/slotKey.d.ts +13 -0
- package/dist/utilities/functions/slotKey.js +4 -0
- package/dist/utilities/functions/sort.d.ts +2 -2
- package/dist/utilities/functions/sort.js +19 -0
- package/dist/utilities/functions/throttle.js +5 -0
- package/dist/utilities/functions/waitFor.d.ts +9 -2
- package/dist/utilities/functions/waitFor.js +14 -0
- package/dist/utilities/functions/warnRemovedAttrs.d.ts +11 -0
- package/dist/utilities/functions/warnRemovedAttrs.js +20 -0
- package/dist/utilities/functions/when.js +5 -0
- package/dist/utilities/misc/iconsInjectionKey.d.ts +15 -0
- package/dist/utilities/misc/iconsInjectionKey.js +4 -0
- package/dist/utilities/misc/transitions.d.ts +5 -0
- package/dist/utilities/misc/transitions.js +8 -0
- package/dist/utils/injectLibraryStyles.d.ts +6 -0
- package/dist/utils/injectLibraryStyles.js +38 -0
- package/dist/validated/BbCheckbox.vue.d.ts +29 -0
- package/dist/validated/BbCheckbox.vue.js +5 -0
- package/dist/validated/BbCheckbox.vue_vue_type_script_setup_true_lang.js +75 -0
- package/dist/validated/BbCheckboxGroup.vue.d.ts +34 -0
- package/dist/validated/BbCheckboxGroup.vue.js +5 -0
- package/dist/validated/BbCheckboxGroup.vue_vue_type_script_setup_true_lang.js +82 -0
- package/dist/validated/BbColorInput.vue.d.ts +36 -0
- package/dist/validated/BbColorInput.vue.js +5 -0
- package/dist/validated/BbColorInput.vue_vue_type_script_setup_true_lang.js +71 -0
- package/dist/validated/BbDatePickerInput.vue.d.ts +41 -0
- package/dist/validated/BbDatePickerInput.vue.js +5 -0
- package/dist/validated/BbDatePickerInput.vue_vue_type_script_setup_true_lang.js +90 -0
- package/dist/validated/BbDropzone.vue.d.ts +24 -0
- package/dist/validated/BbDropzone.vue.js +5 -0
- package/dist/validated/BbDropzone.vue_vue_type_script_setup_true_lang.js +45 -0
- package/dist/validated/BbForm.vue.d.ts +28 -0
- package/dist/validated/BbForm.vue.js +5 -0
- package/dist/validated/BbForm.vue_vue_type_script_setup_true_lang.js +42 -0
- package/dist/validated/BbNumberInput.vue.d.ts +36 -0
- package/dist/validated/BbNumberInput.vue.js +5 -0
- package/dist/validated/BbNumberInput.vue_vue_type_script_setup_true_lang.js +72 -0
- package/dist/validated/BbRadioGroup.vue.d.ts +34 -0
- package/dist/validated/BbRadioGroup.vue.js +5 -0
- package/dist/validated/BbRadioGroup.vue_vue_type_script_setup_true_lang.js +78 -0
- package/dist/validated/BbRating.vue.d.ts +30 -0
- package/dist/validated/BbRating.vue.js +5 -0
- package/dist/validated/BbRating.vue_vue_type_script_setup_true_lang.js +63 -0
- package/dist/validated/BbSelect.vue.d.ts +46 -0
- package/dist/validated/BbSelect.vue.js +5 -0
- package/dist/validated/BbSelect.vue_vue_type_script_setup_true_lang.js +109 -0
- package/dist/validated/BbSlider.vue.d.ts +36 -0
- package/dist/validated/BbSlider.vue.js +5 -0
- package/dist/validated/BbSlider.vue_vue_type_script_setup_true_lang.js +69 -0
- package/dist/validated/BbSwitch.vue.d.ts +29 -0
- package/dist/validated/BbSwitch.vue.js +5 -0
- package/dist/validated/BbSwitch.vue_vue_type_script_setup_true_lang.js +75 -0
- package/dist/validated/BbSwitchGroup.vue.d.ts +34 -0
- package/dist/validated/BbSwitchGroup.vue.js +5 -0
- package/dist/validated/BbSwitchGroup.vue_vue_type_script_setup_true_lang.js +82 -0
- package/dist/validated/BbTag.vue.d.ts +36 -0
- package/dist/validated/BbTag.vue.js +5 -0
- package/dist/validated/BbTag.vue_vue_type_script_setup_true_lang.js +72 -0
- package/dist/validated/BbTextInput.vue.d.ts +36 -0
- package/dist/validated/BbTextInput.vue.js +5 -0
- package/dist/validated/BbTextInput.vue_vue_type_script_setup_true_lang.js +72 -0
- package/dist/validated/BbTextarea.vue.d.ts +36 -0
- package/dist/validated/BbTextarea.vue.js +5 -0
- package/dist/validated/BbTextarea.vue_vue_type_script_setup_true_lang.js +70 -0
- package/dist/validated/BbTimePickerInput.vue.d.ts +37 -0
- package/dist/validated/BbTimePickerInput.vue.js +5 -0
- package/dist/validated/BbTimePickerInput.vue_vue_type_script_setup_true_lang.js +81 -0
- package/dist/validated/index.d.ts +29 -0
- package/dist/validated/useValidatedField.d.ts +93 -0
- package/dist/validated/useValidatedField.js +51 -0
- package/dist/validated.js +21 -0
- package/dist/vite-plugin.d.ts +261 -0
- package/dist/vite.js +603 -0
- package/llms.txt +107 -0
- package/package.json +181 -60
- package/scripts/lib/__fixtures__/validated-manifest.json +65 -0
- package/scripts/lib/ensure-manifest.mjs +39 -0
- package/scripts/lib/eslint-plugin-autofix.node.test.mjs +109 -0
- package/scripts/lib/eslint-plugin-recommended.node.test.mjs +87 -0
- package/scripts/lib/eslint-plugin-slots.node.test.mjs +271 -0
- package/scripts/lib/eslint-plugin.mjs +702 -0
- package/scripts/lib/eslint-plugin.node.test.mjs +193 -0
- package/scripts/lib/html-attributes.mjs +191 -0
- package/scripts/lib/mcp-config.mjs +126 -0
- package/scripts/lib/migrate-fix.mjs +115 -0
- package/scripts/lib/validate-bb-markup.mjs +488 -0
- package/dist/ai/BaseButton.md +0 -449
- package/dist/ai/BaseCheckbox.md +0 -512
- package/dist/ai/BaseCheckboxGroup.md +0 -597
- package/dist/ai/BaseColorInput.md +0 -461
- package/dist/ai/BaseDatePicker.md +0 -739
- package/dist/ai/BaseDatePickerInput.md +0 -1517
- package/dist/ai/BaseDialog.md +0 -610
- package/dist/ai/BaseInputContainer.md +0 -570
- package/dist/ai/BaseNumberInput.md +0 -509
- package/dist/ai/BaseRadio.md +0 -405
- package/dist/ai/BaseRadioGroup.md +0 -535
- package/dist/ai/BaseRating.md +0 -489
- package/dist/ai/BaseSelect.md +0 -1742
- package/dist/ai/BaseSlider.md +0 -871
- package/dist/ai/BaseSwitch.md +0 -322
- package/dist/ai/BaseSwitchGroup.md +0 -298
- package/dist/ai/BaseTag.md +0 -623
- package/dist/ai/BaseTextInput.md +0 -392
- package/dist/ai/BaseTextarea.md +0 -398
- package/dist/ai/BbChip.md +0 -147
- package/dist/ai/BbIntersection.md +0 -120
- package/dist/ai/BbRatio.md +0 -61
- package/dist/ai/BbRows.md +0 -307
- package/dist/ai/BbTab.md +0 -950
- package/dist/ai/CommonInputInnerContainer.md +0 -414
- package/dist/ai/CommonInputOuterContainer.md +0 -55
- package/dist/components/BaseButton/BaseButton.vue.d.ts +0 -14
- package/dist/components/BaseButton/types.d.ts +0 -159
- package/dist/components/BaseCheckbox/BaseCheckbox.vue.d.ts +0 -39
- package/dist/components/BaseCheckbox/types.d.ts +0 -213
- package/dist/components/BaseCheckboxGroup/BaseCheckboxGroup.vue.d.ts +0 -30
- package/dist/components/BaseCheckboxGroup/types.d.ts +0 -282
- package/dist/components/BaseColorInput/BaseColorInput.vue.d.ts +0 -35
- package/dist/components/BaseColorInput/ColorPalette.vue.d.ts +0 -13
- package/dist/components/BaseColorInput/types.d.ts +0 -233
- package/dist/components/BaseDatePicker/BaseDatePickerHeader.vue.d.ts +0 -15
- package/dist/components/BaseDatePicker/BaseDatePickerInputDaySelector.vue.d.ts +0 -58
- package/dist/components/BaseDatePicker/BaseDatePickerMonthSelector.vue.d.ts +0 -12
- package/dist/components/BaseDatePicker/BaseDatePickerYearSelector.vue.d.ts +0 -12
- package/dist/components/BaseDatePicker/types.d.ts +0 -100
- package/dist/components/BaseDatePickerInput/BaseDatePickerInput.vue.d.ts +0 -26
- package/dist/components/BaseDatePickerInput/types.d.ts +0 -206
- package/dist/components/BaseDialog/BaseDialog.vue.d.ts +0 -37
- package/dist/components/BaseDialog/types.d.ts +0 -180
- package/dist/components/BaseInputContainer/BaseInputContainer.vue.d.ts +0 -18
- package/dist/components/BaseInputContainer/types.d.ts +0 -126
- package/dist/components/BaseNumberInput/BaseNumberInput.vue.d.ts +0 -46
- package/dist/components/BaseNumberInput/types.d.ts +0 -191
- package/dist/components/BaseRadio/BaseRadio.vue.d.ts +0 -31
- package/dist/components/BaseRadio/types.d.ts +0 -173
- package/dist/components/BaseRadioGroup/BaseRadioGroup.vue.d.ts +0 -30
- package/dist/components/BaseRadioGroup/types.d.ts +0 -240
- package/dist/components/BaseRating/BaseRating.vue.d.ts +0 -35
- package/dist/components/BaseRating/types.d.ts +0 -144
- package/dist/components/BaseSelect/BaseSelect.vue.d.ts +0 -28
- package/dist/components/BaseSelect/types.d.ts +0 -470
- package/dist/components/BaseSlider/BaseSlider.vue.d.ts +0 -38
- package/dist/components/BaseSlider/types.d.ts +0 -201
- package/dist/components/BaseSwitch/BaseSwitch.vue.d.ts +0 -34
- package/dist/components/BaseSwitch/types.d.ts +0 -25
- package/dist/components/BaseSwitchGroup/BaseSwitchGroup.vue.d.ts +0 -30
- package/dist/components/BaseSwitchGroup/types.d.ts +0 -8
- package/dist/components/BaseTag/BaseTag.vue.d.ts +0 -37
- package/dist/components/BaseTag/types.d.ts +0 -136
- package/dist/components/BaseTextInput/BaseTextInput.vue.d.ts +0 -33
- package/dist/components/BaseTextInput/types.d.ts +0 -166
- package/dist/components/BaseTextarea/BaseTextarea.vue.d.ts +0 -31
- package/dist/components/BaseTextarea/types.d.ts +0 -151
- package/dist/components/BbChip/BbChip.vue.d.ts +0 -17
- package/dist/components/BbChip/types.d.ts +0 -23
- package/dist/components/BbIntersection/BbIntersection.vue.d.ts +0 -23
- package/dist/components/BbIntersection/types.d.ts +0 -36
- package/dist/components/BbRatio/BbRatio.vue.d.ts +0 -14
- package/dist/components/BbRatio/types.d.ts +0 -15
- package/dist/components/BbRows/BbRows.vue.d.ts +0 -62
- package/dist/components/BbRows/types.d.ts +0 -21
- package/dist/components/BbTab/BbTab.vue.d.ts +0 -27
- package/dist/components/BbTab/types.d.ts +0 -186
- package/dist/components/CommonInputInnerContainer/CommonInputInnerContainer.vue.d.ts +0 -15
- package/dist/components/CommonInputInnerContainer/types.d.ts +0 -47
- package/dist/components/CommonInputOuterContainer/CommonInputOuterContainer.vue.d.ts +0 -11
- package/dist/components/CommonInputOuterContainer/types.d.ts +0 -16
- package/dist/composables/useModalsState.d.ts +0 -5
- package/dist/composables/useQueue.d.ts +0 -58
- package/dist/composables/useWizard.d.ts +0 -85
- package/dist/i18n/en.d.ts +0 -67
- package/dist/i18n/it.d.ts +0 -67
- package/dist/index.css +0 -2
- package/dist/index100.js +0 -6
- package/dist/index101.js +0 -300
- package/dist/index102.js +0 -16
- package/dist/index103.js +0 -16
- package/dist/index104.js +0 -27
- package/dist/index109.js +0 -19
- package/dist/index11.js +0 -42
- package/dist/index110.js +0 -12
- package/dist/index111.js +0 -19
- package/dist/index112.js +0 -25
- package/dist/index114.js +0 -7
- package/dist/index115.js +0 -241
- package/dist/index116.js +0 -5
- package/dist/index117.js +0 -42
- package/dist/index118.js +0 -5
- package/dist/index119.js +0 -53
- package/dist/index120.js +0 -5
- package/dist/index121.js +0 -32
- package/dist/index122.js +0 -5
- package/dist/index124.js +0 -52
- package/dist/index125.js +0 -5
- package/dist/index126.js +0 -223
- package/dist/index128.js +0 -6
- package/dist/index129.js +0 -162
- package/dist/index13.js +0 -92
- package/dist/index130.js +0 -25
- package/dist/index131.js +0 -51
- package/dist/index132.js +0 -5
- package/dist/index133.js +0 -607
- package/dist/index135.js +0 -6
- package/dist/index136.js +0 -14
- package/dist/index137.js +0 -153
- package/dist/index139.js +0 -6
- package/dist/index14.js +0 -306
- package/dist/index140.js +0 -224
- package/dist/index141.js +0 -5
- package/dist/index143.js +0 -128
- package/dist/index145.js +0 -6
- package/dist/index146.js +0 -8
- package/dist/index147.js +0 -182
- package/dist/index149.js +0 -6
- package/dist/index15.js +0 -10
- package/dist/index151.js +0 -4
- package/dist/index152.js +0 -95
- package/dist/index154.js +0 -6
- package/dist/index155.js +0 -168
- package/dist/index157.js +0 -6
- package/dist/index158.js +0 -145
- package/dist/index16.js +0 -59
- package/dist/index160.js +0 -6
- package/dist/index161.js +0 -53
- package/dist/index163.js +0 -6
- package/dist/index164.js +0 -16
- package/dist/index166.js +0 -35
- package/dist/index168.js +0 -6
- package/dist/index169.js +0 -89
- package/dist/index171.js +0 -6
- package/dist/index172.js +0 -82
- package/dist/index174.js +0 -6
- package/dist/index175.js +0 -38
- package/dist/index176.js +0 -461
- package/dist/index177.js +0 -28
- package/dist/index178.js +0 -66
- package/dist/index18.js +0 -35
- package/dist/index180.js +0 -162
- package/dist/index181.js +0 -5
- package/dist/index182.js +0 -217
- package/dist/index183.js +0 -5
- package/dist/index184.js +0 -112
- package/dist/index185.js +0 -5
- package/dist/index186.js +0 -459
- package/dist/index188.js +0 -6
- package/dist/index19.js +0 -4
- package/dist/index192.js +0 -268
- package/dist/index194.js +0 -6
- package/dist/index195.js +0 -124
- package/dist/index197.js +0 -6
- package/dist/index198.js +0 -174
- package/dist/index2.js +0 -20
- package/dist/index20.js +0 -9
- package/dist/index200.js +0 -6
- package/dist/index201.js +0 -224
- package/dist/index203.js +0 -6
- package/dist/index204.js +0 -135
- package/dist/index206.js +0 -6
- package/dist/index207.js +0 -5
- package/dist/index208.js +0 -170
- package/dist/index21.js +0 -35
- package/dist/index210.js +0 -6
- package/dist/index211.js +0 -37
- package/dist/index213.js +0 -6
- package/dist/index214.js +0 -63
- package/dist/index216.js +0 -6
- package/dist/index217.js +0 -74
- package/dist/index219.js +0 -6
- package/dist/index22.js +0 -6
- package/dist/index220.js +0 -65
- package/dist/index222.js +0 -6
- package/dist/index223.js +0 -67
- package/dist/index225.js +0 -6
- package/dist/index226.js +0 -41
- package/dist/index228.js +0 -6
- package/dist/index229.js +0 -10
- package/dist/index23.js +0 -7
- package/dist/index230.js +0 -22
- package/dist/index232.js +0 -8
- package/dist/index233.js +0 -297
- package/dist/index235.js +0 -6
- package/dist/index236.js +0 -47
- package/dist/index237.js +0 -165
- package/dist/index239.js +0 -6
- package/dist/index24.js +0 -9
- package/dist/index241.js +0 -159
- package/dist/index243.js +0 -6
- package/dist/index244.js +0 -134
- package/dist/index246.js +0 -6
- package/dist/index247.js +0 -167
- package/dist/index249.js +0 -6
- package/dist/index25.js +0 -24
- package/dist/index250.js +0 -147
- package/dist/index252.js +0 -6
- package/dist/index253.js +0 -35
- package/dist/index255.js +0 -6
- package/dist/index256.js +0 -65
- package/dist/index258.js +0 -6
- package/dist/index259.js +0 -19
- package/dist/index260.js +0 -5
- package/dist/index261.js +0 -160
- package/dist/index263.js +0 -6
- package/dist/index264.js +0 -60
- package/dist/index266.js +0 -6
- package/dist/index267.js +0 -134
- package/dist/index269.js +0 -6
- package/dist/index27.js +0 -4
- package/dist/index271.js +0 -206
- package/dist/index273.js +0 -6
- package/dist/index274.js +0 -85
- package/dist/index275.js +0 -5
- package/dist/index276.js +0 -55
- package/dist/index277.js +0 -5
- package/dist/index278.js +0 -164
- package/dist/index28.js +0 -8
- package/dist/index280.js +0 -6
- package/dist/index281.js +0 -145
- package/dist/index283.js +0 -6
- package/dist/index284.js +0 -11
- package/dist/index285.js +0 -200
- package/dist/index287.js +0 -6
- package/dist/index288.js +0 -241
- package/dist/index29.js +0 -7
- package/dist/index290.js +0 -6
- package/dist/index291.js +0 -21
- package/dist/index293.js +0 -6
- package/dist/index294.js +0 -125
- package/dist/index296.js +0 -6
- package/dist/index297.js +0 -161
- package/dist/index299.js +0 -6
- package/dist/index30.js +0 -118
- package/dist/index300.js +0 -127
- package/dist/index302.js +0 -6
- package/dist/index303.js +0 -20
- package/dist/index305.js +0 -6
- package/dist/index306.js +0 -116
- package/dist/index307.js +0 -5
- package/dist/index308.js +0 -507
- package/dist/index31.js +0 -10
- package/dist/index310.js +0 -6
- package/dist/index311.js +0 -196
- package/dist/index313.js +0 -6
- package/dist/index314.js +0 -142
- package/dist/index316.js +0 -6
- package/dist/index317.js +0 -134
- package/dist/index319.js +0 -6
- package/dist/index32.js +0 -28
- package/dist/index320.js +0 -162
- package/dist/index322.js +0 -6
- package/dist/index323.js +0 -319
- package/dist/index325.js +0 -6
- package/dist/index326.js +0 -40
- package/dist/index327.js +0 -43
- package/dist/index328.js +0 -665
- package/dist/index33.js +0 -5
- package/dist/index330.js +0 -6
- package/dist/index331.js +0 -155
- package/dist/index333.js +0 -6
- package/dist/index334.js +0 -142
- package/dist/index336.js +0 -6
- package/dist/index337.js +0 -155
- package/dist/index339.js +0 -6
- package/dist/index34.js +0 -112
- package/dist/index340.js +0 -58
- package/dist/index341.js +0 -5
- package/dist/index342.js +0 -20
- package/dist/index344.js +0 -6
- package/dist/index345.js +0 -142
- package/dist/index347.js +0 -6
- package/dist/index349.js +0 -109
- package/dist/index351.js +0 -6
- package/dist/index352.js +0 -55
- package/dist/index353.js +0 -24
- package/dist/index354.js +0 -23
- package/dist/index355.js +0 -839
- package/dist/index356.js +0 -474
- package/dist/index357.js +0 -52
- package/dist/index358.js +0 -7
- package/dist/index36.js +0 -6
- package/dist/index38.js +0 -63
- package/dist/index39.js +0 -63
- package/dist/index40.js +0 -9
- package/dist/index42.js +0 -8
- package/dist/index43.js +0 -179
- package/dist/index44.js +0 -21
- package/dist/index46.js +0 -385
- package/dist/index48.js +0 -6
- package/dist/index49.js +0 -33
- package/dist/index5.js +0 -34
- package/dist/index50.js +0 -12
- package/dist/index51.js +0 -8
- package/dist/index52.js +0 -106
- package/dist/index53.js +0 -330
- package/dist/index54.js +0 -97
- package/dist/index55.js +0 -328
- package/dist/index56.js +0 -28
- package/dist/index57.js +0 -102
- package/dist/index58.js +0 -157
- package/dist/index6.js +0 -38
- package/dist/index60.js +0 -6
- package/dist/index61.js +0 -131
- package/dist/index62.js +0 -5
- package/dist/index65.js +0 -35
- package/dist/index66.js +0 -26
- package/dist/index68.js +0 -6
- package/dist/index69.js +0 -54
- package/dist/index7.js +0 -58
- package/dist/index71.js +0 -6
- package/dist/index72.js +0 -14
- package/dist/index74.js +0 -6
- package/dist/index75.js +0 -159
- package/dist/index77.js +0 -6
- package/dist/index78.js +0 -4
- package/dist/index79.js +0 -4
- package/dist/index80.js +0 -54
- package/dist/index81.js +0 -5
- package/dist/index82.js +0 -5
- package/dist/index83.js +0 -141
- package/dist/index85.js +0 -6
- package/dist/index86.js +0 -65
- package/dist/index88.js +0 -6
- package/dist/index89.js +0 -30
- package/dist/index9.js +0 -11
- package/dist/index90.js +0 -19
- package/dist/index91.js +0 -17
- package/dist/index92.js +0 -9
- package/dist/index93.js +0 -34
- package/dist/index95.js +0 -16
- package/dist/index96.js +0 -10
- package/dist/index97.js +0 -18
- package/dist/index98.js +0 -189
- package/dist/types/VoidHtmlElements.d.ts +0 -1
- package/dist/utilities/functions/add.d.ts +0 -1
- package/dist/utilities/functions/booleanishToBoolean.d.ts +0 -6
- package/dist/utilities/functions/capitalize.d.ts +0 -4
- package/dist/utilities/functions/cloneDeep.d.ts +0 -1
- package/dist/utilities/functions/debounce.d.ts +0 -1
- package/dist/utilities/functions/drop.d.ts +0 -1
- package/dist/utilities/functions/exceedsPrecision.d.ts +0 -1
- package/dist/utilities/functions/findRight.d.ts +0 -4
- package/dist/utilities/functions/findRightIndex.d.ts +0 -4
- package/dist/utilities/functions/flow.d.ts +0 -1
- package/dist/utilities/functions/getHueFromRGB.d.ts +0 -9
- package/dist/utilities/functions/groupBy.d.ts +0 -6
- package/dist/utilities/functions/gt.d.ts +0 -7
- package/dist/utilities/functions/hexToRGB.d.ts +0 -7
- package/dist/utilities/functions/hslToHex.d.ts +0 -1
- package/dist/utilities/functions/includes.d.ts +0 -1
- package/dist/utilities/functions/initial.d.ts +0 -1
- package/dist/utilities/functions/isCssColor.d.ts +0 -5
- package/dist/utilities/functions/lt.d.ts +0 -7
- package/dist/utilities/functions/makeUrl.d.ts +0 -1
- package/dist/utilities/functions/mapKeys.d.ts +0 -5
- package/dist/utilities/functions/mapValues.d.ts +0 -1
- package/dist/utilities/functions/matchesPropertyInsensitive.d.ts +0 -1
- package/dist/utilities/functions/max.d.ts +0 -2
- package/dist/utilities/functions/mean.d.ts +0 -4
- package/dist/utilities/functions/merge.d.ts +0 -11
- package/dist/utilities/functions/millisecondsTo.d.ts +0 -1
- package/dist/utilities/functions/negate.d.ts +0 -4
- package/dist/utilities/functions/normalizeItemGroups.d.ts +0 -13
- package/dist/utilities/functions/omit.d.ts +0 -2
- package/dist/utilities/functions/overEvery.d.ts +0 -4
- package/dist/utilities/functions/pick.d.ts +0 -1
- package/dist/utilities/functions/replace.d.ts +0 -1
- package/dist/utilities/functions/replaceInTree.d.ts +0 -4
- package/dist/utilities/functions/retry.d.ts +0 -5
- package/dist/utilities/functions/roundToTheNearest.d.ts +0 -8
- package/dist/utilities/functions/set.d.ts +0 -15
- package/dist/utilities/functions/stub.d.ts +0 -6
- package/dist/utilities/functions/transposePercent.d.ts +0 -9
- package/dist/utilities/functions/tryTo.d.ts +0 -2
- package/dist/utilities/functions/uniqBy.d.ts +0 -1
- /package/dist/{index64.js → _virtual/_plugin-vue_export-helper.js} +0 -0
- /package/dist/components/{BaseButton → BbBaseButton}/RouterComponent.vue.d.ts +0 -0
- /package/dist/components/{BaseDatePicker/BaseDatePicker.vue.d.ts → BbBaseDatePicker/BbBaseDatePicker.vue.d.ts} +0 -0
- /package/dist/{index165.js → composables/useArray.js} +0 -0
- /package/dist/{index17.js → composables/useFrameworkDetection.js} +0 -0
- /package/dist/{index12.js → composables/useId.js} +0 -0
- /package/dist/{index107.js → utilities/functions/chunk.js} +0 -0
- /package/dist/{index8.js → utilities/functions/clamp.js} +0 -0
- /package/dist/{index150.js → utilities/functions/curry.js} +0 -0
- /package/dist/{index45.js → utilities/functions/extractDomContainer.js} +0 -0
- /package/dist/{index270.js → utilities/functions/findInTree.js} +0 -0
- /package/dist/{index348.js → utilities/functions/flattenTree.js} +0 -0
- /package/dist/{index41.js → utilities/functions/get.js} +0 -0
- /package/dist/{index189.js → utilities/functions/head.js} +0 -0
- /package/dist/{index108.js → utilities/functions/identity.js} +0 -0
- /package/dist/{index179.js → utilities/functions/indexBy.js} +0 -0
- /package/dist/{index37.js → utilities/functions/isHex.js} +0 -0
- /package/dist/{index94.js → utilities/functions/isNil.js} +0 -0
- /package/dist/{index26.js → utilities/functions/isNotNil.js} +0 -0
- /package/dist/{index105.js → utilities/functions/last.js} +0 -0
- /package/dist/{index142.js → utilities/functions/noop.js} +0 -0
- /package/dist/{index231.js → utilities/functions/parseWidthString.js} +0 -0
- /package/dist/{index240.js → utilities/functions/pickBy.js} +0 -0
- /package/dist/{index123.js → utilities/functions/range.js} +0 -0
- /package/dist/{index190.js → utilities/functions/snapToStep.js} +0 -0
- /package/dist/{index191.js → utilities/functions/transpose.js} +0 -0
- /package/dist/{index106.js → utilities/functions/validateDateFormat.js} +0 -0
- /package/dist/{index10.js → utilities/functions/wait.js} +0 -0
|
@@ -0,0 +1,1489 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Record Form (Inertia)
|
|
3
|
+
summary: Full-anatomy invoice create/edit form for Laravel + Inertia — sectioned fields with tied selects, usePromiseForm + form.errors for 422 mapping, an inline-editing line-items table, a useConfirmOnRouteLeave dirty guard, a sticky action row, and a customer peek sidebar, all through one coopting page context.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Recipe: Record Form (Inertia)
|
|
7
|
+
|
|
8
|
+
The form archetype at full standard anatomy adapted for **Laravel +
|
|
9
|
+
Inertia**: the same Stripe-grade invoice create/edit page — sectioned
|
|
10
|
+
fields with tied selects, an inline-editing line-items table, a
|
|
11
|
+
dirty-guarded sticky action row, and a customer peek workbench — but the
|
|
12
|
+
draft **is** [`usePromiseForm`](../../guides/inertia-helpers.md) state, so
|
|
13
|
+
422 mapping is free on `form.errors`, dirty tracking is native
|
|
14
|
+
`form.isDirty`, saves are promise-wrapped `form.post`/`form.put` visits, and
|
|
15
|
+
the leave guard is the toolkit's
|
|
16
|
+
[`useConfirmOnRouteLeave`](../../guides/inertia-helpers.md). Anatomy,
|
|
17
|
+
components, the context shape, copy and data are identical to the Vue
|
|
18
|
+
recipe; only the data/URL/mutation layer changes.
|
|
19
|
+
|
|
20
|
+
Sibling recipes: [Vue](../vue/record-form.md) ·
|
|
21
|
+
[Nuxt](../nuxt/record-form.md). The listing this page belongs with is
|
|
22
|
+
[Records Workspace (Inertia)](./records-workspace.md).
|
|
23
|
+
|
|
24
|
+
## Prerequisites
|
|
25
|
+
|
|
26
|
+
- **[Layout Scaffold](./layout-scaffold.md) — build it first.** This recipe
|
|
27
|
+
imports `PageShell` and `usePageShellContext` from your own app.
|
|
28
|
+
They are not package exports; the scaffold recipe is their source. Copy
|
|
29
|
+
those files before anything below, and do not hand-roll replacements —
|
|
30
|
+
the peek stack, the close semantics and the sidebar's out-of-flow
|
|
31
|
+
behaviour are all load-bearing here.
|
|
32
|
+
- `<BbConfirm />` and `<BbToast />` mounted once in the `createInertiaApp` render root — never in a page.
|
|
33
|
+
- From the [Inertia Helpers](../../guides/inertia-helpers.md) toolkit:
|
|
34
|
+
`usePromiseForm`, `useConfirmOnRouteLeave`, `useProp`.
|
|
35
|
+
- Controller: `Inertia::render('Invoices/Edit', ['invoice' => $invoice])`
|
|
36
|
+
— `invoice` is `null` on the create route and the full record (including
|
|
37
|
+
`lineItems`) on the edit route. Standard Laravel validation; on failure
|
|
38
|
+
Inertia puts messages on `form.errors`. Mutation routes are plain Laravel
|
|
39
|
+
routes (`POST /invoices`, `PUT /invoices/:id`, `DELETE /invoices/:id`)
|
|
40
|
+
that redirect back; small JSON GET endpoints (`GET /customers/search`,
|
|
41
|
+
`GET /customers/:id/projects`, `GET /customers/:id/peek`) back the
|
|
42
|
+
customer/project selects and the sidebar peek.
|
|
43
|
+
|
|
44
|
+
## Key ideas
|
|
45
|
+
|
|
46
|
+
- **One coopting page context.** `provideInvoiceForm()` joins the
|
|
47
|
+
page-shell stack once, owns the draft, the server errors, the dirty
|
|
48
|
+
state and every action (including the route-leave guard). Every leaf
|
|
49
|
+
calls `useInvoiceForm()` and nothing else — cells, sections, the sticky
|
|
50
|
+
bar and the peek share state with zero prop drilling.
|
|
51
|
+
- **The draft _is_ the Inertia form.** `ctx.draft` is `usePromiseForm`'s own
|
|
52
|
+
reactive fields, so inline cell edits, the peek's "apply billing
|
|
53
|
+
defaults", and add/duplicate/remove line all mutate `ctx.draft` exactly
|
|
54
|
+
like the Vue/Nuxt recipes — but there is no separate submission payload
|
|
55
|
+
to assemble by hand: `form.transform()` shapes the request just before
|
|
56
|
+
`post`/`put`.
|
|
57
|
+
- **422 errors reach every input — including table rows — through
|
|
58
|
+
Inertia's own `form.errors`.** Laravel-style keys (`customerId`,
|
|
59
|
+
`lineItems.2.unitPrice`) arrive pre-mapped; scalar fields bind
|
|
60
|
+
`ctx.errors.value.<key>` and the editable cells resolve their row key
|
|
61
|
+
through `ctx.lineItemError(line, field)`, unchanged from the other
|
|
62
|
+
platforms.
|
|
63
|
+
- **Tied selects are declarative.** Project depends on customer via
|
|
64
|
+
`dependencies` + `enforce-coherence`; switching customers refetches the
|
|
65
|
+
projects and clears a now-invalid pick automatically. No watchers.
|
|
66
|
+
- **Line items are an inline-editing table (§6.3).** One leaf per cell
|
|
67
|
+
type — description, quantity, unit price — each swapping to an input on
|
|
68
|
+
click, committing on blur/Enter and reverting on Esc. The amount column
|
|
69
|
+
is virtual, computed from the row.
|
|
70
|
+
- **Dirty state is native.** `form.isDirty` compares the live draft
|
|
71
|
+
against the baseline `form.defaults()` set on load and re-taken after
|
|
72
|
+
every successful save — no JSON snapshot to maintain. The route-leave
|
|
73
|
+
guard is the toolkit's `useConfirmOnRouteLeave({ disabled: () =>
|
|
74
|
+
!form.isDirty, whitelist: [submitUrl] })`, whitelisting the form's own
|
|
75
|
+
submit URL so a save can't confirm-block itself.
|
|
76
|
+
- **Clicking the customer's name peeks the customer** — a workbench, not a
|
|
77
|
+
preview: billing profile, stats, recent invoices, and an action that
|
|
78
|
+
writes the customer's defaults back into the draft.
|
|
79
|
+
- **Buttons are `md` by default;** `sm` only in the line-items table and
|
|
80
|
+
the peek. One `variant="primary"`: the save. No filter rail, no tabs, no
|
|
81
|
+
bulk bar — the form archetype has no list to filter and no selection.
|
|
82
|
+
|
|
83
|
+
## Page anatomy
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
InvoiceForm/
|
|
87
|
+
Index.vue ← assembly: PageShell slots, header, <form>, sections
|
|
88
|
+
context.ts ← coopting page context (shell + usePromiseForm draft + actions + leave guard)
|
|
89
|
+
DetailsSection.vue ← tied selects, dates, terms, PO, tax rate
|
|
90
|
+
LineItemsSection.vue ← editable BbTable + add line + live totals
|
|
91
|
+
EditableDescriptionCell.vue ← inline text edit (commit on blur/Enter, Esc reverts)
|
|
92
|
+
EditableQuantityCell.vue ← inline quantity edit, same grammar
|
|
93
|
+
EditableRateCell.vue ← inline unit-price edit, currency-formatted display
|
|
94
|
+
NotesSection.vue ← BbCollapsible disclosure over notes + terms
|
|
95
|
+
FormActions.vue ← sticky action row: delete / cancel / save draft / save
|
|
96
|
+
CustomerPeek.vue ← sidebar workbench for the picked customer
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Chrome assumed from the guides: [App Layout](../../guides/app-layout.md)
|
|
100
|
+
(left nav + fat footer) and [Page Shell](../../guides/page-shell.md)
|
|
101
|
+
(topbar / content / stacked sidebar). The floating bottom panel is
|
|
102
|
+
deliberately absent — it is selection chrome, and a form has no selection.
|
|
103
|
+
|
|
104
|
+
## Action inventory
|
|
105
|
+
|
|
106
|
+
| # | Action | Trigger | Mechanism |
|
|
107
|
+
| --- | ------------------------------- | ------------------------------------- | ---------------------------------------------------------------------- |
|
|
108
|
+
| 1 | Save invoice | sticky bar, the page's one `primary` | `type="submit"` → `persist()` (`form.transform` + `post`/`put`) |
|
|
109
|
+
| 2 | Save as draft | sticky bar outline (create mode) | `persist('draft')`; controller redirect lands on the edit route |
|
|
110
|
+
| 3 | Delete invoice | sticky bar destructive (edit mode) | `useConfirm` → `remove()` (promise-wrapped `form.delete`) → toast |
|
|
111
|
+
| 4 | Cancel | sticky bar ghost | navigation (`href`, an Inertia visit); leave guard intercepts if dirty |
|
|
112
|
+
| 5 | Pick customer | details section select | query-aware `items` provider (small JSON endpoint) |
|
|
113
|
+
| 6 | Pick project | details section select | `dependencies` + `enforce-coherence` (§ tied selects) |
|
|
114
|
+
| 7 | Peek customer | "View customer" link under the select | `ctx.peekCustomer()` → shell stack |
|
|
115
|
+
| 8 | Apply billing defaults | peek workbench button | writes terms + tax rate into the draft, toast |
|
|
116
|
+
| 9 | Edit description inline | line-item cell | §6.3: commit on blur/Enter, Esc reverts |
|
|
117
|
+
| 10 | Edit quantity / rate inline | line-item cells | §6.3 with `BbNumberInput` (min/precision enforced) |
|
|
118
|
+
| 11 | Add line item | section header + empty state | `ctx.addLineItem()` pushes a blank draft row |
|
|
119
|
+
| 12 | Duplicate line | row actions | `ctx.duplicateLineItem(line)` inserts a copy below |
|
|
120
|
+
| 13 | Remove line | row actions | `ctx.removeLineItem(line)` |
|
|
121
|
+
| 14 | Search line items | topbar page search | `lineItemSearch` → `visibleLineItems` filter |
|
|
122
|
+
| 15 | Toggle notes & terms | section disclosure button | `BbCollapsible` + `aria-expanded`/`aria-controls` |
|
|
123
|
+
| 16 | Open customer / recent invoices | peek links | real `href` navigation (an Inertia visit; leave guard applies) |
|
|
124
|
+
|
|
125
|
+
## The code
|
|
126
|
+
|
|
127
|
+
### The shapes
|
|
128
|
+
|
|
129
|
+
What the page props contribute — the record every block below is grounded
|
|
130
|
+
in:
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
// @/types (excerpt)
|
|
134
|
+
export type InvoiceStatus = 'draft' | 'pending' | 'paid' | 'overdue';
|
|
135
|
+
|
|
136
|
+
export interface LineItemDraft {
|
|
137
|
+
/** Server id; unsaved rows carry a negative local id. */
|
|
138
|
+
id: number;
|
|
139
|
+
description: string;
|
|
140
|
+
quantity: number;
|
|
141
|
+
unitPrice: number;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export interface InvoiceDraft {
|
|
145
|
+
customerId: number | null;
|
|
146
|
+
projectId: number | null;
|
|
147
|
+
/** `YYYY-MM-DD` (the date inputs run with `floating`). */
|
|
148
|
+
issuedAt: string | null;
|
|
149
|
+
dueDate: string | null;
|
|
150
|
+
paymentTerms: string | null;
|
|
151
|
+
poNumber: string | null;
|
|
152
|
+
taxRate: number | null;
|
|
153
|
+
lineItems: LineItemDraft[];
|
|
154
|
+
notes: string | null;
|
|
155
|
+
terms: string | null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export interface Invoice extends InvoiceDraft {
|
|
159
|
+
id: number;
|
|
160
|
+
number: string;
|
|
161
|
+
status: InvoiceStatus;
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
// Inertia::render('Invoices/Edit', ['invoice' => ...]) page prop
|
|
167
|
+
const pageProps = {
|
|
168
|
+
invoice: {
|
|
169
|
+
id: 2107,
|
|
170
|
+
number: 'INV-2107',
|
|
171
|
+
status: 'draft',
|
|
172
|
+
customerId: 18, // Northwind Traders
|
|
173
|
+
projectId: 112, // Website relaunch
|
|
174
|
+
issuedAt: '2026-07-01',
|
|
175
|
+
dueDate: '2026-07-31',
|
|
176
|
+
paymentTerms: 'Net 30',
|
|
177
|
+
poNumber: 'PO-2026-0641',
|
|
178
|
+
taxRate: 8.875,
|
|
179
|
+
lineItems: [
|
|
180
|
+
{
|
|
181
|
+
id: 9001,
|
|
182
|
+
description: 'Design sprint — homepage & navigation',
|
|
183
|
+
quantity: 3,
|
|
184
|
+
unitPrice: 1850,
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
id: 9002,
|
|
188
|
+
description: 'Frontend build — catalogue & product views',
|
|
189
|
+
quantity: 5,
|
|
190
|
+
unitPrice: 1200,
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
id: 9003,
|
|
194
|
+
description: 'CMS integration & content migration',
|
|
195
|
+
quantity: 2,
|
|
196
|
+
unitPrice: 1400,
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
id: 9004,
|
|
200
|
+
description: 'QA & cross-browser pass',
|
|
201
|
+
quantity: 1,
|
|
202
|
+
unitPrice: 950,
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
id: 9005,
|
|
206
|
+
description: 'Staging infrastructure (June)',
|
|
207
|
+
quantity: 1,
|
|
208
|
+
unitPrice: 240,
|
|
209
|
+
},
|
|
210
|
+
],
|
|
211
|
+
notes:
|
|
212
|
+
'Second milestone of the relaunch engagement. Hosting billed at cost.',
|
|
213
|
+
terms:
|
|
214
|
+
'Payment due within 30 days. 1.5% monthly interest on overdue balances.',
|
|
215
|
+
}, // null on the create route
|
|
216
|
+
};
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### context.ts
|
|
220
|
+
|
|
221
|
+
```ts
|
|
222
|
+
// InvoiceForm/context.ts
|
|
223
|
+
import {
|
|
224
|
+
computed,
|
|
225
|
+
inject,
|
|
226
|
+
provide,
|
|
227
|
+
ref,
|
|
228
|
+
type ComputedRef,
|
|
229
|
+
type InjectionKey,
|
|
230
|
+
type Ref,
|
|
231
|
+
} from 'vue';
|
|
232
|
+
import { useConfirm, useToast } from 'bitboss-ui';
|
|
233
|
+
import {
|
|
234
|
+
usePageShellContext,
|
|
235
|
+
type PageShellContext,
|
|
236
|
+
} from '@/components/layout/usePageShellContext';
|
|
237
|
+
import { usePromiseForm } from '@/composables/usePromiseForm';
|
|
238
|
+
import { useConfirmOnRouteLeave } from '@/composables/useConfirmOnRouteLeave';
|
|
239
|
+
import { useProp } from '@/composables/useProp';
|
|
240
|
+
import type {
|
|
241
|
+
Customer,
|
|
242
|
+
CustomerDetail,
|
|
243
|
+
Invoice,
|
|
244
|
+
InvoiceDraft,
|
|
245
|
+
InvoiceStatus,
|
|
246
|
+
LineItemDraft,
|
|
247
|
+
Project,
|
|
248
|
+
} from '@/types';
|
|
249
|
+
|
|
250
|
+
/** What the stacked sidebar can show. Clicking the customer's name pushes one. */
|
|
251
|
+
export type PeekItem = { type: 'customer'; id: number };
|
|
252
|
+
|
|
253
|
+
type LineItemField = 'description' | 'quantity' | 'unitPrice';
|
|
254
|
+
|
|
255
|
+
export interface InvoiceFormContext {
|
|
256
|
+
// -- Coopted contexts ------------------------------------------------------
|
|
257
|
+
shell: PageShellContext<PeekItem>;
|
|
258
|
+
// -- Page-owned state ------------------------------------------------------
|
|
259
|
+
mode: 'create' | 'edit';
|
|
260
|
+
/** Inertia delivers `invoice` as a page prop — nothing to await; this stays
|
|
261
|
+
* a trivial computed only so Index.vue's `v-if` reads the same on every
|
|
262
|
+
* platform. */
|
|
263
|
+
loaded: ComputedRef<true>;
|
|
264
|
+
invoiceNumber: ComputedRef<string | null>;
|
|
265
|
+
status: ComputedRef<InvoiceStatus>;
|
|
266
|
+
/** The Inertia form's own reactive fields — the draft IS `form`. */
|
|
267
|
+
draft: InvoiceDraft;
|
|
268
|
+
/** Inertia's own error bag: one message per field (not an array). */
|
|
269
|
+
errors: ComputedRef<Record<string, string>>;
|
|
270
|
+
saving: ComputedRef<boolean>;
|
|
271
|
+
isDirty: ComputedRef<boolean>;
|
|
272
|
+
lineItemSearch: Ref<string>;
|
|
273
|
+
visibleLineItems: ComputedRef<LineItemDraft[]>;
|
|
274
|
+
subtotal: ComputedRef<number>;
|
|
275
|
+
taxAmount: ComputedRef<number>;
|
|
276
|
+
total: ComputedRef<number>;
|
|
277
|
+
// -- Item providers (handed straight to the selects) ------------------------
|
|
278
|
+
fetchCustomers: (query: string, prefill: boolean) => Promise<Customer[]>;
|
|
279
|
+
fetchProjects: () => Promise<Project[]>;
|
|
280
|
+
// -- Row-level error lookup --------------------------------------------------
|
|
281
|
+
lineItemError: (
|
|
282
|
+
line: LineItemDraft,
|
|
283
|
+
field: LineItemField
|
|
284
|
+
) => string | undefined;
|
|
285
|
+
// -- Draft mutations (the cells and the peek write through these) -----------
|
|
286
|
+
updateLineItem: (
|
|
287
|
+
line: LineItemDraft,
|
|
288
|
+
patch: Partial<Omit<LineItemDraft, 'id'>>
|
|
289
|
+
) => void;
|
|
290
|
+
addLineItem: () => void;
|
|
291
|
+
duplicateLineItem: (line: LineItemDraft) => void;
|
|
292
|
+
removeLineItem: (line: LineItemDraft) => void;
|
|
293
|
+
applyCustomerDefaults: (customer: CustomerDetail) => void;
|
|
294
|
+
// -- Peek grammar ------------------------------------------------------------
|
|
295
|
+
peekCustomer: () => void;
|
|
296
|
+
// -- Actions (complete: visit + toast) ----------------------------------------
|
|
297
|
+
saveInvoice: () => Promise<void>;
|
|
298
|
+
saveAsDraft: () => Promise<void>;
|
|
299
|
+
deleteInvoice: () => Promise<void>;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const key: InjectionKey<InvoiceFormContext> = Symbol('invoice-form');
|
|
303
|
+
|
|
304
|
+
export function provideInvoiceForm(): InvoiceFormContext {
|
|
305
|
+
const { toast } = useToast();
|
|
306
|
+
const { confirm } = useConfirm();
|
|
307
|
+
|
|
308
|
+
// -- Coopt the shell: once, here, for the whole page. ------------------------
|
|
309
|
+
const shell = usePageShellContext<PeekItem>();
|
|
310
|
+
|
|
311
|
+
// `invoice` is null on the create route, the full record on the edit route.
|
|
312
|
+
const invoice = useProp<Invoice | null>('invoice');
|
|
313
|
+
const mode: 'create' | 'edit' = invoice.value !== null ? 'edit' : 'create';
|
|
314
|
+
const invoiceId = invoice.value?.id ?? null;
|
|
315
|
+
const loaded = computed(() => true as const);
|
|
316
|
+
|
|
317
|
+
const invoiceNumber = computed(() => invoice.value?.number ?? null);
|
|
318
|
+
const status = computed<InvoiceStatus>(
|
|
319
|
+
() => invoice.value?.status ?? 'draft'
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
// -- The draft IS the Inertia form -------------------------------------------
|
|
323
|
+
// Unsaved rows get negative local ids so `item-value` stays stable and the
|
|
324
|
+
// payload can tell the server which lines are new (negative → null).
|
|
325
|
+
let nextLocalId = -1;
|
|
326
|
+
const blankLineItem = (): LineItemDraft => ({
|
|
327
|
+
id: nextLocalId--,
|
|
328
|
+
description: '',
|
|
329
|
+
quantity: 1,
|
|
330
|
+
unitPrice: 0,
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
const { form, post, put, remove, isSubmitting } =
|
|
334
|
+
usePromiseForm<InvoiceDraft>({
|
|
335
|
+
customerId: invoice.value?.customerId ?? null,
|
|
336
|
+
projectId: invoice.value?.projectId ?? null,
|
|
337
|
+
issuedAt: invoice.value?.issuedAt ?? null,
|
|
338
|
+
dueDate: invoice.value?.dueDate ?? null,
|
|
339
|
+
paymentTerms: invoice.value?.paymentTerms ?? null,
|
|
340
|
+
poNumber: invoice.value?.poNumber ?? null,
|
|
341
|
+
taxRate: invoice.value?.taxRate ?? null,
|
|
342
|
+
lineItems: invoice.value
|
|
343
|
+
? invoice.value.lineItems.map((line) => ({ ...line }))
|
|
344
|
+
: [blankLineItem()],
|
|
345
|
+
notes: invoice.value?.notes ?? null,
|
|
346
|
+
terms: invoice.value?.terms ?? null,
|
|
347
|
+
});
|
|
348
|
+
const draft = form;
|
|
349
|
+
const errors = computed(() => form.errors as Record<string, string>);
|
|
350
|
+
const saving = isSubmitting;
|
|
351
|
+
const isDirty = computed(() => form.isDirty);
|
|
352
|
+
|
|
353
|
+
// -- Item providers -----------------------------------------------------------
|
|
354
|
+
// Small JSON endpoints, not page visits: searching a select must not
|
|
355
|
+
// re-render the page. Component-initiated calls (`prefill === true`) must
|
|
356
|
+
// keep the current selection resolvable, or the chip loses its text.
|
|
357
|
+
const fetchCustomers = async (
|
|
358
|
+
query: string,
|
|
359
|
+
prefill: boolean
|
|
360
|
+
): Promise<Customer[]> => {
|
|
361
|
+
if (prefill && draft.customerId !== null) {
|
|
362
|
+
return fetch(`/customers/search?ids[]=${draft.customerId}`).then((res) =>
|
|
363
|
+
res.json()
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
return fetch(`/customers/search?q=${encodeURIComponent(query)}`).then(
|
|
367
|
+
(res) => res.json()
|
|
368
|
+
);
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
// Re-run by the project select's `dependencies: [draft.customerId]`; the
|
|
372
|
+
// response always contains the customer's full project list, so an existing
|
|
373
|
+
// `projectId` stays resolvable and `enforce-coherence` never wipes a valid pick.
|
|
374
|
+
const fetchProjects = (): Promise<Project[]> =>
|
|
375
|
+
draft.customerId === null
|
|
376
|
+
? Promise.resolve([])
|
|
377
|
+
: fetch(`/customers/${draft.customerId}/projects`).then((res) =>
|
|
378
|
+
res.json()
|
|
379
|
+
);
|
|
380
|
+
|
|
381
|
+
// -- Line items -----------------------------------------------------------------
|
|
382
|
+
const lineItemSearch = ref('');
|
|
383
|
+
const visibleLineItems = computed(() => {
|
|
384
|
+
const query = lineItemSearch.value.trim().toLowerCase();
|
|
385
|
+
if (!query) return draft.lineItems;
|
|
386
|
+
return draft.lineItems.filter((line) =>
|
|
387
|
+
line.description.toLowerCase().includes(query)
|
|
388
|
+
);
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
const subtotal = computed(() =>
|
|
392
|
+
draft.lineItems.reduce(
|
|
393
|
+
(sum, line) => sum + line.quantity * line.unitPrice,
|
|
394
|
+
0
|
|
395
|
+
)
|
|
396
|
+
);
|
|
397
|
+
const taxAmount = computed(
|
|
398
|
+
() => subtotal.value * ((draft.taxRate ?? 0) / 100)
|
|
399
|
+
);
|
|
400
|
+
const total = computed(() => subtotal.value + taxAmount.value);
|
|
401
|
+
|
|
402
|
+
// Server error keys index lines by payload position, which is draft order —
|
|
403
|
+
// resolve a row's key through its id so the lookup survives filtering.
|
|
404
|
+
const lineItemError = (
|
|
405
|
+
line: LineItemDraft,
|
|
406
|
+
field: LineItemField
|
|
407
|
+
): string | undefined => {
|
|
408
|
+
const index = draft.lineItems.findIndex(
|
|
409
|
+
(candidate) => candidate.id === line.id
|
|
410
|
+
);
|
|
411
|
+
return errors.value[`lineItems.${index}.${field}`];
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
const updateLineItem = (
|
|
415
|
+
line: LineItemDraft,
|
|
416
|
+
patch: Partial<Omit<LineItemDraft, 'id'>>
|
|
417
|
+
) => {
|
|
418
|
+
const target = draft.lineItems.find(
|
|
419
|
+
(candidate) => candidate.id === line.id
|
|
420
|
+
);
|
|
421
|
+
if (target) Object.assign(target, patch);
|
|
422
|
+
};
|
|
423
|
+
const addLineItem = () => {
|
|
424
|
+
draft.lineItems.push(blankLineItem());
|
|
425
|
+
};
|
|
426
|
+
const duplicateLineItem = (line: LineItemDraft) => {
|
|
427
|
+
const index = draft.lineItems.findIndex(
|
|
428
|
+
(candidate) => candidate.id === line.id
|
|
429
|
+
);
|
|
430
|
+
draft.lineItems.splice(index + 1, 0, { ...line, id: nextLocalId-- });
|
|
431
|
+
};
|
|
432
|
+
const removeLineItem = (line: LineItemDraft) => {
|
|
433
|
+
draft.lineItems = draft.lineItems.filter(
|
|
434
|
+
(candidate) => candidate.id !== line.id
|
|
435
|
+
);
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
// The peek writes back into the draft: the sidebar is a workbench.
|
|
439
|
+
const applyCustomerDefaults = (customer: CustomerDetail) => {
|
|
440
|
+
draft.paymentTerms = customer.defaultPaymentTerms;
|
|
441
|
+
draft.taxRate = customer.defaultTaxRate;
|
|
442
|
+
toast({
|
|
443
|
+
title: `Applied ${customer.name}'s billing defaults.`,
|
|
444
|
+
variant: 'success',
|
|
445
|
+
});
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
// -- Peek grammar ----------------------------------------------------------------
|
|
449
|
+
const peekCustomer = () => {
|
|
450
|
+
if (draft.customerId === null) return;
|
|
451
|
+
shell.push({ type: 'customer', id: draft.customerId }, true);
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
// -- Persistence -----------------------------------------------------------------
|
|
455
|
+
// The submit target doubles as the whitelist entry below: PUT/DELETE and the
|
|
456
|
+
// create POST land on the same two paths.
|
|
457
|
+
const submitUrl =
|
|
458
|
+
mode === 'edit' && invoiceId !== null
|
|
459
|
+
? `/invoices/${invoiceId}`
|
|
460
|
+
: '/invoices';
|
|
461
|
+
|
|
462
|
+
const persist = async (nextStatus: InvoiceStatus): Promise<boolean> => {
|
|
463
|
+
// New lines travel with `id: null`; the server assigns real ids on save.
|
|
464
|
+
// `transform` shapes the request without mutating the draft's own fields.
|
|
465
|
+
form.transform((data) => ({
|
|
466
|
+
...data,
|
|
467
|
+
status: nextStatus,
|
|
468
|
+
lineItems: (data.lineItems as LineItemDraft[]).map((line) => ({
|
|
469
|
+
...line,
|
|
470
|
+
id: line.id > 0 ? line.id : null,
|
|
471
|
+
})),
|
|
472
|
+
}));
|
|
473
|
+
try {
|
|
474
|
+
await (mode === 'edit' ? put(submitUrl) : post(submitUrl));
|
|
475
|
+
// Re-baseline: the just-saved values become the new "clean" state,
|
|
476
|
+
// same purpose as the Vue/Nuxt recipes' JSON snapshot re-take.
|
|
477
|
+
form.defaults();
|
|
478
|
+
return true;
|
|
479
|
+
} catch {
|
|
480
|
+
// form.errors is already populated by usePromiseForm's reject path.
|
|
481
|
+
toast({
|
|
482
|
+
title: form.hasErrors
|
|
483
|
+
? 'Some fields need attention.'
|
|
484
|
+
: 'Something went wrong. Try again.',
|
|
485
|
+
variant: 'destructive',
|
|
486
|
+
});
|
|
487
|
+
return false;
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
const saveInvoice = async () => {
|
|
492
|
+
// Creating finalizes the invoice; editing keeps whatever status it has.
|
|
493
|
+
const ok = await persist(mode === 'edit' ? status.value : 'pending');
|
|
494
|
+
if (!ok) return;
|
|
495
|
+
// The controller's redirect is followed inside this same visit — no
|
|
496
|
+
// client-side navigation call needed.
|
|
497
|
+
toast({
|
|
498
|
+
title:
|
|
499
|
+
mode === 'edit'
|
|
500
|
+
? `Invoice ${invoiceNumber.value} saved.`
|
|
501
|
+
: 'Invoice created.',
|
|
502
|
+
variant: 'success',
|
|
503
|
+
});
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
const saveAsDraft = async () => {
|
|
507
|
+
const ok = await persist('draft');
|
|
508
|
+
if (!ok) return;
|
|
509
|
+
toast({
|
|
510
|
+
title:
|
|
511
|
+
mode === 'edit'
|
|
512
|
+
? `Draft ${invoiceNumber.value} saved.`
|
|
513
|
+
: 'Draft saved.',
|
|
514
|
+
variant: 'success',
|
|
515
|
+
});
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
const deleteInvoice = async () => {
|
|
519
|
+
if (mode !== 'edit' || invoiceId === null) return;
|
|
520
|
+
const ok = await confirm({
|
|
521
|
+
variant: 'destructive',
|
|
522
|
+
title: 'Delete invoice',
|
|
523
|
+
text: `Delete ${invoiceNumber.value}? This cannot be undone.`,
|
|
524
|
+
yes: 'Delete',
|
|
525
|
+
no: 'Cancel',
|
|
526
|
+
});
|
|
527
|
+
if (!ok) return;
|
|
528
|
+
await remove(submitUrl);
|
|
529
|
+
toast({ title: `${invoiceNumber.value} deleted.`, variant: 'success' });
|
|
530
|
+
};
|
|
531
|
+
|
|
532
|
+
// -- Route-leave guard ---------------------------------------------------------
|
|
533
|
+
// `form.isDirty` needs no snapshot bookkeeping; the submit URL is whitelisted
|
|
534
|
+
// so saving (which IS a visit) can't confirm-block itself.
|
|
535
|
+
useConfirmOnRouteLeave({
|
|
536
|
+
disabled: () => !form.isDirty,
|
|
537
|
+
whitelist: [submitUrl],
|
|
538
|
+
title: 'Unsaved changes',
|
|
539
|
+
text: 'You have unsaved changes on this invoice. Leave without saving?',
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
const ctx: InvoiceFormContext = {
|
|
543
|
+
shell,
|
|
544
|
+
mode,
|
|
545
|
+
loaded,
|
|
546
|
+
invoiceNumber,
|
|
547
|
+
status,
|
|
548
|
+
draft,
|
|
549
|
+
errors,
|
|
550
|
+
saving,
|
|
551
|
+
isDirty,
|
|
552
|
+
lineItemSearch,
|
|
553
|
+
visibleLineItems,
|
|
554
|
+
subtotal,
|
|
555
|
+
taxAmount,
|
|
556
|
+
total,
|
|
557
|
+
fetchCustomers,
|
|
558
|
+
fetchProjects,
|
|
559
|
+
lineItemError,
|
|
560
|
+
updateLineItem,
|
|
561
|
+
addLineItem,
|
|
562
|
+
duplicateLineItem,
|
|
563
|
+
removeLineItem,
|
|
564
|
+
applyCustomerDefaults,
|
|
565
|
+
peekCustomer,
|
|
566
|
+
saveInvoice,
|
|
567
|
+
saveAsDraft,
|
|
568
|
+
deleteInvoice,
|
|
569
|
+
};
|
|
570
|
+
provide(key, ctx);
|
|
571
|
+
return ctx;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
export function useInvoiceForm(): InvoiceFormContext {
|
|
575
|
+
const ctx = inject(key);
|
|
576
|
+
if (!ctx) throw new Error('useInvoiceForm() called outside InvoiceForm');
|
|
577
|
+
return ctx;
|
|
578
|
+
}
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
### Index.vue — the assembly
|
|
582
|
+
|
|
583
|
+
```vue
|
|
584
|
+
<!-- InvoiceForm/Index.vue -->
|
|
585
|
+
<template>
|
|
586
|
+
<PageShell>
|
|
587
|
+
<!-- Topbar: breadcrumbs left, page search right. The search scopes the
|
|
588
|
+
work surface — here, the line-items table. -->
|
|
589
|
+
<template #topbar>
|
|
590
|
+
<div class="flex items-center justify-between gap-4 border-b px-4 py-2">
|
|
591
|
+
<!-- Breadcrumbs take the row's slack on a stable basis (min-w-0 flex-auto);
|
|
592
|
+
without it their overflow recompute fights the flex layout every frame. -->
|
|
593
|
+
<BbBreadcrumbs class="min-w-0 flex-auto" :items="trail" />
|
|
594
|
+
<BbTextInput
|
|
595
|
+
v-model="ctx.lineItemSearch.value"
|
|
596
|
+
autocomplete="off"
|
|
597
|
+
class="shrink-0"
|
|
598
|
+
clearable
|
|
599
|
+
compact
|
|
600
|
+
hide-label
|
|
601
|
+
label="Search line items"
|
|
602
|
+
placeholder="Search line items…"
|
|
603
|
+
prepend:icon="lucide:search"
|
|
604
|
+
/>
|
|
605
|
+
</div>
|
|
606
|
+
</template>
|
|
607
|
+
|
|
608
|
+
<div
|
|
609
|
+
v-if="ctx.loaded.value"
|
|
610
|
+
class="mx-auto flex w-full max-w-4xl flex-col gap-6 p-4"
|
|
611
|
+
>
|
|
612
|
+
<header class="flex items-center gap-3">
|
|
613
|
+
<h1 class="text-lg font-semibold">
|
|
614
|
+
{{
|
|
615
|
+
ctx.mode === 'edit'
|
|
616
|
+
? `Edit ${ctx.invoiceNumber.value}`
|
|
617
|
+
: 'New invoice'
|
|
618
|
+
}}
|
|
619
|
+
</h1>
|
|
620
|
+
<BbBadge v-if="ctx.mode === 'edit'" variant="outline">{{
|
|
621
|
+
ctx.status.value
|
|
622
|
+
}}</BbBadge>
|
|
623
|
+
</header>
|
|
624
|
+
|
|
625
|
+
<!-- One form, one submit. `novalidate` keeps error UX on the inputs'
|
|
626
|
+
`errors` surface instead of browser bubbles. -->
|
|
627
|
+
<form
|
|
628
|
+
class="flex flex-col gap-6"
|
|
629
|
+
novalidate
|
|
630
|
+
@submit.prevent="ctx.saveInvoice"
|
|
631
|
+
>
|
|
632
|
+
<DetailsSection />
|
|
633
|
+
<LineItemsSection />
|
|
634
|
+
<NotesSection />
|
|
635
|
+
<FormActions />
|
|
636
|
+
</form>
|
|
637
|
+
</div>
|
|
638
|
+
|
|
639
|
+
<div v-else class="flex justify-center p-12">
|
|
640
|
+
<BbSpinner label="Loading invoice…" />
|
|
641
|
+
</div>
|
|
642
|
+
|
|
643
|
+
<!-- Stacked peek sidebar: the shell owns back/close; we render by type. -->
|
|
644
|
+
<template #sidebar>
|
|
645
|
+
<CustomerPeek
|
|
646
|
+
v-if="ctx.shell.current.value?.type === 'customer'"
|
|
647
|
+
:customer-id="ctx.shell.current.value.id"
|
|
648
|
+
/>
|
|
649
|
+
</template>
|
|
650
|
+
</PageShell>
|
|
651
|
+
</template>
|
|
652
|
+
|
|
653
|
+
<script setup lang="ts">
|
|
654
|
+
import { computed } from 'vue';
|
|
655
|
+
import { BbBadge, BbBreadcrumbs, BbSpinner, BbTextInput } from 'bitboss-ui';
|
|
656
|
+
import PageShell from '@/components/layout/PageShell.vue';
|
|
657
|
+
import { provideInvoiceForm } from './context';
|
|
658
|
+
import CustomerPeek from './CustomerPeek.vue';
|
|
659
|
+
import DetailsSection from './DetailsSection.vue';
|
|
660
|
+
import FormActions from './FormActions.vue';
|
|
661
|
+
import LineItemsSection from './LineItemsSection.vue';
|
|
662
|
+
import NotesSection from './NotesSection.vue';
|
|
663
|
+
|
|
664
|
+
const ctx = provideInvoiceForm();
|
|
665
|
+
|
|
666
|
+
// A real trail, ≥ 2 linked levels; the current page carries no target.
|
|
667
|
+
const trail = computed(() => [
|
|
668
|
+
{ key: 'dashboard', text: 'Dashboard', href: '/dashboard' },
|
|
669
|
+
{ key: 'billing', text: 'Billing', href: '/billing' },
|
|
670
|
+
{ key: 'invoices', text: 'Invoices', href: '/invoices' },
|
|
671
|
+
{
|
|
672
|
+
key: 'invoice',
|
|
673
|
+
text:
|
|
674
|
+
ctx.mode === 'edit'
|
|
675
|
+
? (ctx.invoiceNumber.value ?? 'Invoice')
|
|
676
|
+
: 'New invoice',
|
|
677
|
+
},
|
|
678
|
+
]);
|
|
679
|
+
</script>
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
### DetailsSection.vue
|
|
683
|
+
|
|
684
|
+
```vue
|
|
685
|
+
<!-- InvoiceForm/DetailsSection.vue -->
|
|
686
|
+
<template>
|
|
687
|
+
<section class="flex flex-col gap-3">
|
|
688
|
+
<h2
|
|
689
|
+
class="text-xs font-semibold uppercase tracking-wide text-[color:var(--bb-text-muted)]"
|
|
690
|
+
>
|
|
691
|
+
Details
|
|
692
|
+
</h2>
|
|
693
|
+
|
|
694
|
+
<div class="grid gap-4 sm:grid-cols-2">
|
|
695
|
+
<!-- Tied selects: project depends on customer. `dependencies` re-runs
|
|
696
|
+
the project provider; `enforce-coherence` clears a project that is
|
|
697
|
+
no longer among the new options. No watchers. -->
|
|
698
|
+
<div class="flex flex-col gap-1">
|
|
699
|
+
<BbSelect
|
|
700
|
+
v-model="ctx.draft.customerId"
|
|
701
|
+
:errors="ctx.errors.value.customerId"
|
|
702
|
+
item-text="name"
|
|
703
|
+
item-value="id"
|
|
704
|
+
:items="ctx.fetchCustomers"
|
|
705
|
+
label="Customer"
|
|
706
|
+
placeholder="Search customers…"
|
|
707
|
+
/>
|
|
708
|
+
<!-- Clicking the NAME of a thing peeks it. -->
|
|
709
|
+
<BbButton
|
|
710
|
+
v-if="ctx.draft.customerId !== null"
|
|
711
|
+
append:icon="lucide:panel-right-open"
|
|
712
|
+
class="self-start"
|
|
713
|
+
size="sm"
|
|
714
|
+
variant="link"
|
|
715
|
+
@click="ctx.peekCustomer"
|
|
716
|
+
>
|
|
717
|
+
View customer
|
|
718
|
+
</BbButton>
|
|
719
|
+
</div>
|
|
720
|
+
|
|
721
|
+
<BbSelect
|
|
722
|
+
v-model="ctx.draft.projectId"
|
|
723
|
+
:dependencies="[ctx.draft.customerId]"
|
|
724
|
+
:disabled="ctx.draft.customerId === null"
|
|
725
|
+
enforce-coherence
|
|
726
|
+
:errors="ctx.errors.value.projectId"
|
|
727
|
+
item-text="name"
|
|
728
|
+
item-value="id"
|
|
729
|
+
:items="ctx.fetchProjects"
|
|
730
|
+
label="Project"
|
|
731
|
+
placeholder="Pick the customer first"
|
|
732
|
+
/>
|
|
733
|
+
|
|
734
|
+
<!-- `floating` keeps the model a plain YYYY-MM-DD string, so the due
|
|
735
|
+
date's `min` can be fed straight from the issue date. -->
|
|
736
|
+
<BbDatePickerInput
|
|
737
|
+
v-model="ctx.draft.issuedAt"
|
|
738
|
+
:errors="ctx.errors.value.issuedAt"
|
|
739
|
+
floating
|
|
740
|
+
label="Issue date"
|
|
741
|
+
prepend:icon="lucide:calendar"
|
|
742
|
+
/>
|
|
743
|
+
<BbDatePickerInput
|
|
744
|
+
v-model="ctx.draft.dueDate"
|
|
745
|
+
:errors="ctx.errors.value.dueDate"
|
|
746
|
+
floating
|
|
747
|
+
label="Due date"
|
|
748
|
+
:min="ctx.draft.issuedAt ?? undefined"
|
|
749
|
+
prepend:icon="lucide:calendar"
|
|
750
|
+
/>
|
|
751
|
+
|
|
752
|
+
<BbSelect
|
|
753
|
+
v-model="ctx.draft.paymentTerms"
|
|
754
|
+
:errors="ctx.errors.value.paymentTerms"
|
|
755
|
+
:items="['Due on receipt', 'Net 15', 'Net 30', 'Net 60']"
|
|
756
|
+
label="Payment terms"
|
|
757
|
+
placeholder="Choose terms"
|
|
758
|
+
/>
|
|
759
|
+
|
|
760
|
+
<BbTextInput
|
|
761
|
+
v-model="ctx.draft.poNumber"
|
|
762
|
+
:errors="ctx.errors.value.poNumber"
|
|
763
|
+
label="PO / reference"
|
|
764
|
+
placeholder="PO-2026-0641"
|
|
765
|
+
/>
|
|
766
|
+
|
|
767
|
+
<BbNumberInput
|
|
768
|
+
v-model="ctx.draft.taxRate"
|
|
769
|
+
:errors="ctx.errors.value.taxRate"
|
|
770
|
+
hint="Applied to the subtotal below."
|
|
771
|
+
label="Tax rate"
|
|
772
|
+
:max="100"
|
|
773
|
+
:max-precision="3"
|
|
774
|
+
:min="0"
|
|
775
|
+
>
|
|
776
|
+
<template #suffix>%</template>
|
|
777
|
+
</BbNumberInput>
|
|
778
|
+
</div>
|
|
779
|
+
</section>
|
|
780
|
+
</template>
|
|
781
|
+
|
|
782
|
+
<script setup lang="ts">
|
|
783
|
+
import {
|
|
784
|
+
BbButton,
|
|
785
|
+
BbDatePickerInput,
|
|
786
|
+
BbNumberInput,
|
|
787
|
+
BbSelect,
|
|
788
|
+
BbTextInput,
|
|
789
|
+
} from 'bitboss-ui';
|
|
790
|
+
import { useInvoiceForm } from './context';
|
|
791
|
+
|
|
792
|
+
const ctx = useInvoiceForm();
|
|
793
|
+
</script>
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
### LineItemsSection.vue
|
|
797
|
+
|
|
798
|
+
```vue
|
|
799
|
+
<!-- InvoiceForm/LineItemsSection.vue -->
|
|
800
|
+
<template>
|
|
801
|
+
<section class="flex flex-col gap-3">
|
|
802
|
+
<div class="flex items-center justify-between">
|
|
803
|
+
<h2
|
|
804
|
+
class="text-xs font-semibold uppercase tracking-wide text-[color:var(--bb-text-muted)]"
|
|
805
|
+
>
|
|
806
|
+
Line items
|
|
807
|
+
</h2>
|
|
808
|
+
<BbButton
|
|
809
|
+
prepend:icon="lucide:plus"
|
|
810
|
+
variant="outline"
|
|
811
|
+
@click="ctx.addLineItem"
|
|
812
|
+
>
|
|
813
|
+
Add line item
|
|
814
|
+
</BbButton>
|
|
815
|
+
</div>
|
|
816
|
+
|
|
817
|
+
<!-- Inline-editing table (§6.3): every editable cell type is its own
|
|
818
|
+
leaf. The amount column is virtual — computed from the row. -->
|
|
819
|
+
<BbTable
|
|
820
|
+
actions
|
|
821
|
+
caption="Invoice line items"
|
|
822
|
+
:columns="columns"
|
|
823
|
+
compact
|
|
824
|
+
item-value="id"
|
|
825
|
+
:items="ctx.visibleLineItems.value"
|
|
826
|
+
>
|
|
827
|
+
<template #description="{ item }">
|
|
828
|
+
<EditableDescriptionCell :line="item" />
|
|
829
|
+
</template>
|
|
830
|
+
|
|
831
|
+
<template #quantity="{ item }">
|
|
832
|
+
<EditableQuantityCell :line="item" />
|
|
833
|
+
</template>
|
|
834
|
+
|
|
835
|
+
<template #unit_price="{ item }">
|
|
836
|
+
<EditableRateCell :line="item" />
|
|
837
|
+
</template>
|
|
838
|
+
|
|
839
|
+
<template #actions="{ item }">
|
|
840
|
+
<div class="flex items-center justify-end gap-1">
|
|
841
|
+
<BbButton
|
|
842
|
+
icon="lucide:copy"
|
|
843
|
+
size="sm"
|
|
844
|
+
variant="ghost"
|
|
845
|
+
@click="() => ctx.duplicateLineItem(item)"
|
|
846
|
+
>
|
|
847
|
+
Duplicate line
|
|
848
|
+
</BbButton>
|
|
849
|
+
<BbButton
|
|
850
|
+
icon="lucide:trash-2"
|
|
851
|
+
size="sm"
|
|
852
|
+
variant="ghost"
|
|
853
|
+
@click="() => ctx.removeLineItem(item)"
|
|
854
|
+
>
|
|
855
|
+
Remove line
|
|
856
|
+
</BbButton>
|
|
857
|
+
</div>
|
|
858
|
+
</template>
|
|
859
|
+
|
|
860
|
+
<template #no-data>
|
|
861
|
+
<div class="flex flex-col items-center gap-2 p-6 text-center">
|
|
862
|
+
<p class="text-sm text-[color:var(--bb-text-muted)]">
|
|
863
|
+
{{
|
|
864
|
+
ctx.lineItemSearch.value
|
|
865
|
+
? 'No line items match your search.'
|
|
866
|
+
: 'No line items yet.'
|
|
867
|
+
}}
|
|
868
|
+
</p>
|
|
869
|
+
<BbButton
|
|
870
|
+
v-if="!ctx.lineItemSearch.value"
|
|
871
|
+
variant="outline"
|
|
872
|
+
@click="ctx.addLineItem"
|
|
873
|
+
>
|
|
874
|
+
Add the first line
|
|
875
|
+
</BbButton>
|
|
876
|
+
</div>
|
|
877
|
+
</template>
|
|
878
|
+
</BbTable>
|
|
879
|
+
|
|
880
|
+
<!-- Totals: live computed from the draft — they move as cells commit. -->
|
|
881
|
+
<dl class="ml-auto grid w-60 grid-cols-[1fr_auto] gap-y-1 text-sm">
|
|
882
|
+
<dt class="text-[color:var(--bb-text-muted)]">Subtotal</dt>
|
|
883
|
+
<dd class="text-right tabular-nums">
|
|
884
|
+
{{ currency.format(ctx.subtotal.value) }}
|
|
885
|
+
</dd>
|
|
886
|
+
<dt class="text-[color:var(--bb-text-muted)]">
|
|
887
|
+
Tax ({{ ctx.draft.taxRate ?? 0 }}%)
|
|
888
|
+
</dt>
|
|
889
|
+
<dd class="text-right tabular-nums">
|
|
890
|
+
{{ currency.format(ctx.taxAmount.value) }}
|
|
891
|
+
</dd>
|
|
892
|
+
<dt class="font-semibold">Total</dt>
|
|
893
|
+
<dd class="text-right font-semibold tabular-nums">
|
|
894
|
+
{{ currency.format(ctx.total.value) }}
|
|
895
|
+
</dd>
|
|
896
|
+
</dl>
|
|
897
|
+
</section>
|
|
898
|
+
</template>
|
|
899
|
+
|
|
900
|
+
<script setup lang="ts">
|
|
901
|
+
import { BbButton, BbTable } from 'bitboss-ui';
|
|
902
|
+
import EditableDescriptionCell from './EditableDescriptionCell.vue';
|
|
903
|
+
import EditableQuantityCell from './EditableQuantityCell.vue';
|
|
904
|
+
import EditableRateCell from './EditableRateCell.vue';
|
|
905
|
+
import { useInvoiceForm } from './context';
|
|
906
|
+
import type { LineItemDraft } from '@/types';
|
|
907
|
+
|
|
908
|
+
const ctx = useInvoiceForm();
|
|
909
|
+
|
|
910
|
+
const currency = new Intl.NumberFormat('en-US', {
|
|
911
|
+
style: 'currency',
|
|
912
|
+
currency: 'USD',
|
|
913
|
+
});
|
|
914
|
+
|
|
915
|
+
// `unit_price` and `amount` match no item field on purpose: the first is
|
|
916
|
+
// fully slotted (snake_case key so the slot name reads naturally), the
|
|
917
|
+
// second is a virtual column deriving its content in the formatter.
|
|
918
|
+
const columns = [
|
|
919
|
+
{ key: 'description', label: 'Description' },
|
|
920
|
+
{ key: 'quantity', label: 'Qty', align: 'right' as const, width: 96 },
|
|
921
|
+
{
|
|
922
|
+
key: 'unit_price',
|
|
923
|
+
label: 'Unit price',
|
|
924
|
+
align: 'right' as const,
|
|
925
|
+
width: 140,
|
|
926
|
+
},
|
|
927
|
+
{
|
|
928
|
+
key: 'amount',
|
|
929
|
+
label: 'Amount',
|
|
930
|
+
align: 'right' as const,
|
|
931
|
+
width: 130,
|
|
932
|
+
formatter: (_: unknown, __: string, item: LineItemDraft) =>
|
|
933
|
+
currency.format(item.quantity * item.unitPrice),
|
|
934
|
+
},
|
|
935
|
+
];
|
|
936
|
+
</script>
|
|
937
|
+
```
|
|
938
|
+
|
|
939
|
+
### EditableDescriptionCell.vue
|
|
940
|
+
|
|
941
|
+
```vue
|
|
942
|
+
<!-- InvoiceForm/EditableDescriptionCell.vue -->
|
|
943
|
+
<template>
|
|
944
|
+
<BbTextInput
|
|
945
|
+
v-if="editing"
|
|
946
|
+
v-model="value"
|
|
947
|
+
autofocus
|
|
948
|
+
compact
|
|
949
|
+
:errors="ctx.lineItemError(line, 'description')"
|
|
950
|
+
hide-label
|
|
951
|
+
label="Line description"
|
|
952
|
+
placeholder="What was delivered?"
|
|
953
|
+
@blur="commit"
|
|
954
|
+
@keydown.enter.prevent="commit"
|
|
955
|
+
@keydown.esc="revert"
|
|
956
|
+
/>
|
|
957
|
+
<button
|
|
958
|
+
v-else
|
|
959
|
+
class="w-full rounded-[var(--bb-radius)] px-1 py-0.5 text-left hover:bg-[color:var(--bb-hover)]"
|
|
960
|
+
:class="{
|
|
961
|
+
'text-[color:var(--bb-text-muted)]': !line.description,
|
|
962
|
+
'text-red-600 dark:text-red-400': hasError,
|
|
963
|
+
}"
|
|
964
|
+
type="button"
|
|
965
|
+
@click="start"
|
|
966
|
+
>
|
|
967
|
+
{{ line.description || 'Add a description…' }}
|
|
968
|
+
</button>
|
|
969
|
+
</template>
|
|
970
|
+
|
|
971
|
+
<script setup lang="ts">
|
|
972
|
+
import { computed, ref } from 'vue';
|
|
973
|
+
import { BbTextInput } from 'bitboss-ui';
|
|
974
|
+
import { useInvoiceForm } from './context';
|
|
975
|
+
import type { LineItemDraft } from '@/types';
|
|
976
|
+
|
|
977
|
+
const props = defineProps<{ line: LineItemDraft }>();
|
|
978
|
+
|
|
979
|
+
const ctx = useInvoiceForm();
|
|
980
|
+
|
|
981
|
+
// Rung 1 of the ladder: only this cell reads or writes its editing state.
|
|
982
|
+
const editing = ref(false);
|
|
983
|
+
const value = ref<string | null>(null);
|
|
984
|
+
|
|
985
|
+
const hasError = computed(() => !!ctx.lineItemError(props.line, 'description'));
|
|
986
|
+
|
|
987
|
+
const start = () => {
|
|
988
|
+
value.value = props.line.description;
|
|
989
|
+
editing.value = true;
|
|
990
|
+
};
|
|
991
|
+
|
|
992
|
+
// Commit on blur/Enter, revert on Esc (§6.3). Enter is prevented so it
|
|
993
|
+
// edits the cell instead of submitting the enclosing invoice form; the
|
|
994
|
+
// `editing` guard keeps the blur that follows an Esc from re-committing.
|
|
995
|
+
const commit = () => {
|
|
996
|
+
if (!editing.value) return;
|
|
997
|
+
editing.value = false;
|
|
998
|
+
ctx.updateLineItem(props.line, { description: value.value ?? '' });
|
|
999
|
+
};
|
|
1000
|
+
|
|
1001
|
+
const revert = () => {
|
|
1002
|
+
editing.value = false;
|
|
1003
|
+
};
|
|
1004
|
+
</script>
|
|
1005
|
+
```
|
|
1006
|
+
|
|
1007
|
+
### EditableQuantityCell.vue
|
|
1008
|
+
|
|
1009
|
+
```vue
|
|
1010
|
+
<!-- InvoiceForm/EditableQuantityCell.vue -->
|
|
1011
|
+
<template>
|
|
1012
|
+
<BbNumberInput
|
|
1013
|
+
v-if="editing"
|
|
1014
|
+
v-model="value"
|
|
1015
|
+
autofocus
|
|
1016
|
+
compact
|
|
1017
|
+
:errors="ctx.lineItemError(line, 'quantity')"
|
|
1018
|
+
hide-label
|
|
1019
|
+
label="Quantity"
|
|
1020
|
+
:max-precision="2"
|
|
1021
|
+
:min="0"
|
|
1022
|
+
@blur="commit"
|
|
1023
|
+
@keydown.enter.prevent="commit"
|
|
1024
|
+
@keydown.esc="revert"
|
|
1025
|
+
/>
|
|
1026
|
+
<button
|
|
1027
|
+
v-else
|
|
1028
|
+
class="w-full rounded-[var(--bb-radius)] px-1 py-0.5 text-right tabular-nums hover:bg-[color:var(--bb-hover)]"
|
|
1029
|
+
:class="{ 'text-red-600 dark:text-red-400': hasError }"
|
|
1030
|
+
type="button"
|
|
1031
|
+
@click="start"
|
|
1032
|
+
>
|
|
1033
|
+
{{ line.quantity }}
|
|
1034
|
+
</button>
|
|
1035
|
+
</template>
|
|
1036
|
+
|
|
1037
|
+
<script setup lang="ts">
|
|
1038
|
+
import { computed, ref } from 'vue';
|
|
1039
|
+
import { BbNumberInput } from 'bitboss-ui';
|
|
1040
|
+
import { useInvoiceForm } from './context';
|
|
1041
|
+
import type { LineItemDraft } from '@/types';
|
|
1042
|
+
|
|
1043
|
+
const props = defineProps<{ line: LineItemDraft }>();
|
|
1044
|
+
|
|
1045
|
+
const ctx = useInvoiceForm();
|
|
1046
|
+
|
|
1047
|
+
const editing = ref(false);
|
|
1048
|
+
const value = ref<number | null>(null);
|
|
1049
|
+
|
|
1050
|
+
const hasError = computed(() => !!ctx.lineItemError(props.line, 'quantity'));
|
|
1051
|
+
|
|
1052
|
+
const start = () => {
|
|
1053
|
+
value.value = props.line.quantity;
|
|
1054
|
+
editing.value = true;
|
|
1055
|
+
};
|
|
1056
|
+
|
|
1057
|
+
const commit = () => {
|
|
1058
|
+
if (!editing.value) return;
|
|
1059
|
+
editing.value = false;
|
|
1060
|
+
// An emptied field emits null — treat it as a revert, not a zero.
|
|
1061
|
+
if (value.value === null) return;
|
|
1062
|
+
ctx.updateLineItem(props.line, { quantity: value.value });
|
|
1063
|
+
};
|
|
1064
|
+
|
|
1065
|
+
const revert = () => {
|
|
1066
|
+
editing.value = false;
|
|
1067
|
+
};
|
|
1068
|
+
</script>
|
|
1069
|
+
```
|
|
1070
|
+
|
|
1071
|
+
### EditableRateCell.vue
|
|
1072
|
+
|
|
1073
|
+
```vue
|
|
1074
|
+
<!-- InvoiceForm/EditableRateCell.vue -->
|
|
1075
|
+
<template>
|
|
1076
|
+
<BbNumberInput
|
|
1077
|
+
v-if="editing"
|
|
1078
|
+
v-model="value"
|
|
1079
|
+
autofocus
|
|
1080
|
+
compact
|
|
1081
|
+
:errors="ctx.lineItemError(line, 'unitPrice')"
|
|
1082
|
+
hide-label
|
|
1083
|
+
label="Unit price"
|
|
1084
|
+
:max-precision="2"
|
|
1085
|
+
:min="0"
|
|
1086
|
+
@blur="commit"
|
|
1087
|
+
@keydown.enter.prevent="commit"
|
|
1088
|
+
@keydown.esc="revert"
|
|
1089
|
+
>
|
|
1090
|
+
<template #prefix>$</template>
|
|
1091
|
+
</BbNumberInput>
|
|
1092
|
+
<button
|
|
1093
|
+
v-else
|
|
1094
|
+
class="w-full rounded-[var(--bb-radius)] px-1 py-0.5 text-right tabular-nums hover:bg-[color:var(--bb-hover)]"
|
|
1095
|
+
:class="{ 'text-red-600 dark:text-red-400': hasError }"
|
|
1096
|
+
type="button"
|
|
1097
|
+
@click="start"
|
|
1098
|
+
>
|
|
1099
|
+
{{ currency.format(line.unitPrice) }}
|
|
1100
|
+
</button>
|
|
1101
|
+
</template>
|
|
1102
|
+
|
|
1103
|
+
<script setup lang="ts">
|
|
1104
|
+
import { computed, ref } from 'vue';
|
|
1105
|
+
import { BbNumberInput } from 'bitboss-ui';
|
|
1106
|
+
import { useInvoiceForm } from './context';
|
|
1107
|
+
import type { LineItemDraft } from '@/types';
|
|
1108
|
+
|
|
1109
|
+
const props = defineProps<{ line: LineItemDraft }>();
|
|
1110
|
+
|
|
1111
|
+
const ctx = useInvoiceForm();
|
|
1112
|
+
|
|
1113
|
+
const editing = ref(false);
|
|
1114
|
+
const value = ref<number | null>(null);
|
|
1115
|
+
|
|
1116
|
+
const hasError = computed(() => !!ctx.lineItemError(props.line, 'unitPrice'));
|
|
1117
|
+
|
|
1118
|
+
const currency = new Intl.NumberFormat('en-US', {
|
|
1119
|
+
style: 'currency',
|
|
1120
|
+
currency: 'USD',
|
|
1121
|
+
});
|
|
1122
|
+
|
|
1123
|
+
const start = () => {
|
|
1124
|
+
value.value = props.line.unitPrice;
|
|
1125
|
+
editing.value = true;
|
|
1126
|
+
};
|
|
1127
|
+
|
|
1128
|
+
const commit = () => {
|
|
1129
|
+
if (!editing.value) return;
|
|
1130
|
+
editing.value = false;
|
|
1131
|
+
if (value.value === null) return;
|
|
1132
|
+
ctx.updateLineItem(props.line, { unitPrice: value.value });
|
|
1133
|
+
};
|
|
1134
|
+
|
|
1135
|
+
const revert = () => {
|
|
1136
|
+
editing.value = false;
|
|
1137
|
+
};
|
|
1138
|
+
</script>
|
|
1139
|
+
```
|
|
1140
|
+
|
|
1141
|
+
### NotesSection.vue
|
|
1142
|
+
|
|
1143
|
+
```vue
|
|
1144
|
+
<!-- InvoiceForm/NotesSection.vue -->
|
|
1145
|
+
<template>
|
|
1146
|
+
<section class="flex flex-col gap-2">
|
|
1147
|
+
<!-- BbCollapsible ships no trigger: this button owns the disclosure
|
|
1148
|
+
ARIA (`aria-expanded` + `aria-controls`). -->
|
|
1149
|
+
<BbButton
|
|
1150
|
+
append:icon="lucide:chevron-down"
|
|
1151
|
+
aria-controls="invoice-notes"
|
|
1152
|
+
:aria-expanded="open"
|
|
1153
|
+
class="self-start"
|
|
1154
|
+
variant="ghost"
|
|
1155
|
+
@click="open = !open"
|
|
1156
|
+
>
|
|
1157
|
+
Notes & terms
|
|
1158
|
+
</BbButton>
|
|
1159
|
+
|
|
1160
|
+
<!-- `eager` mounts the fields up front so their 422 errors render even
|
|
1161
|
+
if the user never opened the section. Spacing lives on the inner
|
|
1162
|
+
content — the collapsible zeroes its own box when closed. -->
|
|
1163
|
+
<BbCollapsible id="invoice-notes" v-model="open" eager>
|
|
1164
|
+
<div class="flex flex-col gap-4 pt-1">
|
|
1165
|
+
<BbTextarea
|
|
1166
|
+
v-model="ctx.draft.notes"
|
|
1167
|
+
auto-grow
|
|
1168
|
+
description="Shown on the invoice, under the line items."
|
|
1169
|
+
:errors="ctx.errors.value.notes"
|
|
1170
|
+
label="Notes to the customer"
|
|
1171
|
+
placeholder="Second milestone of the relaunch engagement…"
|
|
1172
|
+
:rows="2"
|
|
1173
|
+
/>
|
|
1174
|
+
<BbTextarea
|
|
1175
|
+
v-model="ctx.draft.terms"
|
|
1176
|
+
auto-grow
|
|
1177
|
+
description="Payment terms printed in the invoice footer."
|
|
1178
|
+
:errors="ctx.errors.value.terms"
|
|
1179
|
+
label="Terms"
|
|
1180
|
+
placeholder="Payment due within 30 days…"
|
|
1181
|
+
:rows="2"
|
|
1182
|
+
/>
|
|
1183
|
+
</div>
|
|
1184
|
+
</BbCollapsible>
|
|
1185
|
+
</section>
|
|
1186
|
+
</template>
|
|
1187
|
+
|
|
1188
|
+
<script setup lang="ts">
|
|
1189
|
+
import { ref } from 'vue';
|
|
1190
|
+
import { BbButton, BbCollapsible, BbTextarea } from 'bitboss-ui';
|
|
1191
|
+
import { useInvoiceForm } from './context';
|
|
1192
|
+
|
|
1193
|
+
const ctx = useInvoiceForm();
|
|
1194
|
+
|
|
1195
|
+
// Rung 1: only this section cares whether it is open. Inertia delivers the
|
|
1196
|
+
// invoice synchronously as a page prop, so seeding from the draft here
|
|
1197
|
+
// starts an annotated invoice open and a blank one closed, same as Vue/Nuxt.
|
|
1198
|
+
const open = ref(ctx.draft.notes !== null || ctx.draft.terms !== null);
|
|
1199
|
+
</script>
|
|
1200
|
+
```
|
|
1201
|
+
|
|
1202
|
+
### FormActions.vue
|
|
1203
|
+
|
|
1204
|
+
```vue
|
|
1205
|
+
<!-- InvoiceForm/FormActions.vue -->
|
|
1206
|
+
<template>
|
|
1207
|
+
<!-- Sticky action row: the commit affordance never scrolls away. Not the
|
|
1208
|
+
floating bottom panel — that is selection chrome, and a form has no
|
|
1209
|
+
selection. -->
|
|
1210
|
+
<div
|
|
1211
|
+
class="sticky bottom-0 z-10 -mx-4 flex items-center gap-3 border-t bg-[color:var(--bb-panel)] px-4 py-3"
|
|
1212
|
+
>
|
|
1213
|
+
<BbButton
|
|
1214
|
+
v-if="ctx.mode === 'edit'"
|
|
1215
|
+
prepend:icon="lucide:trash-2"
|
|
1216
|
+
variant="destructive"
|
|
1217
|
+
@click="ctx.deleteInvoice"
|
|
1218
|
+
>
|
|
1219
|
+
Delete
|
|
1220
|
+
</BbButton>
|
|
1221
|
+
<p
|
|
1222
|
+
v-if="ctx.isDirty.value"
|
|
1223
|
+
class="text-xs text-[color:var(--bb-text-muted)]"
|
|
1224
|
+
>
|
|
1225
|
+
Unsaved changes
|
|
1226
|
+
</p>
|
|
1227
|
+
|
|
1228
|
+
<div class="ml-auto flex items-center gap-2">
|
|
1229
|
+
<!-- Cancel is navigation: the route-leave guard asks about a dirty
|
|
1230
|
+
draft, so no bespoke confirm here. -->
|
|
1231
|
+
<BbButton :disabled="ctx.saving.value" href="/invoices" variant="ghost">
|
|
1232
|
+
Cancel
|
|
1233
|
+
</BbButton>
|
|
1234
|
+
<BbButton
|
|
1235
|
+
v-if="ctx.mode === 'create'"
|
|
1236
|
+
:disabled="ctx.saving.value"
|
|
1237
|
+
variant="outline"
|
|
1238
|
+
@click="ctx.saveAsDraft"
|
|
1239
|
+
>
|
|
1240
|
+
Save as draft
|
|
1241
|
+
</BbButton>
|
|
1242
|
+
<BbButton :loading="ctx.saving.value" type="submit" variant="primary">
|
|
1243
|
+
{{ ctx.mode === 'edit' ? 'Save changes' : 'Create invoice' }}
|
|
1244
|
+
</BbButton>
|
|
1245
|
+
</div>
|
|
1246
|
+
</div>
|
|
1247
|
+
</template>
|
|
1248
|
+
|
|
1249
|
+
<script setup lang="ts">
|
|
1250
|
+
import { BbButton } from 'bitboss-ui';
|
|
1251
|
+
import { useInvoiceForm } from './context';
|
|
1252
|
+
|
|
1253
|
+
const ctx = useInvoiceForm();
|
|
1254
|
+
</script>
|
|
1255
|
+
```
|
|
1256
|
+
|
|
1257
|
+
### CustomerPeek.vue
|
|
1258
|
+
|
|
1259
|
+
```vue
|
|
1260
|
+
<!-- InvoiceForm/CustomerPeek.vue -->
|
|
1261
|
+
<template>
|
|
1262
|
+
<div v-if="customer" class="flex flex-col gap-4 text-sm">
|
|
1263
|
+
<header class="flex items-center gap-3">
|
|
1264
|
+
<BbAvatar :alt="customer.name" :src="customer.avatarUrl" />
|
|
1265
|
+
<div class="flex flex-col">
|
|
1266
|
+
<h2 class="text-base font-semibold">{{ customer.name }}</h2>
|
|
1267
|
+
<p class="text-xs text-[color:var(--bb-text-muted)]">
|
|
1268
|
+
{{ customer.email }} · {{ customer.country }}
|
|
1269
|
+
</p>
|
|
1270
|
+
</div>
|
|
1271
|
+
</header>
|
|
1272
|
+
|
|
1273
|
+
<!-- Stat strip. -->
|
|
1274
|
+
<div class="grid grid-cols-3 gap-2">
|
|
1275
|
+
<div class="rounded-[var(--bb-radius)] border p-2">
|
|
1276
|
+
<p class="text-xs text-[color:var(--bb-text-muted)]">Total billed</p>
|
|
1277
|
+
<p class="font-semibold">{{ currency.format(customer.totalBilled) }}</p>
|
|
1278
|
+
</div>
|
|
1279
|
+
<div class="rounded-[var(--bb-radius)] border p-2">
|
|
1280
|
+
<p class="text-xs text-[color:var(--bb-text-muted)]">Outstanding</p>
|
|
1281
|
+
<p class="font-semibold">{{ currency.format(customer.outstanding) }}</p>
|
|
1282
|
+
</div>
|
|
1283
|
+
<div class="rounded-[var(--bb-radius)] border p-2">
|
|
1284
|
+
<p class="text-xs text-[color:var(--bb-text-muted)]">Invoices</p>
|
|
1285
|
+
<p class="font-semibold">{{ customer.invoiceCount }}</p>
|
|
1286
|
+
</div>
|
|
1287
|
+
</div>
|
|
1288
|
+
|
|
1289
|
+
<!-- Billing profile: exactly the fields this form negotiates. -->
|
|
1290
|
+
<section class="flex flex-col gap-1.5">
|
|
1291
|
+
<h3
|
|
1292
|
+
class="text-xs font-semibold uppercase tracking-wide text-[color:var(--bb-text-muted)]"
|
|
1293
|
+
>
|
|
1294
|
+
Billing profile
|
|
1295
|
+
</h3>
|
|
1296
|
+
<dl class="grid grid-cols-[auto_1fr] gap-x-4 gap-y-1.5">
|
|
1297
|
+
<dt class="text-[color:var(--bb-text-muted)]">Default terms</dt>
|
|
1298
|
+
<dd>{{ customer.defaultPaymentTerms }}</dd>
|
|
1299
|
+
<dt class="text-[color:var(--bb-text-muted)]">Default tax rate</dt>
|
|
1300
|
+
<dd>{{ customer.defaultTaxRate }}%</dd>
|
|
1301
|
+
<dt class="text-[color:var(--bb-text-muted)]">Tax ID</dt>
|
|
1302
|
+
<dd>{{ customer.taxId }}</dd>
|
|
1303
|
+
<dt class="text-[color:var(--bb-text-muted)]">Billing email</dt>
|
|
1304
|
+
<dd>{{ customer.billingEmail }}</dd>
|
|
1305
|
+
</dl>
|
|
1306
|
+
<!-- The peek acts on the form: this writes into the draft. -->
|
|
1307
|
+
<BbButton
|
|
1308
|
+
class="mt-1 self-start"
|
|
1309
|
+
prepend:icon="lucide:sparkles"
|
|
1310
|
+
size="sm"
|
|
1311
|
+
variant="secondary"
|
|
1312
|
+
@click="() => ctx.applyCustomerDefaults(customer!)"
|
|
1313
|
+
>
|
|
1314
|
+
Apply billing defaults
|
|
1315
|
+
</BbButton>
|
|
1316
|
+
</section>
|
|
1317
|
+
|
|
1318
|
+
<!-- Recent invoices: real links — leaving a dirty draft still goes
|
|
1319
|
+
through the route-leave guard. -->
|
|
1320
|
+
<section class="flex flex-col gap-1.5">
|
|
1321
|
+
<h3
|
|
1322
|
+
class="text-xs font-semibold uppercase tracking-wide text-[color:var(--bb-text-muted)]"
|
|
1323
|
+
>
|
|
1324
|
+
Recent invoices
|
|
1325
|
+
</h3>
|
|
1326
|
+
<ul class="flex flex-col">
|
|
1327
|
+
<li
|
|
1328
|
+
v-for="recent in customer.recentInvoices"
|
|
1329
|
+
:key="recent.id"
|
|
1330
|
+
class="flex items-center justify-between gap-2 border-b py-1.5 last:border-b-0"
|
|
1331
|
+
>
|
|
1332
|
+
<BbButton :href="`/invoices/${recent.id}`" size="sm" variant="link">
|
|
1333
|
+
{{ recent.number }}
|
|
1334
|
+
</BbButton>
|
|
1335
|
+
<BbBadge variant="outline">{{ recent.status }}</BbBadge>
|
|
1336
|
+
<span class="text-xs tabular-nums text-[color:var(--bb-text-muted)]">
|
|
1337
|
+
{{ currency.format(recent.amount) }}
|
|
1338
|
+
</span>
|
|
1339
|
+
</li>
|
|
1340
|
+
</ul>
|
|
1341
|
+
</section>
|
|
1342
|
+
|
|
1343
|
+
<footer class="mt-2 flex items-center gap-2 border-t pt-3">
|
|
1344
|
+
<BbButton
|
|
1345
|
+
append:icon="lucide:arrow-up-right"
|
|
1346
|
+
:href="`/customers/${customer.id}`"
|
|
1347
|
+
size="sm"
|
|
1348
|
+
variant="ghost"
|
|
1349
|
+
>
|
|
1350
|
+
Open customer
|
|
1351
|
+
</BbButton>
|
|
1352
|
+
</footer>
|
|
1353
|
+
</div>
|
|
1354
|
+
|
|
1355
|
+
<div v-else class="flex justify-center p-8">
|
|
1356
|
+
<BbSpinner />
|
|
1357
|
+
</div>
|
|
1358
|
+
</template>
|
|
1359
|
+
|
|
1360
|
+
<script setup lang="ts">
|
|
1361
|
+
import { BbAvatar, BbBadge, BbButton, BbSpinner } from 'bitboss-ui';
|
|
1362
|
+
import { useResourceContext } from '@/composables/useResourceContext';
|
|
1363
|
+
import { useInvoiceForm } from './context';
|
|
1364
|
+
import type { CustomerDetail } from '@/types';
|
|
1365
|
+
|
|
1366
|
+
const props = defineProps<{ customerId: number }>();
|
|
1367
|
+
|
|
1368
|
+
const ctx = useInvoiceForm();
|
|
1369
|
+
|
|
1370
|
+
// A small JSON endpoint, not a page visit — opening the peek never
|
|
1371
|
+
// re-renders the form underneath it.
|
|
1372
|
+
const { data: customer } = useResourceContext<CustomerDetail, number>(
|
|
1373
|
+
() => props.customerId,
|
|
1374
|
+
(id) => fetch(`/customers/${id}/peek`).then((res) => res.json())
|
|
1375
|
+
);
|
|
1376
|
+
|
|
1377
|
+
const currency = new Intl.NumberFormat('en-US', {
|
|
1378
|
+
style: 'currency',
|
|
1379
|
+
currency: 'USD',
|
|
1380
|
+
});
|
|
1381
|
+
</script>
|
|
1382
|
+
```
|
|
1383
|
+
|
|
1384
|
+
## Why it is built this way
|
|
1385
|
+
|
|
1386
|
+
- **The context coopts, leaves consume.** The shell is joined once in
|
|
1387
|
+
`provideInvoiceForm()`; ten components share the draft, the errors, the
|
|
1388
|
+
peek stack and every action through one `useInvoiceForm()` call. The only
|
|
1389
|
+
props on the page are data-identity props: the cells' `line` and the
|
|
1390
|
+
peek's `customer-id`. Notably there is **no table context** here — the
|
|
1391
|
+
line-items table has no selection, no paging and no fetch; its entire
|
|
1392
|
+
state _is_ the draft, so there is nothing for a `useBbTableContext`
|
|
1393
|
+
handle to carry.
|
|
1394
|
+
- **The draft is `usePromiseForm`'s own reactive fields.** Cell commits,
|
|
1395
|
+
line add/duplicate/remove and "apply billing defaults" mutate
|
|
1396
|
+
`ctx.draft` exactly like the Vue/Nuxt recipes (still context actions —
|
|
1397
|
+
leaves never reach around it); the only extra step is `form.transform()`
|
|
1398
|
+
inside `persist()`, which shapes the outgoing request (adds `status`,
|
|
1399
|
+
nulls out negative local ids) without mutating the draft the inputs are
|
|
1400
|
+
bound to.
|
|
1401
|
+
- **422 keys are the server's shape, not reshaped.** `form.errors` arrives
|
|
1402
|
+
keyed exactly like Laravel's validation bag (`customerId`,
|
|
1403
|
+
`lineItems.2.unitPrice`) — no manual mapping. Scalar inputs bind
|
|
1404
|
+
`ctx.errors.value.<field>` directly; row errors keep their dotted keys
|
|
1405
|
+
and the cells resolve them through `lineItemError`, which derives the
|
|
1406
|
+
index from the row's id — so the lookup survives the page-search filter
|
|
1407
|
+
reordering what is visible.
|
|
1408
|
+
- **Tied selects trust the machinery.** `dependencies: [draft.customerId]`
|
|
1409
|
+
re-runs the project provider, `enforce-coherence` prunes a stale pick,
|
|
1410
|
+
and both providers honor the prefill contract (return the selected
|
|
1411
|
+
record on component-initiated calls) so a valid selection is never
|
|
1412
|
+
wiped — the exact failure mode the BbSelect guide warns about. The
|
|
1413
|
+
providers themselves are small JSON endpoints (not lazy props), because
|
|
1414
|
+
the customer search is user-typed and the project list is parameterized
|
|
1415
|
+
by the chosen customer.
|
|
1416
|
+
- **The inline-edit grammar is three tiny leaves, not one configurable
|
|
1417
|
+
cell.** Each editable cell owns rung-1 state (`editing`, the in-flight
|
|
1418
|
+
value), commits on blur/Enter and reverts on Esc. Enter is
|
|
1419
|
+
`.prevent`-ed so it commits the cell instead of submitting the enclosing
|
|
1420
|
+
form, and the `editing` guard keeps the unmount blur after an Esc from
|
|
1421
|
+
re-committing the reverted value.
|
|
1422
|
+
- **Dirty tracking is native, so there is no snapshot to maintain.**
|
|
1423
|
+
`form.isDirty` already compares the live draft against its baseline;
|
|
1424
|
+
`form.defaults()` re-takes that baseline after every successful save,
|
|
1425
|
+
the same purpose the Vue/Nuxt JSON snapshot re-take serves. The
|
|
1426
|
+
route-leave guard is `useConfirmOnRouteLeave({ disabled: () =>
|
|
1427
|
+
!form.isDirty, whitelist: [submitUrl] })` — the whitelist is required
|
|
1428
|
+
because submitting the form _is_ a visit, and without it the guard would
|
|
1429
|
+
confirm-block the form's own save.
|
|
1430
|
+
- **No client-side redirects after save.** Unlike the Vue/Nuxt recipes'
|
|
1431
|
+
explicit `router.push`/`navigateTo`, the Laravel controller's redirect is
|
|
1432
|
+
followed inside the same Inertia visit that `post`/`put` performs —
|
|
1433
|
+
`saveInvoice`, `saveAsDraft` and `deleteInvoice` only toast.
|
|
1434
|
+
- **A sticky action row, not the floating bottom panel.** The panel from
|
|
1435
|
+
the guides is selection-driven chrome that comes and goes; a form's
|
|
1436
|
+
commit affordance must be permanently visible, so it is a sticky row on
|
|
1437
|
+
the page surface (`--bb-panel` background, top border) hosting the one
|
|
1438
|
+
`variant="primary"` of the page.
|
|
1439
|
+
- **Sizing:** the form fields are full-height — a form is not a dense
|
|
1440
|
+
surface. `compact` appears where density is real: the topbar search, the
|
|
1441
|
+
line-items table and its cell inputs. `sm` buttons appear only in the
|
|
1442
|
+
table's row actions, the "View customer" link and the peek; everything
|
|
1443
|
+
in the sticky row is default `md`.
|
|
1444
|
+
- **Destructive actions ride `useConfirm`** — delete and the leave guard
|
|
1445
|
+
both — and the peek is a workbench: it acts on the draft and links out
|
|
1446
|
+
with real `href`s (Inertia visits), never `@click` + `router.visit`.
|
|
1447
|
+
|
|
1448
|
+
## Variations
|
|
1449
|
+
|
|
1450
|
+
- **Inside a `BbDialog`** (quick create from the invoices workspace): drop
|
|
1451
|
+
`useConfirmOnRouteLeave` and breadcrumbs, keep `form.isDirty`, confirm on
|
|
1452
|
+
the dialog's close instead, and move the action row into the dialog
|
|
1453
|
+
`#footer` — the context and sections carry over unchanged.
|
|
1454
|
+
- **Autosave drafts**: debounce a watcher on `draft` that calls
|
|
1455
|
+
`persist('draft')` silently and re-takes `form.defaults()` per save; the
|
|
1456
|
+
"Save as draft" button disappears and the sticky row shows "Saved 12s
|
|
1457
|
+
ago".
|
|
1458
|
+
- **Client-side pre-validation**: validate required fields on submit
|
|
1459
|
+
before calling `post`/`put` and merge the messages into `form.errors`
|
|
1460
|
+
via `form.setError(...)` — the inputs cannot tell the difference, which
|
|
1461
|
+
is the point.
|
|
1462
|
+
- **Per-line tax**: add an `EditableTaxCell` per row (its own leaf, per
|
|
1463
|
+
the no-API-monster rule) and group the totals block by rate.
|
|
1464
|
+
- **Send flow**: the primary becomes "Save and send" and opens a
|
|
1465
|
+
`BbDialog` preview (recipient, subject, PDF) whose open-state lives on
|
|
1466
|
+
the context, before persisting with `status: 'pending'`.
|
|
1467
|
+
- **Static option catalogs**: if the customer list is small and identical
|
|
1468
|
+
for every render, swap `fetchCustomers`' JSON endpoint for a lazy prop —
|
|
1469
|
+
`const { getter: fetchCustomers } = useLazyPropGetter('customers')` — see
|
|
1470
|
+
`useLazyPropGetter` in the [Inertia Helpers](../../guides/inertia-helpers.md)
|
|
1471
|
+
guide.
|
|
1472
|
+
- **Per-field persistence**: this page deliberately has no per-cell save
|
|
1473
|
+
feedback because no per-cell save exists — the draft is the mutation
|
|
1474
|
+
boundary and Save is the one commit. When the domain wants every field
|
|
1475
|
+
to persist itself (no Save button), that is the autosave pattern: see
|
|
1476
|
+
the [Inline Edit Playbook](../../guides/inline-edit-playbook.md) and
|
|
1477
|
+
the inline-edit-workspace recipe.
|
|
1478
|
+
|
|
1479
|
+
## Related
|
|
1480
|
+
|
|
1481
|
+
- [Records Workspace (Inertia)](./records-workspace.md) — the listing page
|
|
1482
|
+
this form is reached from.
|
|
1483
|
+
- [Inertia Helpers](../../guides/inertia-helpers.md) — `usePromiseForm`,
|
|
1484
|
+
`useConfirmOnRouteLeave`, `useResourceContext` in full.
|
|
1485
|
+
- [Page Shell](../../guides/page-shell.md) — the app-side layout the
|
|
1486
|
+
customer peek renders inside.
|
|
1487
|
+
- [BbTable](../../BbTable.md), [BbSelect](../../BbSelect.md),
|
|
1488
|
+
[BbCollapsible](../../BbCollapsible.md) — the components this page
|
|
1489
|
+
leans on hardest.
|