bitboss-ui 2.1.135 → 3.0.0-alpha.1
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 +244 -21
- package/bin/bitboss-ui-mcp.mjs +557 -0
- package/bin/bitboss-ui.mjs +626 -0
- package/dist/ai/BbAccordion.md +522 -113
- package/dist/ai/BbAlert.md +377 -197
- package/dist/ai/BbAsterisk.md +46 -0
- package/dist/ai/BbAvatar.md +454 -178
- package/dist/ai/BbBadge.md +695 -225
- package/dist/ai/BbBadgeButton.md +54 -0
- package/dist/ai/BbBaseButton.md +489 -0
- package/dist/ai/BbBaseCheckbox.md +49 -0
- package/dist/ai/BbBaseCheckboxGroup.md +64 -0
- package/dist/ai/BbBaseCheckboxIcon.md +174 -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 +63 -0
- package/dist/ai/BbBaseRadioIcon.md +187 -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 +184 -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 +542 -503
- package/dist/ai/BbButton.md +767 -636
- package/dist/ai/BbCheckbox.md +572 -254
- package/dist/ai/BbCheckboxGroup.md +561 -361
- package/dist/ai/BbCollapsible.md +419 -103
- package/dist/ai/BbColorInput.md +529 -323
- package/dist/ai/BbColorPalette.md +565 -330
- package/dist/ai/BbConfirm.md +612 -136
- package/dist/ai/BbConfirmPortal.md +28 -0
- package/dist/ai/BbDatePicker.md +244 -0
- package/dist/ai/BbDatePickerInput.md +706 -388
- package/dist/ai/BbDialog.md +770 -99
- package/dist/ai/BbDropdown.md +1054 -763
- package/dist/ai/BbDropdownButton.md +511 -615
- package/dist/ai/BbDropdownGroup.md +34 -0
- package/dist/ai/BbDropzone.md +1148 -436
- package/dist/ai/BbForm.md +239 -0
- package/dist/ai/BbIcon.md +379 -212
- package/dist/ai/BbIndicator.md +283 -0
- package/dist/ai/BbNumberInput.md +713 -355
- package/dist/ai/BbOffCanvas.md +875 -498
- package/dist/ai/BbPagination.md +483 -535
- package/dist/ai/BbPopover.md +583 -546
- package/dist/ai/BbProgress.md +456 -69
- package/dist/ai/BbRadio.md +343 -229
- package/dist/ai/BbRadioGroup.md +426 -353
- package/dist/ai/BbRating.md +361 -226
- package/dist/ai/BbSelect.md +1380 -536
- package/dist/ai/BbSelectPopover.md +1240 -1579
- package/dist/ai/BbSlider.md +478 -257
- package/dist/ai/BbSmoothHeight.md +472 -139
- package/dist/ai/BbSpinner.md +718 -131
- package/dist/ai/BbSwitch.md +549 -119
- package/dist/ai/BbSwitchGroup.md +503 -214
- package/dist/ai/BbTable.md +1956 -2358
- package/dist/ai/BbTabs.md +1113 -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 +393 -292
- package/dist/ai/BbTextInput.md +852 -333
- package/dist/ai/BbTextarea.md +836 -255
- package/dist/ai/BbTimePicker.md +231 -0
- package/dist/ai/BbTimePickerInput.md +196 -0
- package/dist/ai/BbToast.md +624 -196
- package/dist/ai/BbToastPortal.md +28 -0
- package/dist/ai/BbTooltip.md +443 -346
- package/dist/ai/BbTree.md +984 -235
- 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 +15883 -0
- package/dist/ai/composables/useBbConfig.md +80 -0
- package/dist/ai/composables/useBbFormContext.md +110 -0
- package/dist/ai/composables/useBbTableContext.md +164 -0
- package/dist/ai/composables/useBbTabsContext.md +118 -0
- package/dist/ai/composables/useBroadcastChannelInstance.md +97 -0
- package/dist/ai/composables/useConfirm.md +306 -0
- package/dist/ai/composables/useCountdown.md +84 -0
- package/dist/ai/composables/useId.md +60 -0
- package/dist/ai/composables/useMobile.md +85 -0
- package/dist/ai/composables/useToast.md +230 -0
- package/dist/ai/guides/agent-contract.md +108 -0
- package/dist/ai/guides/ai-router.md +111 -0
- package/dist/ai/guides/app-layout.md +295 -0
- package/dist/ai/guides/coherence-playbook.md +136 -0
- package/dist/ai/guides/component-picker.md +195 -0
- package/dist/ai/guides/design-language.md +461 -0
- package/dist/ai/guides/design-tokens.md +424 -0
- package/dist/ai/guides/fetch-items-playbook.md +161 -0
- package/dist/ai/guides/floating-bottom-panel.md +191 -0
- package/dist/ai/guides/icons-policy.md +257 -0
- package/dist/ai/guides/inertia-helpers.md +1394 -0
- package/dist/ai/guides/inline-edit-playbook.md +258 -0
- package/dist/ai/guides/installation-and-plugin-setup.md +377 -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-off-canvas.md +25 -0
- package/dist/ai/guides/migration/components/bb-offcanvas.md +54 -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 +58 -0
- package/dist/ai/guides/migration/components/bb-select.md +68 -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 +314 -0
- package/dist/ai/guides/validated-forms.md +177 -0
- package/dist/ai/index.md +156 -32
- package/dist/ai/recipes/inertia/approvals-inbox.md +1598 -0
- package/dist/ai/recipes/inertia/inline-edit-workspace.md +1733 -0
- package/dist/ai/recipes/inertia/onboarding.md +1765 -0
- package/dist/ai/recipes/inertia/ownership-atlas.md +1281 -0
- package/dist/ai/recipes/inertia/record-form.md +1442 -0
- package/dist/ai/recipes/inertia/records-workspace.md +1328 -0
- package/dist/ai/recipes/inertia/upload-center.md +1926 -0
- package/dist/ai/recipes/inertia/wizard-form.md +1477 -0
- package/dist/ai/recipes/nuxt/approvals-inbox.md +1561 -0
- package/dist/ai/recipes/nuxt/inline-edit-workspace.md +1690 -0
- package/dist/ai/recipes/nuxt/onboarding.md +1698 -0
- package/dist/ai/recipes/nuxt/record-form.md +1456 -0
- package/dist/ai/recipes/nuxt/records-workspace.md +1314 -0
- package/dist/ai/recipes/nuxt/upload-center.md +1847 -0
- package/dist/ai/recipes/nuxt/wizard-form.md +1486 -0
- package/dist/ai/recipes/vue/approvals-inbox.md +1479 -0
- package/dist/ai/recipes/vue/inline-edit-workspace.md +1581 -0
- package/dist/ai/recipes/vue/onboarding.md +1653 -0
- package/dist/ai/recipes/vue/record-form.md +1540 -0
- package/dist/ai/recipes/vue/records-workspace.md +1230 -0
- package/dist/ai/recipes/vue/upload-center.md +1806 -0
- package/dist/ai/recipes/vue/wizard-form.md +1418 -0
- package/dist/ai/source/BbAccordion.md +122 -0
- package/dist/ai/source/BbAlert.md +396 -0
- package/dist/ai/source/BbAsterisk.md +32 -0
- package/dist/ai/source/BbAvatar.md +256 -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 +459 -0
- package/dist/ai/source/BbBaseCheckboxGroup.md +554 -0
- package/dist/ai/source/BbBaseCheckboxIcon.md +201 -0
- package/dist/ai/source/BbBaseColorInput.md +413 -0
- package/dist/ai/source/BbBaseColorPalette.md +934 -0
- package/dist/ai/source/BbBaseDatePicker.md +1001 -0
- package/dist/ai/source/BbBaseDatePickerInput.md +1781 -0
- package/dist/ai/source/BbBaseInputContainer.md +1148 -0
- package/dist/ai/source/BbBaseNumberInput.md +615 -0
- package/dist/ai/source/BbBaseRadio.md +377 -0
- package/dist/ai/source/BbBaseRadioGroup.md +499 -0
- package/dist/ai/source/BbBaseRadioIcon.md +153 -0
- package/dist/ai/source/BbBaseRating.md +573 -0
- package/dist/ai/source/BbBaseSelect.md +895 -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 +688 -0
- package/dist/ai/source/BbBaseTextInput.md +511 -0
- package/dist/ai/source/BbBaseTextarea.md +480 -0
- package/dist/ai/source/BbBaseTimePickerInput.md +1013 -0
- package/dist/ai/source/BbBreadcrumbs.md +811 -0
- package/dist/ai/source/BbButton.md +990 -0
- package/dist/ai/source/BbCheckbox.md +319 -0
- package/dist/ai/source/BbCheckboxGroup.md +412 -0
- package/dist/ai/source/BbCollapsible.md +133 -0
- package/dist/ai/source/BbColorInput.md +315 -0
- package/dist/ai/source/BbColorPalette.md +361 -0
- package/dist/ai/source/BbConfirm.md +173 -0
- package/dist/ai/source/BbConfirmPortal.md +74 -0
- package/dist/ai/source/BbDatePicker.md +725 -0
- package/dist/ai/source/BbDatePickerInput.md +518 -0
- package/dist/ai/source/BbDialog.md +812 -0
- package/dist/ai/source/BbDropdown.md +2369 -0
- package/dist/ai/source/BbDropdownButton.md +313 -0
- package/dist/ai/source/BbDropdownGroup.md +1249 -0
- package/dist/ai/source/BbDropzone.md +749 -0
- package/dist/ai/source/BbForm.md +100 -0
- package/dist/ai/source/BbIcon.md +212 -0
- package/dist/ai/source/BbIndicator.md +298 -0
- package/dist/ai/source/BbNumberInput.md +365 -0
- package/dist/ai/source/BbOffCanvas.md +1260 -0
- package/dist/ai/source/BbPagination.md +829 -0
- package/dist/ai/source/BbPopover.md +853 -0
- package/dist/ai/source/BbProgress.md +140 -0
- package/dist/ai/source/BbRadio.md +294 -0
- package/dist/ai/source/BbRadioGroup.md +401 -0
- package/dist/ai/source/BbRating.md +362 -0
- package/dist/ai/source/BbSelect.md +681 -0
- package/dist/ai/source/BbSelectPopover.md +2209 -0
- package/dist/ai/source/BbSlider.md +371 -0
- package/dist/ai/source/BbSmoothHeight.md +261 -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 +3348 -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 +383 -0
- package/dist/ai/source/BbTextInput.md +345 -0
- package/dist/ai/source/BbTextarea.md +330 -0
- package/dist/ai/source/BbTimePicker.md +591 -0
- package/dist/ai/source/BbTimePickerInput.md +367 -0
- package/dist/ai/source/BbToast.md +524 -0
- package/dist/ai/source/BbToastPortal.md +482 -0
- package/dist/ai/source/BbTooltip.md +508 -0
- package/dist/ai/source/BbTree.md +319 -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 +606 -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 +12 -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 +18 -0
- package/dist/button-variants.d.ts +15 -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 +10 -7
- 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 +51 -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 +4 -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 +28 -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 +222 -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 +207 -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 +713 -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 +309 -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 +184 -0
- package/dist/components/BbBaseInputContainer/types.d.ts +185 -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 +211 -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 +172 -0
- package/dist/components/BbBaseRadioGroup/types.d.ts +215 -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 +294 -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 +517 -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 +33 -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 +177 -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 +24 -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 +44 -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 +10 -9
- 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 +37 -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 +125 -0
- package/dist/components/BbColorPalette/types.d.ts +66 -53
- package/dist/components/BbConfirm/BbConfirm.vue.js +6 -0
- package/dist/components/BbConfirm/BbConfirm.vue_vue_type_script_setup_true_lang.js +130 -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 +155 -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 +127 -16
- package/dist/components/BbDialog/BbDialog.vue.d.ts +8 -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 +18 -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 +560 -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 +27 -0
- package/dist/components/BbDropdown/BbDropdownList.vue.js +6 -0
- package/dist/components/BbDropdown/BbDropdownList.vue_vue_type_script_setup_true_lang.js +464 -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 +457 -45
- 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 +45 -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 +284 -0
- package/dist/components/BbDropzone/types.d.ts +55 -7
- 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 +31 -4
- package/dist/components/BbOffCanvas/BbOffCanvas.vue.d.ts +10 -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 +3 -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 +10 -15
- package/dist/components/BbPopover/BbPopover.vue.js +6 -0
- package/dist/components/BbPopover/BbPopover.vue_vue_type_script_setup_true_lang.js +310 -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 +84 -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 +23 -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 +184 -0
- package/dist/components/BbRadioGroup/types.d.ts +41 -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 +37 -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 +105 -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 +637 -0
- package/dist/components/BbSelectPopover/types.d.ts +189 -102
- 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 +37 -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 +13 -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 +53 -31
- package/dist/components/BbTable/BbTable.vue.js +6 -0
- package/dist/components/BbTable/BbTable.vue_vue_type_script_setup_true_lang.js +889 -0
- package/dist/components/BbTable/types.d.ts +176 -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 +48 -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 +24 -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 +26 -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 +129 -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 +165 -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 +35 -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 +4 -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 +61 -11
- package/dist/components/BbTree/BbTree.vue.d.ts +26 -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 +93 -42
- 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 +3 -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 +61 -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.js +5 -0
- package/dist/components/Transitions/ScaleFade.vue_vue_type_script_setup_true_lang.js +47 -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 +219 -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/useCoherence.js +16 -0
- package/dist/composables/useConfirm.d.ts +257 -67
- package/dist/composables/useConfirm.js +122 -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 +103 -0
- package/dist/composables/useSegmentedFields.js +152 -0
- package/dist/composables/useTableWidthContext.d.ts +5 -0
- package/dist/composables/useTableWidthContext.js +42 -0
- package/dist/composables/useToast.d.ts +17 -73
- package/dist/composables/useToast.js +49 -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 +10 -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/dropdown-item-variants.d.ts +10 -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 +94 -79
- package/dist/index.js +76 -82
- package/dist/indicator-variants.d.ts +13 -0
- package/dist/llms-full.txt +77073 -0
- package/dist/llms-medium.txt +1334 -0
- package/dist/llms.txt +103 -0
- package/dist/locale-blueprint.json +141 -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 +13 -0
- package/dist/tooltip-variants.d.ts +10 -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 +66 -279
- 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 +76 -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 +40 -0
- package/dist/validated/BbDatePickerInput.vue.js +5 -0
- package/dist/validated/BbDatePickerInput.vue_vue_type_script_setup_true_lang.js +87 -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 +75 -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 +100 -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 +76 -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 +36 -0
- package/dist/validated/BbTimePickerInput.vue.js +5 -0
- package/dist/validated/BbTimePickerInput.vue_vue_type_script_setup_true_lang.js +78 -0
- package/dist/validated/index.d.ts +29 -0
- package/dist/validated/useValidatedField.d.ts +52 -0
- package/dist/validated/useValidatedField.js +51 -0
- package/dist/validated.js +21 -0
- package/dist/vite-plugin.d.ts +217 -0
- package/dist/vite.js +530 -0
- package/llms.txt +105 -0
- package/package.json +162 -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.mjs +285 -0
- package/scripts/lib/eslint-plugin.node.test.mjs +161 -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 +421 -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/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/{index352.js → directives/createPopoverDirective.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,1540 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Record Form (Vue)
|
|
3
|
+
summary: Full-anatomy invoice create/edit form for a vue-router SPA — sectioned fields with tied selects and 422 error mapping, inline-editing line-item rows, a dirty-snapshot route-leave guard, a sticky action row, and a customer peek sidebar, all through one coopting page context.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Recipe: Record Form (Vue)
|
|
7
|
+
|
|
8
|
+
The form archetype at full standard anatomy for a **plain Vue SPA with
|
|
9
|
+
vue-router**: a Stripe-grade invoice create/edit page — sectioned fields with
|
|
10
|
+
tied selects, server-side 422 error mapping onto every input (including
|
|
11
|
+
line items), inline-editing line-item rows, a dirty-snapshot
|
|
12
|
+
route-leave guard, a sticky action row, and a customer peek workbench, all
|
|
13
|
+
through one coopting page context so every leaf component takes zero props
|
|
14
|
+
beyond the record it renders.
|
|
15
|
+
|
|
16
|
+
Sibling recipes: [Nuxt](../nuxt/record-form.md) ·
|
|
17
|
+
[Inertia](../inertia/record-form.md). The listing this page belongs with is
|
|
18
|
+
[Records Workspace (Vue)](./records-workspace.md).
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
|
|
22
|
+
- `<BbConfirm />` and `<BbToast />` mounted once in the app root.
|
|
23
|
+
- `@/api` exposes `api.invoices.get/create/update/delete`,
|
|
24
|
+
`api.customers.list/byIds/getDetail` and `api.projects.list` — typed
|
|
25
|
+
async functions returning the shapes below; a 422 response rejects with
|
|
26
|
+
`{ status: 422, body: { errors } }`.
|
|
27
|
+
|
|
28
|
+
## Key ideas
|
|
29
|
+
|
|
30
|
+
- **One coopting page context.** `provideInvoiceForm()` joins the
|
|
31
|
+
page-shell stack once, owns the draft, the server errors, the dirty
|
|
32
|
+
snapshot and every action (including the route-leave guard). Every leaf
|
|
33
|
+
calls `useInvoiceForm()` and nothing else — cells, sections, the sticky
|
|
34
|
+
bar and the peek share state with zero prop drilling.
|
|
35
|
+
- **The draft is the page's single mutable surface.** Inline cell edits,
|
|
36
|
+
the peek's "apply billing defaults", add/duplicate/remove line — all of
|
|
37
|
+
it mutates `ctx.draft`. The API boundary is exactly one call: the save,
|
|
38
|
+
whose 422 response maps back onto the same draft's fields.
|
|
39
|
+
- **422 errors reach every input — including line-item rows.** Laravel-style
|
|
40
|
+
keys (`customerId`, `lineItems.2.unitPrice`) land in one `errors` map;
|
|
41
|
+
scalar fields bind `ctx.errors.value.<key>` and the editable cells
|
|
42
|
+
resolve their row key through `ctx.lineItemError(line, field)`.
|
|
43
|
+
- **Tied selects are declarative.** Project depends on customer via
|
|
44
|
+
`dependencies` + `enforce-coherence`; switching customers refetches the
|
|
45
|
+
projects and clears a now-invalid pick automatically. No watchers.
|
|
46
|
+
- **Line items are inline-editing rows (§6.3).** Each row is a
|
|
47
|
+
`LineItemCard` with labeled fields — quantity, unit price, a computed
|
|
48
|
+
amount readout, and a full-width description — one leaf per cell type,
|
|
49
|
+
swapping to an input on click, committing on blur (Enter too on the
|
|
50
|
+
number cells) and reverting on Esc.
|
|
51
|
+
- **Dirty state is a JSON snapshot,** re-baselined before every navigation
|
|
52
|
+
the page itself causes (save redirect, delete redirect), so the
|
|
53
|
+
route-leave confirm only fires on genuinely abandoned edits.
|
|
54
|
+
- **Clicking the customer's name peeks the customer** — a workbench, not a
|
|
55
|
+
preview: billing profile, stats, recent invoices, and an action that
|
|
56
|
+
writes the customer's defaults back into the draft.
|
|
57
|
+
- **Buttons are `md` by default;** `sm` only in the line-item rows and
|
|
58
|
+
the peek. One `variant="primary"`: the save. No filter rail, no tabs, no
|
|
59
|
+
bulk bar — the form archetype has no list to filter and no selection.
|
|
60
|
+
|
|
61
|
+
## Page anatomy
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
InvoiceForm/
|
|
65
|
+
Index.vue ← assembly: PageShell slots, header, <form>, sections
|
|
66
|
+
context.ts ← coopting page context (shell + draft + errors + actions + leave guard)
|
|
67
|
+
DetailsSection.vue ← tied selects, dates, terms, PO, tax rate
|
|
68
|
+
LineItemsSection.vue ← line-item rows + add line + live totals
|
|
69
|
+
LineItemCard.vue ← one row: labeled inline-edit cells + duplicate/remove
|
|
70
|
+
EditableDescriptionCell.vue ← inline textarea edit (commit on blur, Esc reverts)
|
|
71
|
+
EditableQuantityCell.vue ← inline quantity edit with outer stepper buttons
|
|
72
|
+
EditableRateCell.vue ← inline unit-price edit, currency-formatted display
|
|
73
|
+
NotesSection.vue ← BbCollapsible disclosure over notes + terms
|
|
74
|
+
FormActions.vue ← sticky action row: delete / cancel / save draft / save
|
|
75
|
+
CustomerPeek.vue ← sidebar workbench for the picked customer
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Chrome assumed from the guides: [App Layout](../../guides/app-layout.md)
|
|
79
|
+
(left nav + fat footer) and [Page Shell](../../guides/page-shell.md)
|
|
80
|
+
(topbar / content / stacked sidebar). The floating bottom panel is
|
|
81
|
+
deliberately absent — it is selection chrome, and a form has no selection.
|
|
82
|
+
|
|
83
|
+
## Action inventory
|
|
84
|
+
|
|
85
|
+
| # | Action | Trigger | Mechanism |
|
|
86
|
+
| --- | ------------------------------- | ------------------------------------- | ------------------------------------------------------ |
|
|
87
|
+
| 1 | Save invoice | sticky bar, the page's one `primary` | `type="submit"` → `persist()` + 422 mapping + redirect |
|
|
88
|
+
| 2 | Save as draft | sticky bar outline (create mode) | `persist('draft')`, URL swaps to edit in place |
|
|
89
|
+
| 3 | Delete invoice | sticky bar destructive (edit mode) | `useConfirm` → API → toast → redirect |
|
|
90
|
+
| 4 | Cancel | sticky bar ghost | navigation (`to`); leave guard intercepts if dirty |
|
|
91
|
+
| 5 | Pick customer | details section select | query-aware `items` provider |
|
|
92
|
+
| 6 | Pick project | details section select | `dependencies` + `enforce-coherence` (§ tied selects) |
|
|
93
|
+
| 7 | Peek customer | "View customer" link under the select | `ctx.peekCustomer()` → shell stack |
|
|
94
|
+
| 8 | Apply billing defaults | peek workbench button | writes terms + tax rate into the draft, toast |
|
|
95
|
+
| 9 | Edit description inline | line-item cell | §6.3 with `BbTextarea`: commit on blur, Esc reverts |
|
|
96
|
+
| 10 | Edit quantity / rate inline | line-item cells | §6.3 with `BbNumberInput` + outer stepper buttons |
|
|
97
|
+
| 11 | Add line item | section header + empty state | `ctx.addLineItem()` pushes a blank draft row |
|
|
98
|
+
| 12 | Duplicate line | row actions | `ctx.duplicateLineItem(line)` inserts a copy below |
|
|
99
|
+
| 13 | Remove line | row actions | `ctx.removeLineItem(line)` |
|
|
100
|
+
| 14 | Search line items | topbar page search | `lineItemSearch` → `visibleLineItems` filter |
|
|
101
|
+
| 15 | Toggle notes & terms | section disclosure button | `BbCollapsible` + `aria-expanded`/`aria-controls` |
|
|
102
|
+
| 16 | Open customer / recent invoices | peek links | real `to` navigation (leave guard applies) |
|
|
103
|
+
|
|
104
|
+
## The code
|
|
105
|
+
|
|
106
|
+
### The shapes
|
|
107
|
+
|
|
108
|
+
What `@/types` contributes and what `api.invoices.get(2107)` returns — the
|
|
109
|
+
record every block below is grounded in:
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
// @/types (excerpt)
|
|
113
|
+
export type InvoiceStatus = 'draft' | 'pending' | 'paid' | 'overdue';
|
|
114
|
+
|
|
115
|
+
export interface LineItemDraft {
|
|
116
|
+
/** Server id; unsaved rows carry a negative local id. */
|
|
117
|
+
id: number;
|
|
118
|
+
description: string;
|
|
119
|
+
quantity: number;
|
|
120
|
+
unitPrice: number;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface InvoiceDraft {
|
|
124
|
+
customerId: number | null;
|
|
125
|
+
projectId: number | null;
|
|
126
|
+
/** `YYYY-MM-DD` (the date inputs run with `floating`). */
|
|
127
|
+
issuedAt: string | null;
|
|
128
|
+
dueDate: string | null;
|
|
129
|
+
paymentTerms: string | null;
|
|
130
|
+
poNumber: string | null;
|
|
131
|
+
taxRate: number | null;
|
|
132
|
+
lineItems: LineItemDraft[];
|
|
133
|
+
notes: string | null;
|
|
134
|
+
terms: string | null;
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
// The invoice being edited (api.invoices.get(2107))
|
|
140
|
+
{
|
|
141
|
+
id: 2107,
|
|
142
|
+
number: 'INV-2107',
|
|
143
|
+
status: 'draft',
|
|
144
|
+
customerId: 18, // Northwind Traders
|
|
145
|
+
projectId: 112, // Website relaunch
|
|
146
|
+
issuedAt: '2026-07-01',
|
|
147
|
+
dueDate: '2026-07-31',
|
|
148
|
+
paymentTerms: 'Net 30',
|
|
149
|
+
poNumber: 'PO-2026-0641',
|
|
150
|
+
taxRate: 8.875,
|
|
151
|
+
lineItems: [
|
|
152
|
+
{ id: 9001, description: 'Design sprint — homepage & navigation', quantity: 3, unitPrice: 1850 },
|
|
153
|
+
{ id: 9002, description: 'Frontend build — catalogue & product views', quantity: 5, unitPrice: 1200 },
|
|
154
|
+
{ id: 9003, description: 'CMS integration & content migration', quantity: 2, unitPrice: 1400 },
|
|
155
|
+
{ id: 9004, description: 'QA & cross-browser pass', quantity: 1, unitPrice: 950 },
|
|
156
|
+
{ id: 9005, description: 'Staging infrastructure (June)', quantity: 1, unitPrice: 240 },
|
|
157
|
+
],
|
|
158
|
+
notes: 'Second milestone of the relaunch engagement. Hosting billed at cost.',
|
|
159
|
+
terms: 'Payment due within 30 days. 1.5% monthly interest on overdue balances.',
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### context.ts
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
// InvoiceForm/context.ts
|
|
167
|
+
import {
|
|
168
|
+
computed,
|
|
169
|
+
inject,
|
|
170
|
+
provide,
|
|
171
|
+
reactive,
|
|
172
|
+
ref,
|
|
173
|
+
type ComputedRef,
|
|
174
|
+
type InjectionKey,
|
|
175
|
+
type Ref,
|
|
176
|
+
} from 'vue';
|
|
177
|
+
import { onBeforeRouteLeave, useRoute, useRouter } from 'vue-router';
|
|
178
|
+
import { useConfirm, useToast, type BadgeVariantType } from 'bitboss-ui';
|
|
179
|
+
import {
|
|
180
|
+
usePageShellContext,
|
|
181
|
+
type PageShellContext,
|
|
182
|
+
} from '@/components/layout/usePageShellContext';
|
|
183
|
+
import { api } from '@/api';
|
|
184
|
+
import type {
|
|
185
|
+
Customer,
|
|
186
|
+
CustomerDetail,
|
|
187
|
+
Invoice,
|
|
188
|
+
InvoiceDraft,
|
|
189
|
+
InvoiceStatus,
|
|
190
|
+
LineItemDraft,
|
|
191
|
+
Project,
|
|
192
|
+
} from '@/types';
|
|
193
|
+
|
|
194
|
+
/** What the stacked sidebar can show. Clicking the customer's name pushes one. */
|
|
195
|
+
export type PeekItem = { type: 'customer'; id: number };
|
|
196
|
+
|
|
197
|
+
// Status is a small closed set — the textbook case for the soft accents
|
|
198
|
+
// (design-language.md § Using color; the `soft-*` variants are registered
|
|
199
|
+
// app-side). A badge takes its color from the VARIANT map and its label from
|
|
200
|
+
// the LABEL map; it never renders the raw enum. Used by the customer peek's
|
|
201
|
+
// recent-invoice list.
|
|
202
|
+
export const INVOICE_STATUS_LABEL: Record<InvoiceStatus, string> = {
|
|
203
|
+
draft: 'Draft',
|
|
204
|
+
pending: 'Pending',
|
|
205
|
+
paid: 'Paid',
|
|
206
|
+
overdue: 'Overdue',
|
|
207
|
+
};
|
|
208
|
+
export const INVOICE_STATUS_VARIANT: Record<InvoiceStatus, BadgeVariantType> = {
|
|
209
|
+
draft: 'soft-gray',
|
|
210
|
+
pending: 'soft-amber',
|
|
211
|
+
paid: 'soft-green',
|
|
212
|
+
overdue: 'soft-red',
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
type LineItemField = 'description' | 'quantity' | 'unitPrice';
|
|
216
|
+
|
|
217
|
+
export interface InvoiceFormContext {
|
|
218
|
+
// -- Coopted contexts ------------------------------------------------------
|
|
219
|
+
shell: PageShellContext<PeekItem>;
|
|
220
|
+
// -- Page-owned state ------------------------------------------------------
|
|
221
|
+
mode: 'create' | 'edit';
|
|
222
|
+
loaded: Ref<boolean>;
|
|
223
|
+
invoiceNumber: Ref<string | null>;
|
|
224
|
+
status: Ref<InvoiceStatus>;
|
|
225
|
+
draft: InvoiceDraft;
|
|
226
|
+
/** Server 422 errors, keyed Laravel-style (`customerId`, `lineItems.2.unitPrice`). */
|
|
227
|
+
errors: Ref<Record<string, string[]>>;
|
|
228
|
+
saving: Ref<boolean>;
|
|
229
|
+
isDirty: ComputedRef<boolean>;
|
|
230
|
+
lineItemSearch: Ref<string>;
|
|
231
|
+
visibleLineItems: ComputedRef<LineItemDraft[]>;
|
|
232
|
+
subtotal: ComputedRef<number>;
|
|
233
|
+
taxAmount: ComputedRef<number>;
|
|
234
|
+
total: ComputedRef<number>;
|
|
235
|
+
// -- Item providers (handed straight to the selects) ------------------------
|
|
236
|
+
fetchCustomers: (query: string, prefill: boolean) => Promise<Customer[]>;
|
|
237
|
+
fetchProjects: () => Promise<Project[]>;
|
|
238
|
+
// -- Row-level error lookup --------------------------------------------------
|
|
239
|
+
lineItemError: (
|
|
240
|
+
line: LineItemDraft,
|
|
241
|
+
field: LineItemField
|
|
242
|
+
) => string[] | undefined;
|
|
243
|
+
// -- Draft mutations (the cells and the peek write through these) -----------
|
|
244
|
+
updateLineItem: (
|
|
245
|
+
line: LineItemDraft,
|
|
246
|
+
patch: Partial<Omit<LineItemDraft, 'id'>>
|
|
247
|
+
) => void;
|
|
248
|
+
addLineItem: () => void;
|
|
249
|
+
duplicateLineItem: (line: LineItemDraft) => void;
|
|
250
|
+
removeLineItem: (line: LineItemDraft) => void;
|
|
251
|
+
applyCustomerDefaults: (customer: CustomerDetail) => void;
|
|
252
|
+
// -- Peek grammar ------------------------------------------------------------
|
|
253
|
+
peekCustomer: () => void;
|
|
254
|
+
// -- Actions (complete: API + 422 mapping + toast + redirect) ----------------
|
|
255
|
+
saveInvoice: () => Promise<void>;
|
|
256
|
+
saveAsDraft: () => Promise<void>;
|
|
257
|
+
deleteInvoice: () => Promise<void>;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const key: InjectionKey<InvoiceFormContext> = Symbol('invoice-form');
|
|
261
|
+
|
|
262
|
+
export function provideInvoiceForm(): InvoiceFormContext {
|
|
263
|
+
const route = useRoute();
|
|
264
|
+
const router = useRouter();
|
|
265
|
+
const { toast } = useToast();
|
|
266
|
+
const { confirm } = useConfirm();
|
|
267
|
+
|
|
268
|
+
// -- Coopt the shell: once, here, for the whole page. ------------------------
|
|
269
|
+
const shell = usePageShellContext<PeekItem>();
|
|
270
|
+
|
|
271
|
+
// /invoices/create has no id; /invoices/:id/edit does.
|
|
272
|
+
//
|
|
273
|
+
// NOT `const`: both routes render THIS component, so the create→edit
|
|
274
|
+
// `router.replace` in `saveAsDraft` patches the existing instance rather than
|
|
275
|
+
// remounting it — setup never runs again. Promote them by hand there, or
|
|
276
|
+
// `mode` stays 'create' forever and a second "Save as draft" creates a
|
|
277
|
+
// SECOND invoice.
|
|
278
|
+
let invoiceId = route.params.id ? Number(route.params.id) : null;
|
|
279
|
+
let mode: 'create' | 'edit' = invoiceId === null ? 'create' : 'edit';
|
|
280
|
+
|
|
281
|
+
// -- The draft ---------------------------------------------------------------
|
|
282
|
+
// Unsaved rows get negative local ids so `item-value` stays stable and the
|
|
283
|
+
// payload can tell the server which lines are new (negative → null).
|
|
284
|
+
let nextLocalId = -1;
|
|
285
|
+
const blankLineItem = (): LineItemDraft => ({
|
|
286
|
+
id: nextLocalId--,
|
|
287
|
+
description: '',
|
|
288
|
+
quantity: 1,
|
|
289
|
+
unitPrice: 0,
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
const draft = reactive<InvoiceDraft>({
|
|
293
|
+
customerId: null,
|
|
294
|
+
projectId: null,
|
|
295
|
+
issuedAt: null,
|
|
296
|
+
dueDate: null,
|
|
297
|
+
paymentTerms: null,
|
|
298
|
+
poNumber: null,
|
|
299
|
+
taxRate: null,
|
|
300
|
+
lineItems: [blankLineItem()],
|
|
301
|
+
notes: null,
|
|
302
|
+
terms: null,
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
const loaded = ref(mode === 'create');
|
|
306
|
+
const invoiceNumber = ref<string | null>(null);
|
|
307
|
+
const status = ref<InvoiceStatus>('draft');
|
|
308
|
+
const errors = ref<Record<string, string[]>>({});
|
|
309
|
+
const saving = ref(false);
|
|
310
|
+
|
|
311
|
+
// Dirty = the draft's JSON diverged from the last committed snapshot.
|
|
312
|
+
// Gated on `loaded` so the async seed of edit mode never reads as dirty.
|
|
313
|
+
const snapshot = ref(JSON.stringify(draft));
|
|
314
|
+
const isDirty = computed(
|
|
315
|
+
() => loaded.value && JSON.stringify(draft) !== snapshot.value
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
if (mode === 'edit' && invoiceId !== null) {
|
|
319
|
+
api.invoices.get(invoiceId).then((invoice: Invoice) => {
|
|
320
|
+
invoiceNumber.value = invoice.number;
|
|
321
|
+
status.value = invoice.status;
|
|
322
|
+
draft.customerId = invoice.customerId;
|
|
323
|
+
draft.projectId = invoice.projectId;
|
|
324
|
+
draft.issuedAt = invoice.issuedAt;
|
|
325
|
+
draft.dueDate = invoice.dueDate;
|
|
326
|
+
draft.paymentTerms = invoice.paymentTerms;
|
|
327
|
+
draft.poNumber = invoice.poNumber;
|
|
328
|
+
draft.taxRate = invoice.taxRate;
|
|
329
|
+
draft.lineItems = invoice.lineItems.map((line) => ({ ...line }));
|
|
330
|
+
draft.notes = invoice.notes;
|
|
331
|
+
draft.terms = invoice.terms;
|
|
332
|
+
snapshot.value = JSON.stringify(draft);
|
|
333
|
+
loaded.value = true;
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// -- Item providers -----------------------------------------------------------
|
|
338
|
+
// Component-initiated calls (`prefill === true`) must keep the current
|
|
339
|
+
// selection resolvable, or the chip loses its text.
|
|
340
|
+
const fetchCustomers = async (
|
|
341
|
+
query: string,
|
|
342
|
+
prefill: boolean
|
|
343
|
+
): Promise<Customer[]> => {
|
|
344
|
+
if (prefill && draft.customerId !== null) {
|
|
345
|
+
return api.customers.byIds([draft.customerId]);
|
|
346
|
+
}
|
|
347
|
+
return api.customers.list({ search: query });
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
// Re-run by the project select's `dependencies: [draft.customerId]`; the
|
|
351
|
+
// response always contains the customer's full project list, so an existing
|
|
352
|
+
// `projectId` stays resolvable and `enforce-coherence` never wipes a valid pick.
|
|
353
|
+
const fetchProjects = (): Promise<Project[]> =>
|
|
354
|
+
draft.customerId === null
|
|
355
|
+
? Promise.resolve([])
|
|
356
|
+
: api.projects.list({ customer: draft.customerId });
|
|
357
|
+
|
|
358
|
+
// -- Line items -----------------------------------------------------------------
|
|
359
|
+
const lineItemSearch = ref('');
|
|
360
|
+
const visibleLineItems = computed(() => {
|
|
361
|
+
// A `clearable` BbTextInput emits **null** when it is emptied, not `''` —
|
|
362
|
+
// so coalesce before calling a string method, or the first clear throws
|
|
363
|
+
// `Cannot read properties of null (reading 'trim')` and the section stops
|
|
364
|
+
// re-rendering.
|
|
365
|
+
const query = (lineItemSearch.value ?? '').trim().toLowerCase();
|
|
366
|
+
if (!query) return draft.lineItems;
|
|
367
|
+
return draft.lineItems.filter((line) =>
|
|
368
|
+
line.description.toLowerCase().includes(query)
|
|
369
|
+
);
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
const subtotal = computed(() =>
|
|
373
|
+
draft.lineItems.reduce(
|
|
374
|
+
(sum, line) => sum + line.quantity * line.unitPrice,
|
|
375
|
+
0
|
|
376
|
+
)
|
|
377
|
+
);
|
|
378
|
+
const taxAmount = computed(
|
|
379
|
+
() => subtotal.value * ((draft.taxRate ?? 0) / 100)
|
|
380
|
+
);
|
|
381
|
+
const total = computed(() => subtotal.value + taxAmount.value);
|
|
382
|
+
|
|
383
|
+
// Server 422 keys index lines by payload position, which is draft order —
|
|
384
|
+
// resolve a row's key through its id so the lookup survives filtering.
|
|
385
|
+
const lineItemError = (
|
|
386
|
+
line: LineItemDraft,
|
|
387
|
+
field: LineItemField
|
|
388
|
+
): string[] | undefined => {
|
|
389
|
+
const index = draft.lineItems.findIndex(
|
|
390
|
+
(candidate) => candidate.id === line.id
|
|
391
|
+
);
|
|
392
|
+
return errors.value[`lineItems.${index}.${field}`];
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
const updateLineItem = (
|
|
396
|
+
line: LineItemDraft,
|
|
397
|
+
patch: Partial<Omit<LineItemDraft, 'id'>>
|
|
398
|
+
) => {
|
|
399
|
+
const target = draft.lineItems.find(
|
|
400
|
+
(candidate) => candidate.id === line.id
|
|
401
|
+
);
|
|
402
|
+
if (target) Object.assign(target, patch);
|
|
403
|
+
};
|
|
404
|
+
const addLineItem = () => {
|
|
405
|
+
draft.lineItems.push(blankLineItem());
|
|
406
|
+
};
|
|
407
|
+
const duplicateLineItem = (line: LineItemDraft) => {
|
|
408
|
+
const index = draft.lineItems.findIndex(
|
|
409
|
+
(candidate) => candidate.id === line.id
|
|
410
|
+
);
|
|
411
|
+
draft.lineItems.splice(index + 1, 0, { ...line, id: nextLocalId-- });
|
|
412
|
+
};
|
|
413
|
+
const removeLineItem = (line: LineItemDraft) => {
|
|
414
|
+
draft.lineItems = draft.lineItems.filter(
|
|
415
|
+
(candidate) => candidate.id !== line.id
|
|
416
|
+
);
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
// The peek writes back into the draft: the sidebar is a workbench.
|
|
420
|
+
const applyCustomerDefaults = (customer: CustomerDetail) => {
|
|
421
|
+
draft.paymentTerms = customer.defaultPaymentTerms;
|
|
422
|
+
draft.taxRate = customer.defaultTaxRate;
|
|
423
|
+
toast({
|
|
424
|
+
title: `Applied ${customer.name}'s billing defaults.`,
|
|
425
|
+
variant: 'success',
|
|
426
|
+
});
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
// -- Peek grammar ----------------------------------------------------------------
|
|
430
|
+
const peekCustomer = () => {
|
|
431
|
+
if (draft.customerId === null) return;
|
|
432
|
+
shell.push({ type: 'customer', id: draft.customerId }, true);
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
// -- Persistence -----------------------------------------------------------------
|
|
436
|
+
// New lines travel with `id: null`; the server assigns real ids on save.
|
|
437
|
+
const payload = (nextStatus: InvoiceStatus) => ({
|
|
438
|
+
...draft,
|
|
439
|
+
status: nextStatus,
|
|
440
|
+
lineItems: draft.lineItems.map((line) => ({
|
|
441
|
+
...line,
|
|
442
|
+
id: line.id > 0 ? line.id : null,
|
|
443
|
+
})),
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
const persist = async (
|
|
447
|
+
nextStatus: InvoiceStatus
|
|
448
|
+
): Promise<Invoice | null> => {
|
|
449
|
+
saving.value = true;
|
|
450
|
+
errors.value = {};
|
|
451
|
+
try {
|
|
452
|
+
const saved =
|
|
453
|
+
mode === 'edit' && invoiceId !== null
|
|
454
|
+
? await api.invoices.update(invoiceId, payload(nextStatus))
|
|
455
|
+
: await api.invoices.create(payload(nextStatus));
|
|
456
|
+
// Re-baseline BEFORE navigating so the leave guard lets the redirect through.
|
|
457
|
+
snapshot.value = JSON.stringify(draft);
|
|
458
|
+
status.value = saved.status;
|
|
459
|
+
invoiceNumber.value = saved.number;
|
|
460
|
+
return saved;
|
|
461
|
+
} catch (error: unknown) {
|
|
462
|
+
const err = error as {
|
|
463
|
+
status?: number;
|
|
464
|
+
body?: { errors?: Record<string, string[]> };
|
|
465
|
+
};
|
|
466
|
+
if (err.status === 422) {
|
|
467
|
+
// Laravel-style: { errors: { 'lineItems.2.unitPrice': ['Must be positive.'] } }
|
|
468
|
+
errors.value = err.body?.errors ?? {};
|
|
469
|
+
toast({ title: 'Some fields need attention.', variant: 'destructive' });
|
|
470
|
+
} else {
|
|
471
|
+
toast({
|
|
472
|
+
title: 'Something went wrong. Try again.',
|
|
473
|
+
variant: 'destructive',
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
return null;
|
|
477
|
+
} finally {
|
|
478
|
+
saving.value = false;
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
const saveInvoice = async () => {
|
|
483
|
+
// Creating finalizes the invoice; editing keeps whatever status it has.
|
|
484
|
+
const saved = await persist(mode === 'edit' ? status.value : 'pending');
|
|
485
|
+
if (!saved) return;
|
|
486
|
+
toast({ title: `Invoice ${saved.number} saved.`, variant: 'success' });
|
|
487
|
+
router.push(`/invoices/${saved.id}`);
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
const saveAsDraft = async () => {
|
|
491
|
+
const saved = await persist('draft');
|
|
492
|
+
if (!saved) return;
|
|
493
|
+
toast({ title: `Draft ${saved.number} saved.`, variant: 'success' });
|
|
494
|
+
// Stay on the page; swap the URL so a refresh lands in edit mode. The
|
|
495
|
+
// component is patched, not remounted, so promote this instance too.
|
|
496
|
+
if (mode === 'create') {
|
|
497
|
+
await router.replace(`/invoices/${saved.id}/edit`);
|
|
498
|
+
invoiceId = saved.id;
|
|
499
|
+
mode = 'edit';
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
const deleteInvoice = async () => {
|
|
504
|
+
if (mode !== 'edit' || invoiceId === null) return;
|
|
505
|
+
const ok = await confirm({
|
|
506
|
+
variant: 'destructive',
|
|
507
|
+
title: 'Delete invoice',
|
|
508
|
+
text: `Delete ${invoiceNumber.value}? This cannot be undone.`,
|
|
509
|
+
yes: 'Delete',
|
|
510
|
+
no: 'Cancel',
|
|
511
|
+
});
|
|
512
|
+
if (!ok) return;
|
|
513
|
+
await api.invoices.delete(invoiceId);
|
|
514
|
+
// Re-baseline so the guard doesn't ask about a record that no longer exists.
|
|
515
|
+
snapshot.value = JSON.stringify(draft);
|
|
516
|
+
toast({ title: `${invoiceNumber.value} deleted.`, variant: 'success' });
|
|
517
|
+
router.push('/invoices');
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
// -- Route-leave guard ---------------------------------------------------------
|
|
521
|
+
// Lives with the state it protects. `onBeforeRouteLeave` registers against
|
|
522
|
+
// the page component because provideInvoiceForm() runs in its setup.
|
|
523
|
+
onBeforeRouteLeave(async () => {
|
|
524
|
+
if (!isDirty.value) return true;
|
|
525
|
+
return await confirm({
|
|
526
|
+
variant: 'destructive',
|
|
527
|
+
title: 'Unsaved changes',
|
|
528
|
+
text: 'You have unsaved changes on this invoice. Leave without saving?',
|
|
529
|
+
yes: 'Discard changes',
|
|
530
|
+
no: 'Keep editing',
|
|
531
|
+
});
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
const ctx: InvoiceFormContext = {
|
|
535
|
+
shell,
|
|
536
|
+
mode,
|
|
537
|
+
loaded,
|
|
538
|
+
invoiceNumber,
|
|
539
|
+
status,
|
|
540
|
+
draft,
|
|
541
|
+
errors,
|
|
542
|
+
saving,
|
|
543
|
+
isDirty,
|
|
544
|
+
lineItemSearch,
|
|
545
|
+
visibleLineItems,
|
|
546
|
+
subtotal,
|
|
547
|
+
taxAmount,
|
|
548
|
+
total,
|
|
549
|
+
fetchCustomers,
|
|
550
|
+
fetchProjects,
|
|
551
|
+
lineItemError,
|
|
552
|
+
updateLineItem,
|
|
553
|
+
addLineItem,
|
|
554
|
+
duplicateLineItem,
|
|
555
|
+
removeLineItem,
|
|
556
|
+
applyCustomerDefaults,
|
|
557
|
+
peekCustomer,
|
|
558
|
+
saveInvoice,
|
|
559
|
+
saveAsDraft,
|
|
560
|
+
deleteInvoice,
|
|
561
|
+
};
|
|
562
|
+
provide(key, ctx);
|
|
563
|
+
return ctx;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
export function useInvoiceForm(): InvoiceFormContext {
|
|
567
|
+
const ctx = inject(key);
|
|
568
|
+
if (!ctx) throw new Error('useInvoiceForm() called outside InvoiceForm');
|
|
569
|
+
return ctx;
|
|
570
|
+
}
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
### Index.vue — the assembly
|
|
574
|
+
|
|
575
|
+
```vue
|
|
576
|
+
<!-- InvoiceForm/Index.vue -->
|
|
577
|
+
<template>
|
|
578
|
+
<PageShell>
|
|
579
|
+
<!-- Topbar: breadcrumbs left, page search right. The search scopes the
|
|
580
|
+
work surface — here, the line items. -->
|
|
581
|
+
<template #topbar>
|
|
582
|
+
<div class="flex items-center justify-between gap-4 border-b px-4 py-2">
|
|
583
|
+
<!-- Breadcrumbs take the row's slack on a stable basis (min-w-0 flex-auto);
|
|
584
|
+
without it their overflow recompute fights the flex layout every frame. -->
|
|
585
|
+
<BbBreadcrumbs class="min-w-0 flex-auto" :items="trail" />
|
|
586
|
+
<BbTextInput
|
|
587
|
+
v-model="ctx.lineItemSearch.value"
|
|
588
|
+
class="shrink-0"
|
|
589
|
+
clearable
|
|
590
|
+
compact
|
|
591
|
+
hide-label
|
|
592
|
+
label="Search line items"
|
|
593
|
+
placeholder="Search line items…"
|
|
594
|
+
prepend:icon="lucide:search"
|
|
595
|
+
/>
|
|
596
|
+
</div>
|
|
597
|
+
</template>
|
|
598
|
+
|
|
599
|
+
<div
|
|
600
|
+
v-if="ctx.loaded.value"
|
|
601
|
+
class="mx-auto flex w-full max-w-4xl flex-col gap-6 p-4"
|
|
602
|
+
>
|
|
603
|
+
<header class="flex items-center gap-3">
|
|
604
|
+
<h1 class="text-lg font-semibold">
|
|
605
|
+
{{
|
|
606
|
+
ctx.mode === 'edit'
|
|
607
|
+
? `Edit ${ctx.invoiceNumber.value}`
|
|
608
|
+
: 'New invoice'
|
|
609
|
+
}}
|
|
610
|
+
</h1>
|
|
611
|
+
<BbBadge v-if="ctx.mode === 'edit'" variant="outline">{{
|
|
612
|
+
ctx.status.value
|
|
613
|
+
}}</BbBadge>
|
|
614
|
+
</header>
|
|
615
|
+
|
|
616
|
+
<!-- One form, one submit. `novalidate` keeps error UX on the inputs'
|
|
617
|
+
`errors` surface instead of browser bubbles; `autocomplete="off"`
|
|
618
|
+
(echoed on every field) keeps browser autofill off the draft. -->
|
|
619
|
+
<form
|
|
620
|
+
autocomplete="off"
|
|
621
|
+
class="flex flex-col gap-6"
|
|
622
|
+
novalidate
|
|
623
|
+
@submit.prevent="ctx.saveInvoice"
|
|
624
|
+
>
|
|
625
|
+
<DetailsSection />
|
|
626
|
+
<LineItemsSection />
|
|
627
|
+
<NotesSection />
|
|
628
|
+
<FormActions />
|
|
629
|
+
</form>
|
|
630
|
+
</div>
|
|
631
|
+
|
|
632
|
+
<div v-else class="flex justify-center p-12">
|
|
633
|
+
<BbSpinner label="Loading invoice…" />
|
|
634
|
+
</div>
|
|
635
|
+
|
|
636
|
+
<!-- Stacked peek sidebar: the shell owns back/close; we render by type. -->
|
|
637
|
+
<template #sidebar>
|
|
638
|
+
<CustomerPeek
|
|
639
|
+
v-if="ctx.shell.current.value?.type === 'customer'"
|
|
640
|
+
:customer-id="ctx.shell.current.value.id"
|
|
641
|
+
/>
|
|
642
|
+
</template>
|
|
643
|
+
</PageShell>
|
|
644
|
+
</template>
|
|
645
|
+
|
|
646
|
+
<script setup lang="ts">
|
|
647
|
+
import { computed } from 'vue';
|
|
648
|
+
import { BbBadge, BbBreadcrumbs, BbSpinner, BbTextInput } from 'bitboss-ui';
|
|
649
|
+
import PageShell from '@/components/layout/PageShell.vue';
|
|
650
|
+
import { provideInvoiceForm } from './context';
|
|
651
|
+
import CustomerPeek from './CustomerPeek.vue';
|
|
652
|
+
import DetailsSection from './DetailsSection.vue';
|
|
653
|
+
import FormActions from './FormActions.vue';
|
|
654
|
+
import LineItemsSection from './LineItemsSection.vue';
|
|
655
|
+
import NotesSection from './NotesSection.vue';
|
|
656
|
+
|
|
657
|
+
const ctx = provideInvoiceForm();
|
|
658
|
+
|
|
659
|
+
// A real trail, ≥ 2 linked levels; the current page carries no target.
|
|
660
|
+
const trail = computed(() => [
|
|
661
|
+
{ key: 'dashboard', text: 'Dashboard', to: '/dashboard' },
|
|
662
|
+
{ key: 'billing', text: 'Billing', to: '/billing' },
|
|
663
|
+
{ key: 'invoices', text: 'Invoices', to: '/invoices' },
|
|
664
|
+
{
|
|
665
|
+
key: 'invoice',
|
|
666
|
+
text:
|
|
667
|
+
ctx.mode === 'edit'
|
|
668
|
+
? (ctx.invoiceNumber.value ?? 'Invoice')
|
|
669
|
+
: 'New invoice',
|
|
670
|
+
},
|
|
671
|
+
]);
|
|
672
|
+
</script>
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
### DetailsSection.vue
|
|
676
|
+
|
|
677
|
+
```vue
|
|
678
|
+
<!-- InvoiceForm/DetailsSection.vue -->
|
|
679
|
+
<template>
|
|
680
|
+
<section class="flex flex-col gap-3">
|
|
681
|
+
<h2 class="text-xs font-semibold uppercase tracking-wide text-gray-500">
|
|
682
|
+
Details
|
|
683
|
+
</h2>
|
|
684
|
+
|
|
685
|
+
<div class="grid gap-4 sm:grid-cols-2">
|
|
686
|
+
<!-- Tied selects: project depends on customer. `dependencies` re-runs
|
|
687
|
+
the project provider; `enforce-coherence` clears a project that is
|
|
688
|
+
no longer among the new options. No watchers. -->
|
|
689
|
+
<div class="flex flex-col gap-1">
|
|
690
|
+
<BbSelect
|
|
691
|
+
v-model="ctx.draft.customerId"
|
|
692
|
+
autocomplete="off"
|
|
693
|
+
:errors="ctx.errors.value.customerId"
|
|
694
|
+
item-text="name"
|
|
695
|
+
item-value="id"
|
|
696
|
+
:items="ctx.fetchCustomers"
|
|
697
|
+
label="Customer"
|
|
698
|
+
placeholder="Search customers…"
|
|
699
|
+
/>
|
|
700
|
+
<!-- Clicking the NAME of a thing peeks it. -->
|
|
701
|
+
<BbButton
|
|
702
|
+
v-if="ctx.draft.customerId !== null"
|
|
703
|
+
append:icon="lucide:panel-right-open"
|
|
704
|
+
class="self-start"
|
|
705
|
+
size="sm"
|
|
706
|
+
variant="link"
|
|
707
|
+
@click="ctx.peekCustomer"
|
|
708
|
+
>
|
|
709
|
+
View customer
|
|
710
|
+
</BbButton>
|
|
711
|
+
</div>
|
|
712
|
+
|
|
713
|
+
<BbSelect
|
|
714
|
+
v-model="ctx.draft.projectId"
|
|
715
|
+
autocomplete="off"
|
|
716
|
+
:dependencies="[ctx.draft.customerId]"
|
|
717
|
+
:disabled="ctx.draft.customerId === null"
|
|
718
|
+
enforce-coherence
|
|
719
|
+
:errors="ctx.errors.value.projectId"
|
|
720
|
+
item-text="name"
|
|
721
|
+
item-value="id"
|
|
722
|
+
:items="ctx.fetchProjects"
|
|
723
|
+
label="Project"
|
|
724
|
+
placeholder="Pick the customer first"
|
|
725
|
+
/>
|
|
726
|
+
|
|
727
|
+
<!-- `floating` keeps the model a plain YYYY-MM-DD string, so the due
|
|
728
|
+
date's `min` can be fed straight from the issue date. -->
|
|
729
|
+
<BbDatePickerInput
|
|
730
|
+
v-model="ctx.draft.issuedAt"
|
|
731
|
+
autocomplete="off"
|
|
732
|
+
:errors="ctx.errors.value.issuedAt"
|
|
733
|
+
floating
|
|
734
|
+
label="Issue date"
|
|
735
|
+
prepend:icon="lucide:calendar"
|
|
736
|
+
/>
|
|
737
|
+
<BbDatePickerInput
|
|
738
|
+
v-model="ctx.draft.dueDate"
|
|
739
|
+
autocomplete="off"
|
|
740
|
+
:errors="ctx.errors.value.dueDate"
|
|
741
|
+
floating
|
|
742
|
+
label="Due date"
|
|
743
|
+
:min="ctx.draft.issuedAt ?? undefined"
|
|
744
|
+
prepend:icon="lucide:calendar"
|
|
745
|
+
/>
|
|
746
|
+
|
|
747
|
+
<BbSelect
|
|
748
|
+
v-model="ctx.draft.paymentTerms"
|
|
749
|
+
autocomplete="off"
|
|
750
|
+
:errors="ctx.errors.value.paymentTerms"
|
|
751
|
+
:items="['Due on receipt', 'Net 15', 'Net 30', 'Net 60']"
|
|
752
|
+
label="Payment terms"
|
|
753
|
+
placeholder="Choose terms"
|
|
754
|
+
/>
|
|
755
|
+
|
|
756
|
+
<BbTextInput
|
|
757
|
+
v-model="ctx.draft.poNumber"
|
|
758
|
+
autocomplete="off"
|
|
759
|
+
:errors="ctx.errors.value.poNumber"
|
|
760
|
+
label="PO / reference"
|
|
761
|
+
placeholder="PO-2026-0641"
|
|
762
|
+
/>
|
|
763
|
+
|
|
764
|
+
<BbNumberInput
|
|
765
|
+
v-model="ctx.draft.taxRate"
|
|
766
|
+
autocomplete="off"
|
|
767
|
+
:errors="ctx.errors.value.taxRate"
|
|
768
|
+
hint="Applied to the subtotal below."
|
|
769
|
+
label="Tax rate"
|
|
770
|
+
:max="100"
|
|
771
|
+
:max-precision="3"
|
|
772
|
+
:min="0"
|
|
773
|
+
>
|
|
774
|
+
<template #suffix>%</template>
|
|
775
|
+
</BbNumberInput>
|
|
776
|
+
</div>
|
|
777
|
+
</section>
|
|
778
|
+
</template>
|
|
779
|
+
|
|
780
|
+
<script setup lang="ts">
|
|
781
|
+
import {
|
|
782
|
+
BbButton,
|
|
783
|
+
BbDatePickerInput,
|
|
784
|
+
BbNumberInput,
|
|
785
|
+
BbSelect,
|
|
786
|
+
BbTextInput,
|
|
787
|
+
} from 'bitboss-ui';
|
|
788
|
+
import { useInvoiceForm } from './context';
|
|
789
|
+
|
|
790
|
+
const ctx = useInvoiceForm();
|
|
791
|
+
</script>
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
### LineItemsSection.vue
|
|
795
|
+
|
|
796
|
+
```vue
|
|
797
|
+
<!-- InvoiceForm/LineItemsSection.vue -->
|
|
798
|
+
<template>
|
|
799
|
+
<section class="flex flex-col gap-3">
|
|
800
|
+
<div class="flex items-center justify-between">
|
|
801
|
+
<h2 class="text-xs font-semibold uppercase tracking-wide text-gray-500">
|
|
802
|
+
Line items
|
|
803
|
+
</h2>
|
|
804
|
+
<BbButton
|
|
805
|
+
prepend:icon="lucide:plus"
|
|
806
|
+
variant="outline"
|
|
807
|
+
@click="ctx.addLineItem"
|
|
808
|
+
>
|
|
809
|
+
Add line item
|
|
810
|
+
</BbButton>
|
|
811
|
+
</div>
|
|
812
|
+
|
|
813
|
+
<!-- One bordered panel whose rows are divided by hairlines — not a
|
|
814
|
+
BbTable (no selection, paging or fetch) and not a stack of floating
|
|
815
|
+
cards. Each row keeps the same §6.3 inline-edit cells per field. -->
|
|
816
|
+
<div
|
|
817
|
+
v-if="ctx.visibleLineItems.value.length"
|
|
818
|
+
class="flex flex-col rounded-[var(--bb-radius)] border"
|
|
819
|
+
>
|
|
820
|
+
<LineItemCard
|
|
821
|
+
v-for="line in ctx.visibleLineItems.value"
|
|
822
|
+
:key="line.id"
|
|
823
|
+
:line="line"
|
|
824
|
+
/>
|
|
825
|
+
</div>
|
|
826
|
+
|
|
827
|
+
<!-- Soft empty band — a wash, not a dashed outline. -->
|
|
828
|
+
<div
|
|
829
|
+
v-else
|
|
830
|
+
class="flex flex-col items-center gap-2 rounded-[var(--bb-radius)] bg-gray-50 p-6 text-center dark:bg-gray-900"
|
|
831
|
+
>
|
|
832
|
+
<p class="text-sm text-gray-500">
|
|
833
|
+
{{
|
|
834
|
+
ctx.lineItemSearch.value
|
|
835
|
+
? 'No line items match your search.'
|
|
836
|
+
: 'No line items yet.'
|
|
837
|
+
}}
|
|
838
|
+
</p>
|
|
839
|
+
<BbButton
|
|
840
|
+
v-if="!ctx.lineItemSearch.value"
|
|
841
|
+
variant="outline"
|
|
842
|
+
@click="ctx.addLineItem"
|
|
843
|
+
>
|
|
844
|
+
Add the first line
|
|
845
|
+
</BbButton>
|
|
846
|
+
</div>
|
|
847
|
+
|
|
848
|
+
<!-- Totals: live computed from the draft — they move as cells commit. -->
|
|
849
|
+
<dl class="ml-auto grid w-60 grid-cols-[1fr_auto] gap-y-1 text-sm">
|
|
850
|
+
<dt class="text-gray-500">Subtotal</dt>
|
|
851
|
+
<dd class="text-right tabular-nums">
|
|
852
|
+
{{ currency.format(ctx.subtotal.value) }}
|
|
853
|
+
</dd>
|
|
854
|
+
<dt class="text-gray-500">Tax ({{ ctx.draft.taxRate ?? 0 }}%)</dt>
|
|
855
|
+
<dd class="text-right tabular-nums">
|
|
856
|
+
{{ currency.format(ctx.taxAmount.value) }}
|
|
857
|
+
</dd>
|
|
858
|
+
<dt class="font-semibold">Total</dt>
|
|
859
|
+
<dd class="text-right font-semibold tabular-nums">
|
|
860
|
+
{{ currency.format(ctx.total.value) }}
|
|
861
|
+
</dd>
|
|
862
|
+
</dl>
|
|
863
|
+
</section>
|
|
864
|
+
</template>
|
|
865
|
+
|
|
866
|
+
<script setup lang="ts">
|
|
867
|
+
import { BbButton } from 'bitboss-ui';
|
|
868
|
+
import LineItemCard from './LineItemCard.vue';
|
|
869
|
+
import { useInvoiceForm } from './context';
|
|
870
|
+
|
|
871
|
+
const ctx = useInvoiceForm();
|
|
872
|
+
|
|
873
|
+
const currency = new Intl.NumberFormat('en-US', {
|
|
874
|
+
style: 'currency',
|
|
875
|
+
currency: 'USD',
|
|
876
|
+
});
|
|
877
|
+
</script>
|
|
878
|
+
```
|
|
879
|
+
|
|
880
|
+
### LineItemCard.vue
|
|
881
|
+
|
|
882
|
+
```vue
|
|
883
|
+
<!-- InvoiceForm/LineItemCard.vue -->
|
|
884
|
+
<template>
|
|
885
|
+
<article class="flex flex-col gap-3 border-b p-3 last:border-b-0">
|
|
886
|
+
<div class="flex items-end gap-3">
|
|
887
|
+
<div class="grid min-w-0 flex-1 grid-cols-3 gap-3">
|
|
888
|
+
<div>
|
|
889
|
+
<p
|
|
890
|
+
class="mb-1 text-right text-xs font-semibold uppercase tracking-wide text-gray-500"
|
|
891
|
+
>
|
|
892
|
+
Qty
|
|
893
|
+
</p>
|
|
894
|
+
<EditableQuantityCell :line="line" />
|
|
895
|
+
</div>
|
|
896
|
+
<div>
|
|
897
|
+
<p
|
|
898
|
+
class="mb-1 text-right text-xs font-semibold uppercase tracking-wide text-gray-500"
|
|
899
|
+
>
|
|
900
|
+
Unit price
|
|
901
|
+
</p>
|
|
902
|
+
<EditableRateCell :line="line" />
|
|
903
|
+
</div>
|
|
904
|
+
<div>
|
|
905
|
+
<p
|
|
906
|
+
class="mb-1 text-right text-xs font-semibold uppercase tracking-wide text-gray-500"
|
|
907
|
+
>
|
|
908
|
+
Amount
|
|
909
|
+
</p>
|
|
910
|
+
<!-- Virtual field: computed from the row, never edited. -->
|
|
911
|
+
<p class="px-1 py-0.5 text-right tabular-nums">
|
|
912
|
+
{{ currency.format(line.quantity * line.unitPrice) }}
|
|
913
|
+
</p>
|
|
914
|
+
</div>
|
|
915
|
+
</div>
|
|
916
|
+
|
|
917
|
+
<div class="flex shrink-0 items-center gap-1">
|
|
918
|
+
<BbButton
|
|
919
|
+
icon="lucide:copy"
|
|
920
|
+
size="sm"
|
|
921
|
+
variant="ghost"
|
|
922
|
+
@click="() => ctx.duplicateLineItem(line)"
|
|
923
|
+
>
|
|
924
|
+
Duplicate line
|
|
925
|
+
</BbButton>
|
|
926
|
+
<BbButton
|
|
927
|
+
icon="lucide:trash-2"
|
|
928
|
+
size="sm"
|
|
929
|
+
variant="ghost"
|
|
930
|
+
@click="() => ctx.removeLineItem(line)"
|
|
931
|
+
>
|
|
932
|
+
Remove line
|
|
933
|
+
</BbButton>
|
|
934
|
+
</div>
|
|
935
|
+
</div>
|
|
936
|
+
|
|
937
|
+
<div>
|
|
938
|
+
<p
|
|
939
|
+
class="mb-1 text-xs font-semibold uppercase tracking-wide text-gray-500"
|
|
940
|
+
>
|
|
941
|
+
Description
|
|
942
|
+
</p>
|
|
943
|
+
<EditableDescriptionCell :line="line" />
|
|
944
|
+
</div>
|
|
945
|
+
</article>
|
|
946
|
+
</template>
|
|
947
|
+
|
|
948
|
+
<script setup lang="ts">
|
|
949
|
+
import { BbButton } from 'bitboss-ui';
|
|
950
|
+
import EditableDescriptionCell from './EditableDescriptionCell.vue';
|
|
951
|
+
import EditableQuantityCell from './EditableQuantityCell.vue';
|
|
952
|
+
import EditableRateCell from './EditableRateCell.vue';
|
|
953
|
+
import { useInvoiceForm } from './context';
|
|
954
|
+
import type { LineItemDraft } from '@/types';
|
|
955
|
+
|
|
956
|
+
defineProps<{ line: LineItemDraft }>();
|
|
957
|
+
|
|
958
|
+
const ctx = useInvoiceForm();
|
|
959
|
+
|
|
960
|
+
const currency = new Intl.NumberFormat('en-US', {
|
|
961
|
+
style: 'currency',
|
|
962
|
+
currency: 'USD',
|
|
963
|
+
});
|
|
964
|
+
</script>
|
|
965
|
+
```
|
|
966
|
+
|
|
967
|
+
### EditableDescriptionCell.vue
|
|
968
|
+
|
|
969
|
+
```vue
|
|
970
|
+
<!-- InvoiceForm/EditableDescriptionCell.vue -->
|
|
971
|
+
<template>
|
|
972
|
+
<BbTextarea
|
|
973
|
+
v-if="editing"
|
|
974
|
+
ref="inputRef"
|
|
975
|
+
v-model="value"
|
|
976
|
+
auto-grow
|
|
977
|
+
compact
|
|
978
|
+
:errors="ctx.lineItemError(line, 'description')"
|
|
979
|
+
hide-label
|
|
980
|
+
label="Line description"
|
|
981
|
+
placeholder="What was delivered?"
|
|
982
|
+
:rows="2"
|
|
983
|
+
@blur="commit"
|
|
984
|
+
@keydown.esc="revert"
|
|
985
|
+
/>
|
|
986
|
+
<button
|
|
987
|
+
v-else
|
|
988
|
+
class="w-full whitespace-pre-wrap rounded-[var(--bb-radius)] px-1 py-0.5 text-left hover:bg-gray-100 dark:hover:bg-gray-800"
|
|
989
|
+
:class="{
|
|
990
|
+
'text-[color:var(--bb-text-muted)]': !line.description,
|
|
991
|
+
'text-[color:var(--bb-danger)]': hasError,
|
|
992
|
+
}"
|
|
993
|
+
type="button"
|
|
994
|
+
@click="start"
|
|
995
|
+
>
|
|
996
|
+
{{ line.description || 'Add a description…' }}
|
|
997
|
+
</button>
|
|
998
|
+
</template>
|
|
999
|
+
|
|
1000
|
+
<script setup lang="ts">
|
|
1001
|
+
import { computed, nextTick, ref } from 'vue';
|
|
1002
|
+
import { BbTextarea } from 'bitboss-ui';
|
|
1003
|
+
import { useInvoiceForm } from './context';
|
|
1004
|
+
import type { LineItemDraft } from '@/types';
|
|
1005
|
+
|
|
1006
|
+
const props = defineProps<{ line: LineItemDraft }>();
|
|
1007
|
+
|
|
1008
|
+
const ctx = useInvoiceForm();
|
|
1009
|
+
|
|
1010
|
+
// Rung 1 of the ladder: only this cell reads or writes its editing state.
|
|
1011
|
+
const editing = ref(false);
|
|
1012
|
+
const value = ref<string | null>(null);
|
|
1013
|
+
const inputRef = ref<{ $el?: HTMLElement } | null>(null);
|
|
1014
|
+
|
|
1015
|
+
const hasError = computed(() => !!ctx.lineItemError(props.line, 'description'));
|
|
1016
|
+
|
|
1017
|
+
/** Focus the native textarea after the editor mounts — HTML `autofocus` only
|
|
1018
|
+
* runs on page load, not on later mounts. */
|
|
1019
|
+
const focusInput = async () => {
|
|
1020
|
+
await nextTick();
|
|
1021
|
+
inputRef.value?.$el?.querySelector('textarea')?.focus();
|
|
1022
|
+
};
|
|
1023
|
+
|
|
1024
|
+
const start = () => {
|
|
1025
|
+
value.value = props.line.description;
|
|
1026
|
+
editing.value = true;
|
|
1027
|
+
void focusInput();
|
|
1028
|
+
};
|
|
1029
|
+
|
|
1030
|
+
// Commit on blur, revert on Esc (§6.3). Enter inserts a newline (textarea),
|
|
1031
|
+
// so it is not wired to commit the way a single-line input would be; the
|
|
1032
|
+
// `editing` guard keeps the blur that follows an Esc from re-committing.
|
|
1033
|
+
const commit = () => {
|
|
1034
|
+
if (!editing.value) return;
|
|
1035
|
+
editing.value = false;
|
|
1036
|
+
ctx.updateLineItem(props.line, { description: value.value ?? '' });
|
|
1037
|
+
};
|
|
1038
|
+
|
|
1039
|
+
const revert = () => {
|
|
1040
|
+
editing.value = false;
|
|
1041
|
+
};
|
|
1042
|
+
</script>
|
|
1043
|
+
```
|
|
1044
|
+
|
|
1045
|
+
### EditableQuantityCell.vue
|
|
1046
|
+
|
|
1047
|
+
```vue
|
|
1048
|
+
<!-- InvoiceForm/EditableQuantityCell.vue -->
|
|
1049
|
+
<template>
|
|
1050
|
+
<BbNumberInput
|
|
1051
|
+
v-if="editing"
|
|
1052
|
+
ref="inputRef"
|
|
1053
|
+
v-model="value"
|
|
1054
|
+
class="[&_input]:text-right"
|
|
1055
|
+
compact
|
|
1056
|
+
:errors="ctx.lineItemError(line, 'quantity')"
|
|
1057
|
+
hide-label
|
|
1058
|
+
label="Quantity"
|
|
1059
|
+
:max-precision="2"
|
|
1060
|
+
:min="0"
|
|
1061
|
+
@blur="commit"
|
|
1062
|
+
@keydown.enter.prevent="commit"
|
|
1063
|
+
@keydown.esc="revert"
|
|
1064
|
+
>
|
|
1065
|
+
<!-- Outer steppers: mousedown.prevent keeps focus in the field so blur
|
|
1066
|
+
doesn't commit/unmount before the click lands. -->
|
|
1067
|
+
<template #prepend-outer="{ decrease }">
|
|
1068
|
+
<BbButton
|
|
1069
|
+
aria-label="Decrease quantity"
|
|
1070
|
+
icon="lucide:minus"
|
|
1071
|
+
size="xs"
|
|
1072
|
+
variant="ghost"
|
|
1073
|
+
@click="decrease"
|
|
1074
|
+
@mousedown.prevent
|
|
1075
|
+
/>
|
|
1076
|
+
</template>
|
|
1077
|
+
<template #append-outer="{ increase }">
|
|
1078
|
+
<BbButton
|
|
1079
|
+
aria-label="Increase quantity"
|
|
1080
|
+
icon="lucide:plus"
|
|
1081
|
+
size="xs"
|
|
1082
|
+
variant="ghost"
|
|
1083
|
+
@click="increase"
|
|
1084
|
+
@mousedown.prevent
|
|
1085
|
+
/>
|
|
1086
|
+
</template>
|
|
1087
|
+
</BbNumberInput>
|
|
1088
|
+
<button
|
|
1089
|
+
v-else
|
|
1090
|
+
class="w-full rounded-[var(--bb-radius)] px-1 py-0.5 text-right tabular-nums hover:bg-gray-100 dark:hover:bg-gray-800"
|
|
1091
|
+
:class="{ 'text-[color:var(--bb-danger)]': hasError }"
|
|
1092
|
+
type="button"
|
|
1093
|
+
@click="start"
|
|
1094
|
+
>
|
|
1095
|
+
{{ line.quantity }}
|
|
1096
|
+
</button>
|
|
1097
|
+
</template>
|
|
1098
|
+
|
|
1099
|
+
<script setup lang="ts">
|
|
1100
|
+
import { computed, nextTick, ref } from 'vue';
|
|
1101
|
+
import { BbButton, BbNumberInput } from 'bitboss-ui';
|
|
1102
|
+
import { useInvoiceForm } from './context';
|
|
1103
|
+
import type { LineItemDraft } from '@/types';
|
|
1104
|
+
|
|
1105
|
+
const props = defineProps<{ line: LineItemDraft }>();
|
|
1106
|
+
|
|
1107
|
+
const ctx = useInvoiceForm();
|
|
1108
|
+
|
|
1109
|
+
const editing = ref(false);
|
|
1110
|
+
const value = ref<number | null>(null);
|
|
1111
|
+
const inputRef = ref<{ $el?: HTMLElement } | null>(null);
|
|
1112
|
+
|
|
1113
|
+
const hasError = computed(() => !!ctx.lineItemError(props.line, 'quantity'));
|
|
1114
|
+
|
|
1115
|
+
/** Focus the native input after the editor mounts — HTML `autofocus` only
|
|
1116
|
+
* runs on page load, not on later mounts. */
|
|
1117
|
+
const focusInput = async () => {
|
|
1118
|
+
await nextTick();
|
|
1119
|
+
inputRef.value?.$el?.querySelector('input')?.focus();
|
|
1120
|
+
};
|
|
1121
|
+
|
|
1122
|
+
const start = () => {
|
|
1123
|
+
value.value = props.line.quantity;
|
|
1124
|
+
editing.value = true;
|
|
1125
|
+
void focusInput();
|
|
1126
|
+
};
|
|
1127
|
+
|
|
1128
|
+
const commit = () => {
|
|
1129
|
+
if (!editing.value) return;
|
|
1130
|
+
editing.value = false;
|
|
1131
|
+
// An emptied field emits null — treat it as a revert, not a zero.
|
|
1132
|
+
if (value.value === null) return;
|
|
1133
|
+
ctx.updateLineItem(props.line, { quantity: value.value });
|
|
1134
|
+
};
|
|
1135
|
+
|
|
1136
|
+
const revert = () => {
|
|
1137
|
+
editing.value = false;
|
|
1138
|
+
};
|
|
1139
|
+
</script>
|
|
1140
|
+
```
|
|
1141
|
+
|
|
1142
|
+
### EditableRateCell.vue
|
|
1143
|
+
|
|
1144
|
+
```vue
|
|
1145
|
+
<!-- InvoiceForm/EditableRateCell.vue -->
|
|
1146
|
+
<template>
|
|
1147
|
+
<BbNumberInput
|
|
1148
|
+
v-if="editing"
|
|
1149
|
+
ref="inputRef"
|
|
1150
|
+
v-model="value"
|
|
1151
|
+
class="[&_input]:text-right"
|
|
1152
|
+
compact
|
|
1153
|
+
:errors="ctx.lineItemError(line, 'unitPrice')"
|
|
1154
|
+
hide-label
|
|
1155
|
+
label="Unit price"
|
|
1156
|
+
:max-precision="2"
|
|
1157
|
+
:min="0"
|
|
1158
|
+
@blur="commit"
|
|
1159
|
+
@keydown.enter.prevent="commit"
|
|
1160
|
+
@keydown.esc="revert"
|
|
1161
|
+
>
|
|
1162
|
+
<template #prefix>$</template>
|
|
1163
|
+
</BbNumberInput>
|
|
1164
|
+
<button
|
|
1165
|
+
v-else
|
|
1166
|
+
class="w-full rounded-[var(--bb-radius)] px-1 py-0.5 text-right tabular-nums hover:bg-gray-100 dark:hover:bg-gray-800"
|
|
1167
|
+
:class="{ 'text-[color:var(--bb-danger)]': hasError }"
|
|
1168
|
+
type="button"
|
|
1169
|
+
@click="start"
|
|
1170
|
+
>
|
|
1171
|
+
{{ currency.format(line.unitPrice) }}
|
|
1172
|
+
</button>
|
|
1173
|
+
</template>
|
|
1174
|
+
|
|
1175
|
+
<script setup lang="ts">
|
|
1176
|
+
import { computed, nextTick, ref } from 'vue';
|
|
1177
|
+
import { BbNumberInput } from 'bitboss-ui';
|
|
1178
|
+
import { useInvoiceForm } from './context';
|
|
1179
|
+
import type { LineItemDraft } from '@/types';
|
|
1180
|
+
|
|
1181
|
+
const props = defineProps<{ line: LineItemDraft }>();
|
|
1182
|
+
|
|
1183
|
+
const ctx = useInvoiceForm();
|
|
1184
|
+
|
|
1185
|
+
const editing = ref(false);
|
|
1186
|
+
const value = ref<number | null>(null);
|
|
1187
|
+
const inputRef = ref<{ $el?: HTMLElement } | null>(null);
|
|
1188
|
+
|
|
1189
|
+
const hasError = computed(() => !!ctx.lineItemError(props.line, 'unitPrice'));
|
|
1190
|
+
|
|
1191
|
+
const currency = new Intl.NumberFormat('en-US', {
|
|
1192
|
+
style: 'currency',
|
|
1193
|
+
currency: 'USD',
|
|
1194
|
+
});
|
|
1195
|
+
|
|
1196
|
+
/** Focus the native input after the editor mounts — HTML `autofocus` only
|
|
1197
|
+
* runs on page load, not on later mounts. */
|
|
1198
|
+
const focusInput = async () => {
|
|
1199
|
+
await nextTick();
|
|
1200
|
+
inputRef.value?.$el?.querySelector('input')?.focus();
|
|
1201
|
+
};
|
|
1202
|
+
|
|
1203
|
+
const start = () => {
|
|
1204
|
+
value.value = props.line.unitPrice;
|
|
1205
|
+
editing.value = true;
|
|
1206
|
+
void focusInput();
|
|
1207
|
+
};
|
|
1208
|
+
|
|
1209
|
+
const commit = () => {
|
|
1210
|
+
if (!editing.value) return;
|
|
1211
|
+
editing.value = false;
|
|
1212
|
+
if (value.value === null) return;
|
|
1213
|
+
ctx.updateLineItem(props.line, { unitPrice: value.value });
|
|
1214
|
+
};
|
|
1215
|
+
|
|
1216
|
+
const revert = () => {
|
|
1217
|
+
editing.value = false;
|
|
1218
|
+
};
|
|
1219
|
+
</script>
|
|
1220
|
+
```
|
|
1221
|
+
|
|
1222
|
+
### NotesSection.vue
|
|
1223
|
+
|
|
1224
|
+
```vue
|
|
1225
|
+
<!-- InvoiceForm/NotesSection.vue -->
|
|
1226
|
+
<template>
|
|
1227
|
+
<section class="flex flex-col gap-2">
|
|
1228
|
+
<!-- BbCollapsible ships no trigger: this button owns the disclosure
|
|
1229
|
+
ARIA (`aria-expanded` + `aria-controls`). -->
|
|
1230
|
+
<BbButton
|
|
1231
|
+
append:icon="lucide:chevron-down"
|
|
1232
|
+
aria-controls="invoice-notes"
|
|
1233
|
+
:aria-expanded="open"
|
|
1234
|
+
class="self-start"
|
|
1235
|
+
variant="ghost"
|
|
1236
|
+
@click="open = !open"
|
|
1237
|
+
>
|
|
1238
|
+
Notes & terms
|
|
1239
|
+
</BbButton>
|
|
1240
|
+
|
|
1241
|
+
<!-- `eager` mounts the fields up front so their 422 errors render even
|
|
1242
|
+
if the user never opened the section. Spacing lives on the inner
|
|
1243
|
+
content — the collapsible zeroes its own box when closed. -->
|
|
1244
|
+
<BbCollapsible id="invoice-notes" v-model="open" eager>
|
|
1245
|
+
<div class="flex flex-col gap-4 pt-1">
|
|
1246
|
+
<BbTextarea
|
|
1247
|
+
v-model="ctx.draft.notes"
|
|
1248
|
+
auto-grow
|
|
1249
|
+
description="Shown on the invoice, under the line items."
|
|
1250
|
+
:errors="ctx.errors.value.notes"
|
|
1251
|
+
label="Notes to the customer"
|
|
1252
|
+
placeholder="Second milestone of the relaunch engagement…"
|
|
1253
|
+
:rows="2"
|
|
1254
|
+
/>
|
|
1255
|
+
<BbTextarea
|
|
1256
|
+
v-model="ctx.draft.terms"
|
|
1257
|
+
auto-grow
|
|
1258
|
+
description="Payment terms printed in the invoice footer."
|
|
1259
|
+
:errors="ctx.errors.value.terms"
|
|
1260
|
+
label="Terms"
|
|
1261
|
+
placeholder="Payment due within 30 days…"
|
|
1262
|
+
:rows="2"
|
|
1263
|
+
/>
|
|
1264
|
+
</div>
|
|
1265
|
+
</BbCollapsible>
|
|
1266
|
+
</section>
|
|
1267
|
+
</template>
|
|
1268
|
+
|
|
1269
|
+
<script setup lang="ts">
|
|
1270
|
+
import { ref } from 'vue';
|
|
1271
|
+
import { BbButton, BbCollapsible, BbTextarea } from 'bitboss-ui';
|
|
1272
|
+
import { useInvoiceForm } from './context';
|
|
1273
|
+
|
|
1274
|
+
const ctx = useInvoiceForm();
|
|
1275
|
+
|
|
1276
|
+
// Rung 1: only this section cares whether it is open. The section mounts
|
|
1277
|
+
// after the invoice loaded (Index gates on ctx.loaded), so seeding from the
|
|
1278
|
+
// draft starts an annotated invoice open and a blank one closed.
|
|
1279
|
+
const open = ref(ctx.draft.notes !== null || ctx.draft.terms !== null);
|
|
1280
|
+
</script>
|
|
1281
|
+
```
|
|
1282
|
+
|
|
1283
|
+
### FormActions.vue
|
|
1284
|
+
|
|
1285
|
+
```vue
|
|
1286
|
+
<!-- InvoiceForm/FormActions.vue -->
|
|
1287
|
+
<template>
|
|
1288
|
+
<!-- Sticky action row: the commit affordance never scrolls away. Not the
|
|
1289
|
+
floating bottom panel — that is selection chrome, and a form has no
|
|
1290
|
+
selection. -->
|
|
1291
|
+
<div
|
|
1292
|
+
class="sticky bottom-0 z-10 -mx-4 flex items-center gap-3 border-t bg-[color:var(--bb-panel)] px-4 py-3"
|
|
1293
|
+
>
|
|
1294
|
+
<BbButton
|
|
1295
|
+
v-if="ctx.mode === 'edit'"
|
|
1296
|
+
prepend:icon="lucide:trash-2"
|
|
1297
|
+
variant="destructive"
|
|
1298
|
+
@click="ctx.deleteInvoice"
|
|
1299
|
+
>
|
|
1300
|
+
Delete
|
|
1301
|
+
</BbButton>
|
|
1302
|
+
<p v-if="ctx.isDirty.value" class="text-xs text-gray-500">
|
|
1303
|
+
Unsaved changes
|
|
1304
|
+
</p>
|
|
1305
|
+
|
|
1306
|
+
<div class="ml-auto flex items-center gap-2">
|
|
1307
|
+
<!-- Cancel is navigation: the route-leave guard asks about a dirty
|
|
1308
|
+
draft, so no bespoke confirm here. -->
|
|
1309
|
+
<BbButton :disabled="ctx.saving.value" to="/invoices" variant="ghost">
|
|
1310
|
+
Cancel
|
|
1311
|
+
</BbButton>
|
|
1312
|
+
<BbButton
|
|
1313
|
+
v-if="ctx.mode === 'create'"
|
|
1314
|
+
:disabled="ctx.saving.value"
|
|
1315
|
+
variant="outline"
|
|
1316
|
+
@click="ctx.saveAsDraft"
|
|
1317
|
+
>
|
|
1318
|
+
Save as draft
|
|
1319
|
+
</BbButton>
|
|
1320
|
+
<BbButton :loading="ctx.saving.value" type="submit" variant="primary">
|
|
1321
|
+
{{ ctx.mode === 'edit' ? 'Save changes' : 'Create invoice' }}
|
|
1322
|
+
</BbButton>
|
|
1323
|
+
</div>
|
|
1324
|
+
</div>
|
|
1325
|
+
</template>
|
|
1326
|
+
|
|
1327
|
+
<script setup lang="ts">
|
|
1328
|
+
import { BbButton } from 'bitboss-ui';
|
|
1329
|
+
import { useInvoiceForm } from './context';
|
|
1330
|
+
|
|
1331
|
+
const ctx = useInvoiceForm();
|
|
1332
|
+
</script>
|
|
1333
|
+
```
|
|
1334
|
+
|
|
1335
|
+
### CustomerPeek.vue
|
|
1336
|
+
|
|
1337
|
+
```vue
|
|
1338
|
+
<!-- InvoiceForm/CustomerPeek.vue -->
|
|
1339
|
+
<template>
|
|
1340
|
+
<div v-if="customer" class="flex flex-col gap-4 text-sm">
|
|
1341
|
+
<header class="flex items-center gap-3">
|
|
1342
|
+
<BbAvatar :alt="customer.name" :src="customer.avatarUrl" />
|
|
1343
|
+
<div class="flex flex-col">
|
|
1344
|
+
<h2 class="text-base font-semibold">{{ customer.name }}</h2>
|
|
1345
|
+
<p class="text-xs text-gray-500">
|
|
1346
|
+
{{ customer.email }} · {{ customer.country }}
|
|
1347
|
+
</p>
|
|
1348
|
+
</div>
|
|
1349
|
+
</header>
|
|
1350
|
+
|
|
1351
|
+
<!-- Stat strip. -->
|
|
1352
|
+
<div class="grid grid-cols-3 gap-2">
|
|
1353
|
+
<div class="rounded-[var(--bb-radius)] border p-2">
|
|
1354
|
+
<p class="text-xs text-gray-500">Total billed</p>
|
|
1355
|
+
<p class="font-semibold">{{ currency.format(customer.totalBilled) }}</p>
|
|
1356
|
+
</div>
|
|
1357
|
+
<div class="rounded-[var(--bb-radius)] border p-2">
|
|
1358
|
+
<p class="text-xs text-gray-500">Outstanding</p>
|
|
1359
|
+
<p class="font-semibold">{{ currency.format(customer.outstanding) }}</p>
|
|
1360
|
+
</div>
|
|
1361
|
+
<div class="rounded-[var(--bb-radius)] border p-2">
|
|
1362
|
+
<p class="text-xs text-gray-500">Invoices</p>
|
|
1363
|
+
<p class="font-semibold">{{ customer.invoiceCount }}</p>
|
|
1364
|
+
</div>
|
|
1365
|
+
</div>
|
|
1366
|
+
|
|
1367
|
+
<!-- Billing profile: exactly the fields this form negotiates. -->
|
|
1368
|
+
<section class="flex flex-col gap-1.5">
|
|
1369
|
+
<h3 class="text-xs font-semibold uppercase tracking-wide text-gray-500">
|
|
1370
|
+
Billing profile
|
|
1371
|
+
</h3>
|
|
1372
|
+
<dl class="grid grid-cols-[auto_1fr] gap-x-4 gap-y-1.5">
|
|
1373
|
+
<dt class="text-gray-500">Default terms</dt>
|
|
1374
|
+
<dd>{{ customer.defaultPaymentTerms }}</dd>
|
|
1375
|
+
<dt class="text-gray-500">Default tax rate</dt>
|
|
1376
|
+
<dd>{{ customer.defaultTaxRate }}%</dd>
|
|
1377
|
+
<dt class="text-gray-500">Tax ID</dt>
|
|
1378
|
+
<dd>{{ customer.taxId }}</dd>
|
|
1379
|
+
<dt class="text-gray-500">Billing email</dt>
|
|
1380
|
+
<dd>{{ customer.billingEmail }}</dd>
|
|
1381
|
+
</dl>
|
|
1382
|
+
<!-- The peek acts on the form: this writes into the draft. -->
|
|
1383
|
+
<BbButton
|
|
1384
|
+
class="mt-1 self-start"
|
|
1385
|
+
prepend:icon="lucide:sparkles"
|
|
1386
|
+
size="sm"
|
|
1387
|
+
variant="secondary"
|
|
1388
|
+
@click="() => ctx.applyCustomerDefaults(customer!)"
|
|
1389
|
+
>
|
|
1390
|
+
Apply billing defaults
|
|
1391
|
+
</BbButton>
|
|
1392
|
+
</section>
|
|
1393
|
+
|
|
1394
|
+
<!-- Recent invoices: real links — leaving a dirty draft still goes
|
|
1395
|
+
through the route-leave guard. -->
|
|
1396
|
+
<section class="flex flex-col gap-1.5">
|
|
1397
|
+
<h3 class="text-xs font-semibold uppercase tracking-wide text-gray-500">
|
|
1398
|
+
Recent invoices
|
|
1399
|
+
</h3>
|
|
1400
|
+
<ul class="flex flex-col">
|
|
1401
|
+
<li
|
|
1402
|
+
v-for="recent in customer.recentInvoices"
|
|
1403
|
+
:key="recent.id"
|
|
1404
|
+
class="flex items-center justify-between gap-2 border-b py-1.5 last:border-b-0"
|
|
1405
|
+
>
|
|
1406
|
+
<BbButton :to="`/invoices/${recent.id}`" size="sm" variant="link">
|
|
1407
|
+
{{ recent.number }}
|
|
1408
|
+
</BbButton>
|
|
1409
|
+
<BbBadge variant="outline">{{ recent.status }}</BbBadge>
|
|
1410
|
+
<span class="text-xs tabular-nums text-gray-500">
|
|
1411
|
+
{{ currency.format(recent.amount) }}
|
|
1412
|
+
</span>
|
|
1413
|
+
</li>
|
|
1414
|
+
</ul>
|
|
1415
|
+
</section>
|
|
1416
|
+
|
|
1417
|
+
<footer class="mt-2 flex items-center gap-2 border-t pt-3">
|
|
1418
|
+
<BbButton
|
|
1419
|
+
append:icon="lucide:arrow-up-right"
|
|
1420
|
+
size="sm"
|
|
1421
|
+
:to="`/customers/${customer.id}`"
|
|
1422
|
+
variant="ghost"
|
|
1423
|
+
>
|
|
1424
|
+
Open customer
|
|
1425
|
+
</BbButton>
|
|
1426
|
+
</footer>
|
|
1427
|
+
</div>
|
|
1428
|
+
|
|
1429
|
+
<div v-else class="flex justify-center p-8">
|
|
1430
|
+
<BbSpinner />
|
|
1431
|
+
</div>
|
|
1432
|
+
</template>
|
|
1433
|
+
|
|
1434
|
+
<script setup lang="ts">
|
|
1435
|
+
import { ref, watchEffect } from 'vue';
|
|
1436
|
+
import { BbAvatar, BbBadge, BbButton, BbSpinner } from 'bitboss-ui';
|
|
1437
|
+
import { useInvoiceForm } from './context';
|
|
1438
|
+
import { api } from '@/api';
|
|
1439
|
+
import type { CustomerDetail } from '@/types';
|
|
1440
|
+
|
|
1441
|
+
const props = defineProps<{ customerId: number }>();
|
|
1442
|
+
|
|
1443
|
+
const ctx = useInvoiceForm();
|
|
1444
|
+
|
|
1445
|
+
const customer = ref<CustomerDetail | null>(null);
|
|
1446
|
+
watchEffect(async () => {
|
|
1447
|
+
customer.value = await api.customers.getDetail(props.customerId);
|
|
1448
|
+
});
|
|
1449
|
+
|
|
1450
|
+
const currency = new Intl.NumberFormat('en-US', {
|
|
1451
|
+
style: 'currency',
|
|
1452
|
+
currency: 'USD',
|
|
1453
|
+
});
|
|
1454
|
+
</script>
|
|
1455
|
+
```
|
|
1456
|
+
|
|
1457
|
+
## Why it is built this way
|
|
1458
|
+
|
|
1459
|
+
- **The context coopts, leaves consume.** The shell is joined once in
|
|
1460
|
+
`provideInvoiceForm()`; ten components share the draft, the errors, the
|
|
1461
|
+
peek stack and every action through one `useInvoiceForm()` call. The only
|
|
1462
|
+
props on the page are data-identity props: the cells' `line` and the
|
|
1463
|
+
peek's `customer-id`. Notably there is **no table context** here — the
|
|
1464
|
+
line-items table has no selection, no paging and no fetch; its entire
|
|
1465
|
+
state _is_ the draft, so there is nothing for a `useBbTableContext`
|
|
1466
|
+
handle to carry.
|
|
1467
|
+
- **The draft is the mutation boundary.** On a workspace page every action
|
|
1468
|
+
hits the API; on a form the unsaved state is the product. Cell commits,
|
|
1469
|
+
line add/duplicate/remove and "apply billing defaults" therefore mutate
|
|
1470
|
+
`ctx.draft` (still context actions — leaves never reach around it), and
|
|
1471
|
+
the single `persist()` call is where the API, the 422 mapping, the toast
|
|
1472
|
+
and the snapshot re-baseline live.
|
|
1473
|
+
- **422 keys are the server's shape, not reshaped.** Scalar inputs bind
|
|
1474
|
+
`ctx.errors.value.<field>` directly; row errors keep their
|
|
1475
|
+
`lineItems.<index>.<field>` keys and the cells resolve them through
|
|
1476
|
+
`lineItemError`, which derives the index from the row's id — so the
|
|
1477
|
+
lookup survives the page-search filter reordering what is visible.
|
|
1478
|
+
- **Tied selects trust the machinery.** `dependencies: [draft.customerId]`
|
|
1479
|
+
re-runs the project provider, `enforce-coherence` prunes a stale pick,
|
|
1480
|
+
and both providers honor the prefill contract (return the selected
|
|
1481
|
+
record on component-initiated calls) so a valid selection is never
|
|
1482
|
+
wiped — the exact failure mode the BbSelect guide warns about.
|
|
1483
|
+
- **The inline-edit grammar is three tiny leaves, not one configurable
|
|
1484
|
+
cell.** Each editable cell owns rung-1 state (`editing`, the in-flight
|
|
1485
|
+
value), commits on blur/Enter and reverts on Esc. Enter is
|
|
1486
|
+
`.prevent`-ed so it commits the cell instead of submitting the enclosing
|
|
1487
|
+
form, and the `editing` guard keeps the unmount blur after an Esc from
|
|
1488
|
+
re-committing the reverted value.
|
|
1489
|
+
- **The dirty snapshot re-baselines before every self-inflicted
|
|
1490
|
+
navigation** — successful save, draft save, delete — so the
|
|
1491
|
+
`onBeforeRouteLeave` confirm fires only when the user genuinely walks
|
|
1492
|
+
away. Cancel is a plain `to` link for the same reason: the guard _is_ the
|
|
1493
|
+
confirm, so the button needs none of its own.
|
|
1494
|
+
- **A sticky action row, not the floating bottom panel.** The panel from
|
|
1495
|
+
the guides is selection-driven chrome that comes and goes; a form's
|
|
1496
|
+
commit affordance must be permanently visible, so it is a sticky row on
|
|
1497
|
+
the page surface (`--bb-panel` background, top border) hosting the one
|
|
1498
|
+
`variant="primary"` of the page.
|
|
1499
|
+
- **Sizing:** the form fields are full-height — a form is not a dense
|
|
1500
|
+
surface. `compact` appears where density is real: the topbar search, the
|
|
1501
|
+
line-items table and its cell inputs. `sm` buttons appear only in the
|
|
1502
|
+
table's row actions, the "View customer" link and the peek; everything
|
|
1503
|
+
in the sticky row is default `md`.
|
|
1504
|
+
- **Destructive actions ride `useConfirm`** — delete and the leave guard
|
|
1505
|
+
both — and the peek is a workbench: it acts on the draft and links out
|
|
1506
|
+
with real `to` links, never `@click` + router.
|
|
1507
|
+
|
|
1508
|
+
## Variations
|
|
1509
|
+
|
|
1510
|
+
- **Inside a `BbDialog`** (quick create from the invoices workspace): drop
|
|
1511
|
+
the route guard and breadcrumbs, keep `isDirty`, confirm on the dialog's
|
|
1512
|
+
close instead, and move the action row into the dialog `#footer` — the
|
|
1513
|
+
context and sections carry over unchanged.
|
|
1514
|
+
- **Autosave drafts**: debounce a watcher on `draft` that calls
|
|
1515
|
+
`persist('draft')` silently and re-baselines per save; the "Save as
|
|
1516
|
+
draft" button disappears and the sticky row shows "Saved 12s ago".
|
|
1517
|
+
- **Client-side pre-validation**: validate required fields on submit
|
|
1518
|
+
before calling the API and merge the messages into the same `errors`
|
|
1519
|
+
map — the inputs cannot tell the difference, which is the point.
|
|
1520
|
+
- **Per-line tax**: add an `EditableTaxCell` per row (its own leaf, per
|
|
1521
|
+
the no-API-monster rule) and group the totals block by rate.
|
|
1522
|
+
- **Send flow**: the primary becomes "Save and send" and opens a
|
|
1523
|
+
`BbDialog` preview (recipient, subject, PDF) whose open-state lives on
|
|
1524
|
+
the context, before persisting with `status: 'pending'`.
|
|
1525
|
+
- **Per-field persistence**: this page deliberately has no per-cell save
|
|
1526
|
+
feedback because no per-cell save exists — the draft is the mutation
|
|
1527
|
+
boundary and Save is the one commit. When the domain wants every field
|
|
1528
|
+
to persist itself (no Save button), that is the autosave pattern: see
|
|
1529
|
+
the [Inline Edit Playbook](../../guides/inline-edit-playbook.md) and
|
|
1530
|
+
the inline-edit-workspace recipe.
|
|
1531
|
+
|
|
1532
|
+
## Related
|
|
1533
|
+
|
|
1534
|
+
- [Records Workspace (Vue)](./records-workspace.md) — the listing page
|
|
1535
|
+
this form is reached from.
|
|
1536
|
+
- [Page Shell](../../guides/page-shell.md) — the app-side layout the
|
|
1537
|
+
customer peek renders inside.
|
|
1538
|
+
- [BbTable](../../BbTable.md), [BbSelect](../../BbSelect.md),
|
|
1539
|
+
[BbCollapsible](../../BbCollapsible.md) — the components this page
|
|
1540
|
+
leans on hardest.
|