@openreachtech/furo-vue 1.3.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 +9 -0
- package/README.ja.md +334 -0
- package/README.md +333 -0
- package/index.js +11 -0
- package/lib/assets/css/furo/0010.variables-palette-color-scale.css +298 -0
- package/lib/assets/css/furo/0020.variables-z-index.css +10 -0
- package/lib/assets/css/furo/0030.variables-semantic-color.css +178 -0
- package/lib/assets/css/furo/0035.variables-semantic-dimension.css +104 -0
- package/lib/assets/css/furo/0040.variables-component-furo.css +28 -0
- package/lib/assets/css/furo/0050.furo-editor-content.css +22 -0
- package/lib/assets/css/furo.css +15 -0
- package/lib/components/atoms/FuroAvatar/FuroAvatar.vue +135 -0
- package/lib/components/atoms/FuroAvatar/FuroAvatarContext.js +147 -0
- package/lib/components/atoms/FuroBadge/FuroBadge.vue +240 -0
- package/lib/components/atoms/FuroBadge/FuroBadgeContext.js +298 -0
- package/lib/components/atoms/FuroButton/ButtonEmitPayload.js +44 -0
- package/lib/components/atoms/FuroButton/FuroButton.vue +301 -0
- package/lib/components/atoms/FuroButton/FuroButtonContext.js +328 -0
- package/lib/components/atoms/FuroCheckbox/CheckboxEmitPayload.js +106 -0
- package/lib/components/atoms/FuroCheckbox/FuroCheckbox.vue +173 -0
- package/lib/components/atoms/FuroCheckbox/FuroCheckboxContext.js +682 -0
- package/lib/components/atoms/FuroEmailField/EmailFieldEmitPayload.js +74 -0
- package/lib/components/atoms/FuroEmailField/FuroEmailField.vue +119 -0
- package/lib/components/atoms/FuroEmailField/FuroEmailFieldContext.js +311 -0
- package/lib/components/atoms/FuroFileField/FileFieldEmitPayload.js +80 -0
- package/lib/components/atoms/FuroFileField/FuroFileField.vue +128 -0
- package/lib/components/atoms/FuroFileField/FuroFileFieldContext.js +195 -0
- package/lib/components/atoms/FuroIcon/FuroIcon.vue +74 -0
- package/lib/components/atoms/FuroIcon/FuroIconContext.js +293 -0
- package/lib/components/atoms/FuroIcon/FuroIconRegistry.js +617 -0
- package/lib/components/atoms/FuroIcon/FuroIconifyRendererContext.js +101 -0
- package/lib/components/atoms/FuroIcon/furoIconData.js +164 -0
- package/lib/components/atoms/FuroIcon/iconRenderers.js +62 -0
- package/lib/components/atoms/FuroIcon/icons.js +7 -0
- package/lib/components/atoms/FuroNumberField/FuroNumberField.vue +213 -0
- package/lib/components/atoms/FuroNumberField/FuroNumberFieldContext.js +442 -0
- package/lib/components/atoms/FuroNumberField/NumberFieldEmitPayload.js +162 -0
- package/lib/components/atoms/FuroPasswordField/FuroPasswordField.vue +119 -0
- package/lib/components/atoms/FuroPasswordField/FuroPasswordFieldContext.js +311 -0
- package/lib/components/atoms/FuroPasswordField/PasswordFieldEmitPayload.js +74 -0
- package/lib/components/atoms/FuroRadio/FuroRadio.vue +193 -0
- package/lib/components/atoms/FuroRadio/FuroRadioContext.js +557 -0
- package/lib/components/atoms/FuroRadio/RadioEmitPayload.js +72 -0
- package/lib/components/atoms/FuroSwitch/FuroSwitch.vue +165 -0
- package/lib/components/atoms/FuroSwitch/FuroSwitchContext.js +440 -0
- package/lib/components/atoms/FuroSwitch/SwitchEmitPayload.js +86 -0
- package/lib/components/atoms/FuroTextField/FuroTextField.vue +119 -0
- package/lib/components/atoms/FuroTextField/FuroTextFieldContext.js +311 -0
- package/lib/components/atoms/FuroTextField/TextFieldEmitPayload.js +83 -0
- package/lib/components/atoms/FuroTextarea/FuroTextarea.vue +121 -0
- package/lib/components/atoms/FuroTextarea/FuroTextareaContext.js +322 -0
- package/lib/components/atoms/FuroTextarea/TextareaEmitPayload.js +83 -0
- package/lib/components/atoms/FuroToggle/FuroToggle.vue +172 -0
- package/lib/components/atoms/FuroToggle/FuroToggleContext.js +410 -0
- package/lib/components/atoms/FuroToggle/ToggleEmitPayload.js +86 -0
- package/lib/components/molecules/FuroAccordion/AccordionEmitPayload.js +80 -0
- package/lib/components/molecules/FuroAccordion/FuroAccordion.vue +219 -0
- package/lib/components/molecules/FuroAccordion/FuroAccordionContext.js +547 -0
- package/lib/components/molecules/FuroAttachment/FuroAttachment.vue +376 -0
- package/lib/components/molecules/FuroAttachment/FuroAttachmentContext.js +628 -0
- package/lib/components/molecules/FuroAutocompleteField/AutocompleteEmitPayload.js +225 -0
- package/lib/components/molecules/FuroAutocompleteField/FuroAutocompleteField.vue +669 -0
- package/lib/components/molecules/FuroAutocompleteField/FuroAutocompleteFieldContext.js +1525 -0
- package/lib/components/molecules/FuroAvatarGroup/FuroAvatarGroup.vue +126 -0
- package/lib/components/molecules/FuroAvatarGroup/FuroAvatarGroupContext.js +245 -0
- package/lib/components/molecules/FuroBreadcrumb/FuroBreadcrumb.vue +219 -0
- package/lib/components/molecules/FuroBreadcrumb/FuroBreadcrumbContext.js +257 -0
- package/lib/components/molecules/FuroCollapsible/CollapsibleEmitPayload.js +69 -0
- package/lib/components/molecules/FuroCollapsible/FuroCollapsible.vue +177 -0
- package/lib/components/molecules/FuroCollapsible/FuroCollapsibleContext.js +389 -0
- package/lib/components/molecules/FuroControlBlock/FuroControlBlock.vue +149 -0
- package/lib/components/molecules/FuroControlBlock/FuroControlBlockContext.js +81 -0
- package/lib/components/molecules/FuroDatePicker/DatePickerEmitPayload.js +74 -0
- package/lib/components/molecules/FuroDatePicker/FuroDatePicker.vue +661 -0
- package/lib/components/molecules/FuroDatePicker/FuroDatePickerContext.js +1038 -0
- package/lib/components/molecules/FuroDateTimePicker/DateTimePickerEmitPayload.js +96 -0
- package/lib/components/molecules/FuroDateTimePicker/FuroDateTimePicker.vue +153 -0
- package/lib/components/molecules/FuroDateTimePicker/FuroDateTimePickerContext.js +840 -0
- package/lib/components/molecules/FuroDropdownMenu/DropdownMenuOptionSelectEmitPayload.js +100 -0
- package/lib/components/molecules/FuroDropdownMenu/FuroDropdownMenu.vue +348 -0
- package/lib/components/molecules/FuroDropdownMenu/FuroDropdownMenuContext.js +714 -0
- package/lib/components/molecules/FuroDropdownMenu/FuroDropdownMenuOptionRows.vue +222 -0
- package/lib/components/molecules/FuroEditableField/EditableEmitPayload.js +89 -0
- package/lib/components/molecules/FuroEditableField/FuroEditableField.vue +314 -0
- package/lib/components/molecules/FuroEditableField/FuroEditableFieldContext.js +1018 -0
- package/lib/components/molecules/FuroEditableField/processors/EditorCollisionProcessor.js +236 -0
- package/lib/components/molecules/FuroEditableField/processors/ObjectProcessor.js +101 -0
- package/lib/components/molecules/FuroEmptyState/FuroEmptyState.vue +124 -0
- package/lib/components/molecules/FuroEmptyState/FuroEmptyStateContext.js +42 -0
- package/lib/components/molecules/FuroErrorState/FuroErrorState.vue +125 -0
- package/lib/components/molecules/FuroErrorState/FuroErrorStateContext.js +42 -0
- package/lib/components/molecules/FuroPagination/FuroPagination.vue +246 -0
- package/lib/components/molecules/FuroPagination/FuroPaginationContext.js +489 -0
- package/lib/components/molecules/FuroPopover/FuroPopover.vue +171 -0
- package/lib/components/molecules/FuroPopover/FuroPopoverContext.js +279 -0
- package/lib/components/molecules/FuroScrollArea/FuroScrollArea.vue +181 -0
- package/lib/components/molecules/FuroScrollArea/FuroScrollAreaContext.js +149 -0
- package/lib/components/molecules/FuroSearchField/FuroSearchField.vue +155 -0
- package/lib/components/molecules/FuroSearchField/FuroSearchFieldContext.js +231 -0
- package/lib/components/molecules/FuroSectionHeader/FuroSectionHeader.vue +140 -0
- package/lib/components/molecules/FuroSectionHeader/FuroSectionHeaderContext.js +204 -0
- package/lib/components/molecules/FuroSelect/FuroSelect.vue +474 -0
- package/lib/components/molecules/FuroSelect/FuroSelectContext.js +743 -0
- package/lib/components/molecules/FuroSelect/SelectEmitPayload.js +181 -0
- package/lib/components/molecules/FuroSeparator/FuroSeparator.vue +88 -0
- package/lib/components/molecules/FuroSeparator/FuroSeparatorContext.js +40 -0
- package/lib/components/molecules/FuroSkeleton/FuroSkeleton.vue +101 -0
- package/lib/components/molecules/FuroSkeleton/FuroSkeletonContext.js +82 -0
- package/lib/components/molecules/FuroSplitter/FuroSplitter.vue +237 -0
- package/lib/components/molecules/FuroSplitter/FuroSplitterContext.js +429 -0
- package/lib/components/molecules/FuroSplitter/SplitterEmitPayload.js +69 -0
- package/lib/components/molecules/FuroTimeField/FuroTimeField.vue +251 -0
- package/lib/components/molecules/FuroTimeField/FuroTimeFieldContext.js +1138 -0
- package/lib/components/molecules/FuroTimeField/TimeFieldEmitPayload.js +74 -0
- package/lib/components/molecules/FuroToggleGroup/FuroToggleGroup.vue +211 -0
- package/lib/components/molecules/FuroToggleGroup/FuroToggleGroupContext.js +551 -0
- package/lib/components/molecules/FuroToggleGroup/ToggleGroupEmitPayload.js +80 -0
- package/lib/components/molecules/FuroToolBar/FuroToolBar.vue +91 -0
- package/lib/components/molecules/FuroToolBar/FuroToolBarContext.js +75 -0
- package/lib/components/molecules/FuroTooltip/FuroTooltip.vue +183 -0
- package/lib/components/molecules/FuroTooltip/FuroTooltipContext.js +290 -0
- package/lib/components/organisms/FuroAlertDialog/FuroAlertDialog.vue +324 -0
- package/lib/components/organisms/FuroAlertDialog/FuroAlertDialogContext.js +608 -0
- package/lib/components/organisms/FuroDialog/FuroDialog.vue +362 -0
- package/lib/components/organisms/FuroDialog/FuroDialogContext.js +535 -0
- package/lib/components/organisms/FuroDrawer/FuroDrawer.vue +618 -0
- package/lib/components/organisms/FuroDrawer/FuroDrawerContext.js +676 -0
- package/lib/components/organisms/FuroEditor/FuroEditor.vue +613 -0
- package/lib/components/organisms/FuroEditor/FuroEditorCommandContext.js +420 -0
- package/lib/components/organisms/FuroEditor/FuroEditorContext.js +2048 -0
- package/lib/components/organisms/FuroEditor/FuroEditorMentionMenu.vue +120 -0
- package/lib/components/organisms/FuroEditor/FuroEditorMentionMenuContext.js +261 -0
- package/lib/components/organisms/FuroEditor/PreserveNewlinesPasteExtension.js +267 -0
- package/lib/components/organisms/FuroEditor/build-editor-extensions.js +97 -0
- package/lib/components/organisms/FuroEditor/mention-suggestion.js +320 -0
- package/lib/components/organisms/FuroEditor/paste-link-segments.js +124 -0
- package/lib/components/organisms/FuroSidebar/FuroSidebar.vue +473 -0
- package/lib/components/organisms/FuroSidebar/FuroSidebarContext.js +1014 -0
- package/lib/components/organisms/FuroStepper/FuroStepper.vue +345 -0
- package/lib/components/organisms/FuroStepper/FuroStepperContext.js +481 -0
- package/lib/components/organisms/FuroTable/FuroTable.vue +707 -0
- package/lib/components/organisms/FuroTable/FuroTableContext.js +1965 -0
- package/lib/components/organisms/FuroTable/FuroTableRowItemContext.js +111 -0
- package/lib/components/organisms/FuroTabs/FuroTabs.vue +403 -0
- package/lib/components/organisms/FuroTabs/FuroTabsContext.js +846 -0
- package/lib/components/organisms/FuroToast/FuroToast.vue +624 -0
- package/lib/components/organisms/FuroToast/FuroToastContext.js +1089 -0
- package/lib/components/organisms/FuroToast/FuroToaster.vue +44 -0
- package/lib/components/organisms/FuroToast/FuroToasterContext.js +113 -0
- package/lib/components/organisms/FuroToast/ToastQueue.js +325 -0
- package/lib/components/organisms/FuroToast/toast.js +64 -0
- package/lib/contexts/BaseFuroContext.js +318 -0
- package/lib/contexts/BaseFuroContextAccessor.js +47 -0
- package/lib/emit-payload/BaseEmitPayload.js +91 -0
- package/lib/index.components.js +61 -0
- package/lib/index.core.js +18 -0
- package/lib/tools/TimerClerk.js +75 -0
- package/nuxt/index.js +109 -0
- package/nuxt/runtime/furo-icon-renderer.js +24 -0
- package/package.json +137 -0
- package/public/furo-vue/components.json +4619 -0
- package/public/furo-vue/llms.txt +66 -0
- package/types/furo-nuxt.d.ts +48 -0
- package/types/generated/components/atoms/FuroAvatar/FuroAvatar.vue.d.ts +74 -0
- package/types/generated/components/atoms/FuroAvatar/FuroAvatarContext.d.ts +82 -0
- package/types/generated/components/atoms/FuroBadge/FuroBadge.vue.d.ts +12 -0
- package/types/generated/components/atoms/FuroBadge/FuroBadgeContext.d.ts +160 -0
- package/types/generated/components/atoms/FuroButton/ButtonEmitPayload.d.ts +19 -0
- package/types/generated/components/atoms/FuroButton/FuroButton.vue.d.ts +53 -0
- package/types/generated/components/atoms/FuroButton/FuroButtonContext.d.ts +156 -0
- package/types/generated/components/atoms/FuroCheckbox/CheckboxEmitPayload.d.ts +55 -0
- package/types/generated/components/atoms/FuroCheckbox/FuroCheckbox.vue.d.ts +87 -0
- package/types/generated/components/atoms/FuroCheckbox/FuroCheckboxContext.d.ts +342 -0
- package/types/generated/components/atoms/FuroEmailField/EmailFieldEmitPayload.d.ts +46 -0
- package/types/generated/components/atoms/FuroEmailField/FuroEmailField.vue.d.ts +458 -0
- package/types/generated/components/atoms/FuroEmailField/FuroEmailFieldContext.d.ts +161 -0
- package/types/generated/components/atoms/FuroFileField/FileFieldEmitPayload.d.ts +46 -0
- package/types/generated/components/atoms/FuroFileField/FuroFileField.vue.d.ts +454 -0
- package/types/generated/components/atoms/FuroFileField/FuroFileFieldContext.d.ts +109 -0
- package/types/generated/components/atoms/FuroIcon/FuroIcon.vue.d.ts +14 -0
- package/types/generated/components/atoms/FuroIcon/FuroIconContext.d.ts +152 -0
- package/types/generated/components/atoms/FuroIcon/FuroIconRegistry.d.ts +457 -0
- package/types/generated/components/atoms/FuroIcon/FuroIconifyRendererContext.d.ts +65 -0
- package/types/generated/components/atoms/FuroIcon/furoIconData.d.ts +154 -0
- package/types/generated/components/atoms/FuroIcon/iconRenderers.d.ts +6 -0
- package/types/generated/components/atoms/FuroIcon/icons.d.ts +6 -0
- package/types/generated/components/atoms/FuroNumberField/FuroNumberField.vue.d.ts +500 -0
- package/types/generated/components/atoms/FuroNumberField/FuroNumberFieldContext.d.ts +243 -0
- package/types/generated/components/atoms/FuroNumberField/NumberFieldEmitPayload.d.ts +74 -0
- package/types/generated/components/atoms/FuroPasswordField/FuroPasswordField.vue.d.ts +458 -0
- package/types/generated/components/atoms/FuroPasswordField/FuroPasswordFieldContext.d.ts +161 -0
- package/types/generated/components/atoms/FuroPasswordField/PasswordFieldEmitPayload.d.ts +46 -0
- package/types/generated/components/atoms/FuroRadio/FuroRadio.vue.d.ts +96 -0
- package/types/generated/components/atoms/FuroRadio/FuroRadioContext.d.ts +290 -0
- package/types/generated/components/atoms/FuroRadio/RadioEmitPayload.d.ts +39 -0
- package/types/generated/components/atoms/FuroSwitch/FuroSwitch.vue.d.ts +74 -0
- package/types/generated/components/atoms/FuroSwitch/FuroSwitchContext.d.ts +218 -0
- package/types/generated/components/atoms/FuroSwitch/SwitchEmitPayload.d.ts +42 -0
- package/types/generated/components/atoms/FuroTextField/FuroTextField.vue.d.ts +458 -0
- package/types/generated/components/atoms/FuroTextField/FuroTextFieldContext.d.ts +161 -0
- package/types/generated/components/atoms/FuroTextField/TextFieldEmitPayload.d.ts +52 -0
- package/types/generated/components/atoms/FuroTextarea/FuroTextarea.vue.d.ts +422 -0
- package/types/generated/components/atoms/FuroTextarea/FuroTextareaContext.d.ts +161 -0
- package/types/generated/components/atoms/FuroTextarea/TextareaEmitPayload.d.ts +52 -0
- package/types/generated/components/atoms/FuroToggle/FuroToggle.vue.d.ts +51 -0
- package/types/generated/components/atoms/FuroToggle/FuroToggleContext.d.ts +191 -0
- package/types/generated/components/atoms/FuroToggle/ToggleEmitPayload.d.ts +42 -0
- package/types/generated/components/molecules/FuroAccordion/AccordionEmitPayload.d.ts +41 -0
- package/types/generated/components/molecules/FuroAccordion/FuroAccordion.vue.d.ts +133 -0
- package/types/generated/components/molecules/FuroAccordion/FuroAccordionContext.d.ts +279 -0
- package/types/generated/components/molecules/FuroAttachment/FuroAttachment.vue.d.ts +79 -0
- package/types/generated/components/molecules/FuroAttachment/FuroAttachmentContext.d.ts +363 -0
- package/types/generated/components/molecules/FuroAutocompleteField/AutocompleteEmitPayload.d.ts +129 -0
- package/types/generated/components/molecules/FuroAutocompleteField/FuroAutocompleteField.vue.d.ts +751 -0
- package/types/generated/components/molecules/FuroAutocompleteField/FuroAutocompleteFieldContext.d.ts +722 -0
- package/types/generated/components/molecules/FuroAvatarGroup/FuroAvatarGroup.vue.d.ts +84 -0
- package/types/generated/components/molecules/FuroAvatarGroup/FuroAvatarGroupContext.d.ts +150 -0
- package/types/generated/components/molecules/FuroBreadcrumb/FuroBreadcrumb.vue.d.ts +24 -0
- package/types/generated/components/molecules/FuroBreadcrumb/FuroBreadcrumbContext.d.ts +152 -0
- package/types/generated/components/molecules/FuroCollapsible/CollapsibleEmitPayload.d.ts +36 -0
- package/types/generated/components/molecules/FuroCollapsible/FuroCollapsible.vue.d.ts +98 -0
- package/types/generated/components/molecules/FuroCollapsible/FuroCollapsibleContext.d.ts +191 -0
- package/types/generated/components/molecules/FuroControlBlock/FuroControlBlock.vue.d.ts +30 -0
- package/types/generated/components/molecules/FuroControlBlock/FuroControlBlockContext.d.ts +57 -0
- package/types/generated/components/molecules/FuroDatePicker/DatePickerEmitPayload.d.ts +41 -0
- package/types/generated/components/molecules/FuroDatePicker/FuroDatePicker.vue.d.ts +456 -0
- package/types/generated/components/molecules/FuroDatePicker/FuroDatePickerContext.d.ts +516 -0
- package/types/generated/components/molecules/FuroDateTimePicker/DateTimePickerEmitPayload.d.ts +55 -0
- package/types/generated/components/molecules/FuroDateTimePicker/FuroDateTimePicker.vue.d.ts +1365 -0
- package/types/generated/components/molecules/FuroDateTimePicker/FuroDateTimePickerContext.d.ts +435 -0
- package/types/generated/components/molecules/FuroDropdownMenu/DropdownMenuOptionSelectEmitPayload.d.ts +48 -0
- package/types/generated/components/molecules/FuroDropdownMenu/FuroDropdownMenu.vue.d.ts +773 -0
- package/types/generated/components/molecules/FuroDropdownMenu/FuroDropdownMenuContext.d.ts +380 -0
- package/types/generated/components/molecules/FuroDropdownMenu/FuroDropdownMenuOptionRows.vue.d.ts +261 -0
- package/types/generated/components/molecules/FuroEditableField/EditableEmitPayload.d.ts +39 -0
- package/types/generated/components/molecules/FuroEditableField/FuroEditableField.vue.d.ts +646 -0
- package/types/generated/components/molecules/FuroEditableField/FuroEditableFieldContext.d.ts +477 -0
- package/types/generated/components/molecules/FuroEditableField/processors/EditorCollisionProcessor.d.ts +121 -0
- package/types/generated/components/molecules/FuroEditableField/processors/ObjectProcessor.d.ts +45 -0
- package/types/generated/components/molecules/FuroEmptyState/FuroEmptyState.vue.d.ts +21 -0
- package/types/generated/components/molecules/FuroEmptyState/FuroEmptyStateContext.d.ts +32 -0
- package/types/generated/components/molecules/FuroErrorState/FuroErrorState.vue.d.ts +21 -0
- package/types/generated/components/molecules/FuroErrorState/FuroErrorStateContext.d.ts +32 -0
- package/types/generated/components/molecules/FuroPagination/FuroPagination.vue.d.ts +534 -0
- package/types/generated/components/molecules/FuroPagination/FuroPaginationContext.d.ts +271 -0
- package/types/generated/components/molecules/FuroPopover/FuroPopover.vue.d.ts +513 -0
- package/types/generated/components/molecules/FuroPopover/FuroPopoverContext.d.ts +160 -0
- package/types/generated/components/molecules/FuroScrollArea/FuroScrollArea.vue.d.ts +124 -0
- package/types/generated/components/molecules/FuroScrollArea/FuroScrollAreaContext.d.ts +86 -0
- package/types/generated/components/molecules/FuroSearchField/FuroSearchField.vue.d.ts +487 -0
- package/types/generated/components/molecules/FuroSearchField/FuroSearchFieldContext.d.ts +137 -0
- package/types/generated/components/molecules/FuroSectionHeader/FuroSectionHeader.vue.d.ts +49 -0
- package/types/generated/components/molecules/FuroSectionHeader/FuroSectionHeaderContext.d.ts +121 -0
- package/types/generated/components/molecules/FuroSelect/FuroSelect.vue.d.ts +719 -0
- package/types/generated/components/molecules/FuroSelect/FuroSelectContext.d.ts +402 -0
- package/types/generated/components/molecules/FuroSelect/SelectEmitPayload.d.ts +102 -0
- package/types/generated/components/molecules/FuroSeparator/FuroSeparator.vue.d.ts +39 -0
- package/types/generated/components/molecules/FuroSeparator/FuroSeparatorContext.d.ts +31 -0
- package/types/generated/components/molecules/FuroSkeleton/FuroSkeleton.vue.d.ts +27 -0
- package/types/generated/components/molecules/FuroSkeleton/FuroSkeletonContext.d.ts +61 -0
- package/types/generated/components/molecules/FuroSplitter/FuroSplitter.vue.d.ts +139 -0
- package/types/generated/components/molecules/FuroSplitter/FuroSplitterContext.d.ts +226 -0
- package/types/generated/components/molecules/FuroSplitter/SplitterEmitPayload.d.ts +41 -0
- package/types/generated/components/molecules/FuroTimeField/FuroTimeField.vue.d.ts +498 -0
- package/types/generated/components/molecules/FuroTimeField/FuroTimeFieldContext.d.ts +555 -0
- package/types/generated/components/molecules/FuroTimeField/TimeFieldEmitPayload.d.ts +41 -0
- package/types/generated/components/molecules/FuroToggleGroup/FuroToggleGroup.vue.d.ts +70 -0
- package/types/generated/components/molecules/FuroToggleGroup/FuroToggleGroupContext.d.ts +270 -0
- package/types/generated/components/molecules/FuroToggleGroup/ToggleGroupEmitPayload.d.ts +41 -0
- package/types/generated/components/molecules/FuroToolBar/FuroToolBar.vue.d.ts +30 -0
- package/types/generated/components/molecules/FuroToolBar/FuroToolBarContext.d.ts +48 -0
- package/types/generated/components/molecules/FuroTooltip/FuroTooltip.vue.d.ts +524 -0
- package/types/generated/components/molecules/FuroTooltip/FuroTooltipContext.d.ts +166 -0
- package/types/generated/components/organisms/FuroAlertDialog/FuroAlertDialog.vue.d.ts +651 -0
- package/types/generated/components/organisms/FuroAlertDialog/FuroAlertDialogContext.d.ts +299 -0
- package/types/generated/components/organisms/FuroDialog/FuroDialog.vue.d.ts +583 -0
- package/types/generated/components/organisms/FuroDialog/FuroDialogContext.d.ts +264 -0
- package/types/generated/components/organisms/FuroDrawer/FuroDrawer.vue.d.ts +635 -0
- package/types/generated/components/organisms/FuroDrawer/FuroDrawerContext.d.ts +347 -0
- package/types/generated/components/organisms/FuroEditor/FuroEditor.vue.d.ts +2487 -0
- package/types/generated/components/organisms/FuroEditor/FuroEditorCommandContext.d.ts +192 -0
- package/types/generated/components/organisms/FuroEditor/FuroEditorContext.d.ts +904 -0
- package/types/generated/components/organisms/FuroEditor/FuroEditorMentionMenu.vue.d.ts +43 -0
- package/types/generated/components/organisms/FuroEditor/FuroEditorMentionMenuContext.d.ts +159 -0
- package/types/generated/components/organisms/FuroEditor/PreserveNewlinesPasteExtension.d.ts +43 -0
- package/types/generated/components/organisms/FuroEditor/build-editor-extensions.d.ts +22 -0
- package/types/generated/components/organisms/FuroEditor/mention-suggestion.d.ts +25 -0
- package/types/generated/components/organisms/FuroEditor/paste-link-segments.d.ts +31 -0
- package/types/generated/components/organisms/FuroSidebar/FuroSidebar.vue.d.ts +898 -0
- package/types/generated/components/organisms/FuroSidebar/FuroSidebarContext.d.ts +596 -0
- package/types/generated/components/organisms/FuroStepper/FuroStepper.vue.d.ts +589 -0
- package/types/generated/components/organisms/FuroStepper/FuroStepperContext.d.ts +266 -0
- package/types/generated/components/organisms/FuroTable/FuroTable.vue.d.ts +528 -0
- package/types/generated/components/organisms/FuroTable/FuroTableContext.d.ts +1035 -0
- package/types/generated/components/organisms/FuroTable/FuroTableRowItemContext.d.ts +62 -0
- package/types/generated/components/organisms/FuroTabs/FuroTabs.vue.d.ts +515 -0
- package/types/generated/components/organisms/FuroTabs/FuroTabsContext.d.ts +438 -0
- package/types/generated/components/organisms/FuroToast/FuroToast.vue.d.ts +725 -0
- package/types/generated/components/organisms/FuroToast/FuroToastContext.d.ts +543 -0
- package/types/generated/components/organisms/FuroToast/FuroToaster.vue.d.ts +729 -0
- package/types/generated/components/organisms/FuroToast/FuroToasterContext.d.ts +62 -0
- package/types/generated/components/organisms/FuroToast/ToastQueue.d.ts +201 -0
- package/types/generated/components/organisms/FuroToast/toast.d.ts +23 -0
- package/types/generated/contexts/BaseFuroContext.d.ts +207 -0
- package/types/generated/contexts/BaseFuroContextAccessor.d.ts +28 -0
- package/types/generated/emit-payload/BaseEmitPayload.d.ts +62 -0
- package/types/generated/index.components.d.ts +50 -0
- package/types/generated/index.core.d.ts +6 -0
- package/types/generated/tools/TimerClerk.d.ts +50 -0
- package/types/index.d.ts +12 -0
|
@@ -0,0 +1,1965 @@
|
|
|
1
|
+
import {
|
|
2
|
+
onBeforeUnmount,
|
|
3
|
+
onMounted,
|
|
4
|
+
ref,
|
|
5
|
+
shallowRef,
|
|
6
|
+
watch,
|
|
7
|
+
} from 'vue'
|
|
8
|
+
|
|
9
|
+
import BaseFuroContext from '../../../contexts/BaseFuroContext.js'
|
|
10
|
+
|
|
11
|
+
import FuroTableRowItemContext from './FuroTableRowItemContext.js'
|
|
12
|
+
|
|
13
|
+
const DEFAULT_ROW_KEY = 'id'
|
|
14
|
+
const DEFAULT_EMPTY_TEXT = 'No records'
|
|
15
|
+
const DEFAULT_OVERSCAN = 4
|
|
16
|
+
const DEFAULT_END_THRESHOLD = 8
|
|
17
|
+
|
|
18
|
+
const SORT_ICON_NEUTRAL = 'ph:arrows-down-up'
|
|
19
|
+
const SORT_ICON_HASH = {
|
|
20
|
+
asc: 'ph:arrow-up',
|
|
21
|
+
desc: 'ph:arrow-down',
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const MIN_COLUMN_WIDTH = 80
|
|
25
|
+
const SELECTION_COLUMN_WIDTH = '2.75rem'
|
|
26
|
+
const DEFAULT_ROW_ACTIONS_WIDTH = '6rem'
|
|
27
|
+
const DEFAULT_ROW_ACTIONS_LABEL = ''
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Context class for FuroTable organism.
|
|
31
|
+
*
|
|
32
|
+
* Presentational, domain-agnostic table. Sort and selection are controlled by
|
|
33
|
+
* the consumer (the table emits intent and re-reads its parcel); it never sorts
|
|
34
|
+
* or fetches data itself.
|
|
35
|
+
*
|
|
36
|
+
* @extends {BaseFuroContext<null, Props, EmitEventName>}
|
|
37
|
+
*/
|
|
38
|
+
export default class FuroTableContext extends BaseFuroContext {
|
|
39
|
+
/**
|
|
40
|
+
* Constructor.
|
|
41
|
+
*
|
|
42
|
+
* @param {FuroTableContextParams} params - Parameters of this constructor.
|
|
43
|
+
*/
|
|
44
|
+
constructor ({
|
|
45
|
+
props,
|
|
46
|
+
componentContext,
|
|
47
|
+
|
|
48
|
+
refHash,
|
|
49
|
+
}) {
|
|
50
|
+
super({
|
|
51
|
+
props,
|
|
52
|
+
componentContext,
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
this.refHash = refHash
|
|
56
|
+
|
|
57
|
+
/** @type {{ first: number, last: number } | null} */
|
|
58
|
+
this.lastEmittedRange = null
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The loaded-window end (`rowOffset + rows.length`) for which `reach-end`
|
|
62
|
+
* was last emitted. Guards against re-emitting until the loaded window grows.
|
|
63
|
+
*
|
|
64
|
+
* @type {number | null}
|
|
65
|
+
*/
|
|
66
|
+
this.lastReachEndLoadedEnd = null
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @type {{
|
|
70
|
+
* field: string
|
|
71
|
+
* startX: number
|
|
72
|
+
* startWidth: number
|
|
73
|
+
* nextField: string
|
|
74
|
+
* nextStartWidth: number
|
|
75
|
+
* } | null}
|
|
76
|
+
*/
|
|
77
|
+
this.resizeState = null
|
|
78
|
+
|
|
79
|
+
/** @type {number | null} */
|
|
80
|
+
this.scrollFrameId = null
|
|
81
|
+
|
|
82
|
+
this.pendingScrollTop = 0
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Factory method.
|
|
87
|
+
*
|
|
88
|
+
* @template {X extends typeof FuroTableContext ? X : never} T, X
|
|
89
|
+
* @override
|
|
90
|
+
* @param {FuroTableContextFactoryParams} params - Parameters of this factory method.
|
|
91
|
+
* @returns {InstanceType<T>} An instance of this class.
|
|
92
|
+
* @this {T}
|
|
93
|
+
*/
|
|
94
|
+
static create ({
|
|
95
|
+
props,
|
|
96
|
+
componentContext,
|
|
97
|
+
|
|
98
|
+
refHash = this.buildRefHash(),
|
|
99
|
+
}) {
|
|
100
|
+
return /** @type {InstanceType<T>} */ (
|
|
101
|
+
new this({
|
|
102
|
+
props,
|
|
103
|
+
componentContext,
|
|
104
|
+
|
|
105
|
+
refHash,
|
|
106
|
+
})
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Build ref hash.
|
|
112
|
+
*
|
|
113
|
+
* @override
|
|
114
|
+
* @returns {FuroTableContextParams['refHash']}
|
|
115
|
+
*/
|
|
116
|
+
static buildRefHash () {
|
|
117
|
+
return {
|
|
118
|
+
selectedRowKeysRef: ref([]),
|
|
119
|
+
// Shallow so `derivedFrom` stays reference-comparable against the prop.
|
|
120
|
+
pendingSelectionRef: shallowRef(null),
|
|
121
|
+
scrollTopRef: ref(0),
|
|
122
|
+
columnWidthsRef: ref({}),
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Schedule a callback on the next animation frame.
|
|
128
|
+
*
|
|
129
|
+
* Wrapped as a static hook so scroll throttling can be driven deterministically
|
|
130
|
+
* in tests (spy on this method) instead of depending on the browser global.
|
|
131
|
+
*
|
|
132
|
+
* @param {FrameRequestCallback} callback - Callback to run on the next frame.
|
|
133
|
+
* @returns {number} The frame request id.
|
|
134
|
+
*/
|
|
135
|
+
static requestFrame (callback) {
|
|
136
|
+
return globalThis.requestAnimationFrame(callback)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Cancel a scheduled animation frame.
|
|
141
|
+
*
|
|
142
|
+
* Wrapped as a static hook (mirrors `requestFrame`) so teardown can be driven
|
|
143
|
+
* deterministically in tests.
|
|
144
|
+
*
|
|
145
|
+
* @param {number} frameId - The frame request id to cancel.
|
|
146
|
+
* @returns {void}
|
|
147
|
+
*/
|
|
148
|
+
static cancelFrame (frameId) {
|
|
149
|
+
globalThis.cancelAnimationFrame(frameId)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Get emit event name.
|
|
154
|
+
*
|
|
155
|
+
* @override
|
|
156
|
+
* @returns {Readonly<{
|
|
157
|
+
* SORT_CHANGE: 'sort:change'
|
|
158
|
+
* SELECTION_CHANGE: 'selection:change'
|
|
159
|
+
* UPDATE_SELECTED_ROW_KEYS: 'update:selectedRowKeys'
|
|
160
|
+
* RANGE_CHANGE: 'range:change'
|
|
161
|
+
* REACH_END: 'reach-end'
|
|
162
|
+
* ROW_CLICK: 'row:click'
|
|
163
|
+
* }>}
|
|
164
|
+
*/
|
|
165
|
+
static get EMIT_EVENT_NAME () {
|
|
166
|
+
return {
|
|
167
|
+
SORT_CHANGE: 'sort:change',
|
|
168
|
+
SELECTION_CHANGE: 'selection:change',
|
|
169
|
+
UPDATE_SELECTED_ROW_KEYS: 'update:selectedRowKeys',
|
|
170
|
+
RANGE_CHANGE: 'range:change',
|
|
171
|
+
REACH_END: 'reach-end',
|
|
172
|
+
ROW_CLICK: 'row:click',
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Get emit options.
|
|
178
|
+
*
|
|
179
|
+
* @returns {{
|
|
180
|
+
* 'sort:change': (payload: {
|
|
181
|
+
* field: string
|
|
182
|
+
* direction: 'asc' | 'desc' | null
|
|
183
|
+
* }) => void
|
|
184
|
+
* 'selection:change': (payload: {
|
|
185
|
+
* selectedRowKeys: Array<string | number>
|
|
186
|
+
* }) => void
|
|
187
|
+
* 'update:selectedRowKeys': (selectedRowKeys: Array<string | number>) => void
|
|
188
|
+
* 'range:change': (payload: {
|
|
189
|
+
* first: number
|
|
190
|
+
* last: number
|
|
191
|
+
* }) => void
|
|
192
|
+
* 'reach-end': (payload: {
|
|
193
|
+
* first: number
|
|
194
|
+
* last: number
|
|
195
|
+
* loadedCount: number
|
|
196
|
+
* totalRecords: number
|
|
197
|
+
* }) => void
|
|
198
|
+
* 'row:click': (payload: {
|
|
199
|
+
* row: Record<string, unknown>
|
|
200
|
+
* rowKey: string | number | null
|
|
201
|
+
* }) => void
|
|
202
|
+
* }}
|
|
203
|
+
*/
|
|
204
|
+
static get EMIT_EVENT_HASH () {
|
|
205
|
+
return {
|
|
206
|
+
[this.EMIT_EVENT_NAME.SORT_CHANGE]: payload => {},
|
|
207
|
+
[this.EMIT_EVENT_NAME.SELECTION_CHANGE]: payload => {},
|
|
208
|
+
[this.EMIT_EVENT_NAME.UPDATE_SELECTED_ROW_KEYS]: selectedRowKeys => {},
|
|
209
|
+
[this.EMIT_EVENT_NAME.RANGE_CHANGE]: payload => {},
|
|
210
|
+
[this.EMIT_EVENT_NAME.REACH_END]: payload => {},
|
|
211
|
+
[this.EMIT_EVENT_NAME.ROW_CLICK]: payload => {},
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* get: Constructor of this class.
|
|
217
|
+
*
|
|
218
|
+
* @override
|
|
219
|
+
* @returns {typeof FuroTableContext}
|
|
220
|
+
*/
|
|
221
|
+
get Ctor () {
|
|
222
|
+
return /** @type {typeof FuroTableContext} */ (
|
|
223
|
+
this.constructor
|
|
224
|
+
)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Get internal selected row keys ref (uncontrolled mode).
|
|
229
|
+
*
|
|
230
|
+
* @returns {import('vue').Ref<Array<string | number>>}
|
|
231
|
+
*/
|
|
232
|
+
get selectedRowKeysRef () {
|
|
233
|
+
return this.refHash.selectedRowKeysRef
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Get the ref holding the last selection this table emitted.
|
|
238
|
+
*
|
|
239
|
+
* @returns {import('vue').Ref<PendingSelection | null>}
|
|
240
|
+
*/
|
|
241
|
+
get pendingSelectionRef () {
|
|
242
|
+
return this.refHash.pendingSelectionRef
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Get the scroll-top ref driving virtual windowing.
|
|
247
|
+
*
|
|
248
|
+
* @returns {import('vue').Ref<number>}
|
|
249
|
+
*/
|
|
250
|
+
get scrollTopRef () {
|
|
251
|
+
return this.refHash.scrollTopRef
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Get the per-column width overrides (px) set by resizing.
|
|
256
|
+
*
|
|
257
|
+
* @returns {import('vue').Ref<Record<string, number>>}
|
|
258
|
+
*/
|
|
259
|
+
get columnWidthsRef () {
|
|
260
|
+
return this.refHash.columnWidthsRef
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Parcel passed from parent via props.
|
|
265
|
+
*
|
|
266
|
+
* @override
|
|
267
|
+
* @returns {NonNullable<Props['parcel']>}
|
|
268
|
+
*/
|
|
269
|
+
get parcel () {
|
|
270
|
+
return {
|
|
271
|
+
columns: [],
|
|
272
|
+
rows: [],
|
|
273
|
+
rowKey: DEFAULT_ROW_KEY,
|
|
274
|
+
selectable: false,
|
|
275
|
+
selectedRowKeys: null,
|
|
276
|
+
sort: null,
|
|
277
|
+
loading: false,
|
|
278
|
+
errorMessage: null,
|
|
279
|
+
emptyText: DEFAULT_EMPTY_TEXT,
|
|
280
|
+
virtual: null,
|
|
281
|
+
totalRecords: null,
|
|
282
|
+
rowOffset: 0,
|
|
283
|
+
resizable: false,
|
|
284
|
+
compact: false,
|
|
285
|
+
gridLines: false,
|
|
286
|
+
rowActionsLabel: DEFAULT_ROW_ACTIONS_LABEL,
|
|
287
|
+
rowActionsWidth: DEFAULT_ROW_ACTIONS_WIDTH,
|
|
288
|
+
...this.props.parcel,
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Extract the column configuration.
|
|
294
|
+
*
|
|
295
|
+
* @returns {Array<FuroTableColumn>}
|
|
296
|
+
*/
|
|
297
|
+
extractColumns () {
|
|
298
|
+
return this.parcel.columns
|
|
299
|
+
?? []
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Extract the raw row records.
|
|
304
|
+
*
|
|
305
|
+
* @returns {Array<Record<string, unknown>>}
|
|
306
|
+
*/
|
|
307
|
+
extractRows () {
|
|
308
|
+
return this.parcel.rows
|
|
309
|
+
?? []
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Extract the field used as the unique row key.
|
|
314
|
+
*
|
|
315
|
+
* @returns {string}
|
|
316
|
+
*/
|
|
317
|
+
extractRowKey () {
|
|
318
|
+
return this.parcel.rowKey
|
|
319
|
+
?? DEFAULT_ROW_KEY
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Extract the empty-state text.
|
|
324
|
+
*
|
|
325
|
+
* @returns {string}
|
|
326
|
+
*/
|
|
327
|
+
extractEmptyText () {
|
|
328
|
+
return this.parcel.emptyText
|
|
329
|
+
?? DEFAULT_EMPTY_TEXT
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Extract the error message, when present.
|
|
334
|
+
*
|
|
335
|
+
* @returns {string | null}
|
|
336
|
+
*/
|
|
337
|
+
extractErrorMessage () {
|
|
338
|
+
return this.parcel.errorMessage
|
|
339
|
+
?? null
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Total rendered column count (data columns plus the selection column).
|
|
344
|
+
*
|
|
345
|
+
* @returns {number}
|
|
346
|
+
*/
|
|
347
|
+
extractTotalColumnCount () {
|
|
348
|
+
const selectionColumnCount = this.isSelectable()
|
|
349
|
+
? 1
|
|
350
|
+
: 0
|
|
351
|
+
|
|
352
|
+
const actionsColumnCount = this.hasRowActionsColumn()
|
|
353
|
+
? 1
|
|
354
|
+
: 0
|
|
355
|
+
|
|
356
|
+
return this.extractColumns().length
|
|
357
|
+
+ selectionColumnCount
|
|
358
|
+
+ actionsColumnCount
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Extract how many columns a placeholder row's skeleton spans.
|
|
363
|
+
*
|
|
364
|
+
* Data columns only, so an unloaded row keeps its selection and actions cells.
|
|
365
|
+
*
|
|
366
|
+
* @returns {number}
|
|
367
|
+
*/
|
|
368
|
+
extractPlaceholderColumnCount () {
|
|
369
|
+
return this.extractColumns().length
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Whether a trailing row-actions column is rendered.
|
|
374
|
+
*
|
|
375
|
+
* True when the consumer provides a `row-actions` slot; the column then holds
|
|
376
|
+
* that slot's per-row content (edit/delete buttons, an action menu, etc.).
|
|
377
|
+
*
|
|
378
|
+
* @returns {boolean}
|
|
379
|
+
*/
|
|
380
|
+
hasRowActionsColumn () {
|
|
381
|
+
return Boolean(this.componentContext.slots?.['row-actions'])
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Whether a footer region is rendered.
|
|
386
|
+
*
|
|
387
|
+
* True when the consumer provides a `footer` slot (bulk-action bar, summary
|
|
388
|
+
* row, etc.).
|
|
389
|
+
*
|
|
390
|
+
* @returns {boolean}
|
|
391
|
+
*/
|
|
392
|
+
hasFooterSlot () {
|
|
393
|
+
return Boolean(this.componentContext.slots?.footer)
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Extract the header label for the row-actions column.
|
|
398
|
+
*
|
|
399
|
+
* @returns {string}
|
|
400
|
+
*/
|
|
401
|
+
extractRowActionsLabel () {
|
|
402
|
+
return this.parcel.rowActionsLabel
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Extract the `<col>` inline style for the row-actions column.
|
|
407
|
+
*
|
|
408
|
+
* @returns {Record<string, string>}
|
|
409
|
+
*/
|
|
410
|
+
extractRowActionsColStyle () {
|
|
411
|
+
return {
|
|
412
|
+
width: this.parcel.rowActionsWidth,
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Extract root class names.
|
|
418
|
+
*
|
|
419
|
+
* @returns {Record<string, boolean>}
|
|
420
|
+
*/
|
|
421
|
+
extractRootClassNames () {
|
|
422
|
+
return {
|
|
423
|
+
'unit-table': true,
|
|
424
|
+
loading: this.isLoading(),
|
|
425
|
+
compact: this.isCompact(),
|
|
426
|
+
'grid-lines': this.hasGridLines(),
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Whether the table renders in the compact (denser) density.
|
|
432
|
+
*
|
|
433
|
+
* @returns {boolean}
|
|
434
|
+
*/
|
|
435
|
+
isCompact () {
|
|
436
|
+
return Boolean(this.parcel.compact)
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Whether the table renders vertical grid lines between columns.
|
|
441
|
+
*
|
|
442
|
+
* @returns {boolean}
|
|
443
|
+
*/
|
|
444
|
+
hasGridLines () {
|
|
445
|
+
return Boolean(this.parcel.gridLines)
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Extract the label rendered in a column header.
|
|
450
|
+
*
|
|
451
|
+
* @param {{
|
|
452
|
+
* column: FuroTableColumn
|
|
453
|
+
* }} params - Parameters of this method.
|
|
454
|
+
* @returns {string}
|
|
455
|
+
*/
|
|
456
|
+
extractColumnLabel ({
|
|
457
|
+
column,
|
|
458
|
+
}) {
|
|
459
|
+
return column.label
|
|
460
|
+
?? column.field
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Extract the text alignment for a column.
|
|
465
|
+
*
|
|
466
|
+
* @param {{
|
|
467
|
+
* column: FuroTableColumn
|
|
468
|
+
* }} params - Parameters of this method.
|
|
469
|
+
* @returns {'start' | 'center' | 'end'}
|
|
470
|
+
*/
|
|
471
|
+
extractColumnAlign ({
|
|
472
|
+
column,
|
|
473
|
+
}) {
|
|
474
|
+
return column.align
|
|
475
|
+
?? 'start'
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Whether columns can be resized by dragging their right edge.
|
|
480
|
+
*
|
|
481
|
+
* @returns {boolean}
|
|
482
|
+
*/
|
|
483
|
+
isResizable () {
|
|
484
|
+
return Boolean(this.parcel.resizable)
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Extract the `<col>` inline style for the selection column.
|
|
489
|
+
*
|
|
490
|
+
* @returns {Record<string, string>}
|
|
491
|
+
*/
|
|
492
|
+
extractSelectionColStyle () {
|
|
493
|
+
return {
|
|
494
|
+
width: SELECTION_COLUMN_WIDTH,
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Extract the `<col>` inline style for a data column. A resized width wins
|
|
500
|
+
* over a configured `column.width`; otherwise the column is unsized (the
|
|
501
|
+
* fixed table layout shares remaining width).
|
|
502
|
+
*
|
|
503
|
+
* @param {{
|
|
504
|
+
* column: FuroTableColumn
|
|
505
|
+
* }} params - Parameters of this method.
|
|
506
|
+
* @returns {Record<string, string>}
|
|
507
|
+
*/
|
|
508
|
+
extractColumnColStyle ({
|
|
509
|
+
column,
|
|
510
|
+
}) {
|
|
511
|
+
const resizedWidth = this.columnWidthsRef.value[column.field]
|
|
512
|
+
?? null
|
|
513
|
+
|
|
514
|
+
if (resizedWidth !== null) {
|
|
515
|
+
return {
|
|
516
|
+
width: `${resizedWidth}px`,
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
if (column.width) {
|
|
521
|
+
return {
|
|
522
|
+
width: column.width,
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
return {}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Whether row selection is enabled.
|
|
531
|
+
*
|
|
532
|
+
* @returns {boolean}
|
|
533
|
+
*/
|
|
534
|
+
isSelectable () {
|
|
535
|
+
return Boolean(this.parcel.selectable)
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Whether the table is in the loading state.
|
|
540
|
+
*
|
|
541
|
+
* @returns {boolean}
|
|
542
|
+
*/
|
|
543
|
+
isLoading () {
|
|
544
|
+
return Boolean(this.parcel.loading)
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Whether interaction with the table body is blocked.
|
|
549
|
+
*
|
|
550
|
+
* @returns {boolean}
|
|
551
|
+
*/
|
|
552
|
+
isInteractionBlocked () {
|
|
553
|
+
return this.isLoading()
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Extract the `inert` attribute for the scroll viewport. Any present value
|
|
558
|
+
* makes the subtree inert, so absence must be `null` and never `false`.
|
|
559
|
+
*
|
|
560
|
+
* @returns {true | null}
|
|
561
|
+
*/
|
|
562
|
+
extractInertAttribute () {
|
|
563
|
+
if (!this.isInteractionBlocked()) {
|
|
564
|
+
return null
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
return true
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* Extract the `aria-busy` attribute for the table root.
|
|
572
|
+
*
|
|
573
|
+
* @returns {'true' | null}
|
|
574
|
+
*/
|
|
575
|
+
extractAriaBusyAttribute () {
|
|
576
|
+
if (!this.isLoading()) {
|
|
577
|
+
return null
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
return 'true'
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* Whether the table is in the error state.
|
|
585
|
+
*
|
|
586
|
+
* @returns {boolean}
|
|
587
|
+
*/
|
|
588
|
+
hasError () {
|
|
589
|
+
return this.extractErrorMessage() !== null
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* Whether the error state should render.
|
|
594
|
+
*
|
|
595
|
+
* @returns {boolean}
|
|
596
|
+
*/
|
|
597
|
+
shouldShowError () {
|
|
598
|
+
return this.hasError()
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* Whether the in-body loading spinner should render. Only the first load (no
|
|
603
|
+
* rows yet) shows the centered spinner row; a refresh over existing rows keeps
|
|
604
|
+
* the rows mounted and shows the floating overlay instead (see
|
|
605
|
+
* `shouldShowLoadingOverlay`), so the layout never collapses.
|
|
606
|
+
*
|
|
607
|
+
* @returns {boolean}
|
|
608
|
+
*/
|
|
609
|
+
shouldShowLoading () {
|
|
610
|
+
if (this.hasError()) {
|
|
611
|
+
return false
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
if (!this.isLoading()) {
|
|
615
|
+
return false
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
return this.extractRows().length === 0
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* Whether the floating loading overlay should render. Used on a refresh, when
|
|
623
|
+
* rows are already present: the overlay is layered above the whole table
|
|
624
|
+
* (out of flow) so it dims and blocks the current rows without a layout shift.
|
|
625
|
+
*
|
|
626
|
+
* @returns {boolean}
|
|
627
|
+
*/
|
|
628
|
+
shouldShowLoadingOverlay () {
|
|
629
|
+
if (this.hasError()) {
|
|
630
|
+
return false
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
if (!this.isLoading()) {
|
|
634
|
+
return false
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
return this.extractRows().length > 0
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* Whether the empty state should render.
|
|
642
|
+
*
|
|
643
|
+
* @returns {boolean}
|
|
644
|
+
*/
|
|
645
|
+
shouldShowEmpty () {
|
|
646
|
+
if (this.hasError()) {
|
|
647
|
+
return false
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
if (this.isLoading()) {
|
|
651
|
+
return false
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
return this.extractRows().length === 0
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
/**
|
|
658
|
+
* Whether the data rows should render.
|
|
659
|
+
*
|
|
660
|
+
* @returns {boolean}
|
|
661
|
+
*/
|
|
662
|
+
shouldShowRows () {
|
|
663
|
+
if (this.hasError()) {
|
|
664
|
+
return false
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
return this.extractRows().length > 0
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* Whether a column header is sortable.
|
|
672
|
+
*
|
|
673
|
+
* @param {{
|
|
674
|
+
* column: FuroTableColumn
|
|
675
|
+
* }} params - Parameters of this method.
|
|
676
|
+
* @returns {boolean}
|
|
677
|
+
*/
|
|
678
|
+
isColumnSortable ({
|
|
679
|
+
column,
|
|
680
|
+
}) {
|
|
681
|
+
return Boolean(column.sortable)
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* Extract the controlled sort indicator.
|
|
686
|
+
*
|
|
687
|
+
* @returns {FuroTableSort | null}
|
|
688
|
+
*/
|
|
689
|
+
extractSort () {
|
|
690
|
+
return this.parcel.sort
|
|
691
|
+
?? null
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
* Extract the active sort direction for a column, when sorted by it.
|
|
696
|
+
*
|
|
697
|
+
* @param {{
|
|
698
|
+
* column: FuroTableColumn
|
|
699
|
+
* }} params - Parameters of this method.
|
|
700
|
+
* @returns {'asc' | 'desc' | null}
|
|
701
|
+
*/
|
|
702
|
+
extractSortDirectionFor ({
|
|
703
|
+
column,
|
|
704
|
+
}) {
|
|
705
|
+
const sort = this.extractSort()
|
|
706
|
+
|
|
707
|
+
if (!sort) {
|
|
708
|
+
return null
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
if (sort.field !== column.field) {
|
|
712
|
+
return null
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
return sort.direction
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/**
|
|
719
|
+
* Whether the table is currently sorted by a column.
|
|
720
|
+
*
|
|
721
|
+
* @param {{
|
|
722
|
+
* column: FuroTableColumn
|
|
723
|
+
* }} params - Parameters of this method.
|
|
724
|
+
* @returns {boolean}
|
|
725
|
+
*/
|
|
726
|
+
isSortedBy ({
|
|
727
|
+
column,
|
|
728
|
+
}) {
|
|
729
|
+
return this.extractSortDirectionFor({ column }) !== null
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
/**
|
|
733
|
+
* Extract the sort indicator icon name for a column. Returns a neutral
|
|
734
|
+
* (bidirectional) icon when the column is sortable but not the active sort,
|
|
735
|
+
* and a directional icon when it is.
|
|
736
|
+
*
|
|
737
|
+
* @param {{
|
|
738
|
+
* column: FuroTableColumn
|
|
739
|
+
* }} params - Parameters of this method.
|
|
740
|
+
* @returns {string}
|
|
741
|
+
*/
|
|
742
|
+
extractSortIconName ({
|
|
743
|
+
column,
|
|
744
|
+
}) {
|
|
745
|
+
const direction = this.extractSortDirectionFor({ column })
|
|
746
|
+
|
|
747
|
+
if (direction === null) {
|
|
748
|
+
return SORT_ICON_NEUTRAL
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
return SORT_ICON_HASH[direction]
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
/**
|
|
755
|
+
* Extract the controlled selected row keys.
|
|
756
|
+
*
|
|
757
|
+
* @returns {Array<string | number>}
|
|
758
|
+
*/
|
|
759
|
+
extractSelectedRowKeys () {
|
|
760
|
+
if (this.isSelectionControlled()) {
|
|
761
|
+
return this.parcel.selectedRowKeys
|
|
762
|
+
?? []
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
return this.selectedRowKeysRef.value
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
* Extract the number of currently selected rows.
|
|
770
|
+
*
|
|
771
|
+
* Exposed to the `footer` slot so a bulk-action bar can render "N selected".
|
|
772
|
+
*
|
|
773
|
+
* @returns {number}
|
|
774
|
+
*/
|
|
775
|
+
extractSelectedCount () {
|
|
776
|
+
return this.extractSelectedRowKeys()
|
|
777
|
+
.length
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* Extract the scope object passed to the `footer` slot.
|
|
782
|
+
*
|
|
783
|
+
* A single object (single-word slot key) so a bulk-action bar can read the
|
|
784
|
+
* selected keys and count without any kebab/camel binding ambiguity.
|
|
785
|
+
*
|
|
786
|
+
* @returns {{
|
|
787
|
+
* keys: Array<string | number>
|
|
788
|
+
* count: number
|
|
789
|
+
* }}
|
|
790
|
+
*/
|
|
791
|
+
extractFooterSelection () {
|
|
792
|
+
return {
|
|
793
|
+
keys: this.extractSelectedRowKeys(),
|
|
794
|
+
count: this.extractSelectedCount(),
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
/**
|
|
799
|
+
* Extract the keys of every row.
|
|
800
|
+
*
|
|
801
|
+
* @returns {Array<string | number>}
|
|
802
|
+
*/
|
|
803
|
+
extractAllRowKeys () {
|
|
804
|
+
const rowKeyField = this.extractRowKey()
|
|
805
|
+
|
|
806
|
+
return /** @type {Array<string | number>} */ (
|
|
807
|
+
this.extractRows()
|
|
808
|
+
.map(row => row?.[rowKeyField] ?? null)
|
|
809
|
+
.filter(rowKey => rowKey !== null)
|
|
810
|
+
)
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* Whether selection is controlled by the parent (via `parcel.selectedRowKeys`).
|
|
815
|
+
*
|
|
816
|
+
* @returns {boolean}
|
|
817
|
+
*/
|
|
818
|
+
isSelectionControlled () {
|
|
819
|
+
return (this.parcel.selectedRowKeys ?? null) !== null
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
/**
|
|
823
|
+
* Whether every row is currently selected.
|
|
824
|
+
*
|
|
825
|
+
* @returns {boolean}
|
|
826
|
+
*/
|
|
827
|
+
hasAllRowsSelected () {
|
|
828
|
+
const rowKeys = this.extractAllRowKeys()
|
|
829
|
+
|
|
830
|
+
if (rowKeys.length === 0) {
|
|
831
|
+
return false
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
const selectedRowKeys = this.extractSelectedRowKeys()
|
|
835
|
+
|
|
836
|
+
return rowKeys.every(rowKey => selectedRowKeys.includes(rowKey))
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
/**
|
|
840
|
+
* Whether some (but not all) rows are selected.
|
|
841
|
+
*
|
|
842
|
+
* @returns {boolean}
|
|
843
|
+
*/
|
|
844
|
+
hasIndeterminateSelection () {
|
|
845
|
+
const selectedRowKeys = this.extractSelectedRowKeys()
|
|
846
|
+
|
|
847
|
+
if (selectedRowKeys.length === 0) {
|
|
848
|
+
return false
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
return !this.hasAllRowsSelected()
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
/**
|
|
855
|
+
* Resolve the parcel for the select-all header checkbox.
|
|
856
|
+
*
|
|
857
|
+
* @returns {{
|
|
858
|
+
* value: boolean
|
|
859
|
+
* indeterminate: boolean
|
|
860
|
+
* disabled: boolean
|
|
861
|
+
* }}
|
|
862
|
+
*/
|
|
863
|
+
extractSelectAllParcel () {
|
|
864
|
+
return {
|
|
865
|
+
value: this.hasAllRowsSelected(),
|
|
866
|
+
indeterminate: this.hasIndeterminateSelection(),
|
|
867
|
+
disabled: this.isSelectAllDisabled(),
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
/**
|
|
872
|
+
* Whether the select-all header checkbox is disabled. Disabled while loading
|
|
873
|
+
* (interaction is blocked during a refresh) and when there are no rows.
|
|
874
|
+
*
|
|
875
|
+
* @returns {boolean}
|
|
876
|
+
*/
|
|
877
|
+
isSelectAllDisabled () {
|
|
878
|
+
if (this.isInteractionBlocked()) {
|
|
879
|
+
return true
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
return this.extractRows().length === 0
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
/**
|
|
886
|
+
* Resolve the parcel for a single row's selection checkbox.
|
|
887
|
+
*
|
|
888
|
+
* @param {{
|
|
889
|
+
* rowContext: FuroTableRowItemContext
|
|
890
|
+
* }} params - Parameters of this method.
|
|
891
|
+
* @returns {{
|
|
892
|
+
* value: boolean
|
|
893
|
+
* disabled: boolean
|
|
894
|
+
* }}
|
|
895
|
+
*/
|
|
896
|
+
extractRowSelectionParcel ({
|
|
897
|
+
rowContext,
|
|
898
|
+
}) {
|
|
899
|
+
return {
|
|
900
|
+
value: rowContext.isSelected(),
|
|
901
|
+
disabled: this.isInteractionBlocked(),
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
/**
|
|
906
|
+
* Create the per-row presentation contexts for the current rows.
|
|
907
|
+
*
|
|
908
|
+
* @returns {Array<FuroTableRowItemContext>}
|
|
909
|
+
*/
|
|
910
|
+
createRowItemContexts () {
|
|
911
|
+
const rowKeyField = this.extractRowKey()
|
|
912
|
+
const selectedRowKeys = this.extractSelectedRowKeys()
|
|
913
|
+
|
|
914
|
+
return this.extractRows()
|
|
915
|
+
.map(row => FuroTableRowItemContext.create({
|
|
916
|
+
row,
|
|
917
|
+
rowKeyField,
|
|
918
|
+
selectedRowKeys,
|
|
919
|
+
}))
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
/**
|
|
923
|
+
* Resolve the next sort direction in the tri-state cycle:
|
|
924
|
+
* unsorted → ascending → descending → unsorted.
|
|
925
|
+
*
|
|
926
|
+
* @param {{
|
|
927
|
+
* current: 'asc' | 'desc' | null
|
|
928
|
+
* }} params - Parameters of this method.
|
|
929
|
+
* @returns {'asc' | 'desc' | null}
|
|
930
|
+
*/
|
|
931
|
+
resolveNextSortDirection ({
|
|
932
|
+
current,
|
|
933
|
+
}) {
|
|
934
|
+
if (current === 'asc') {
|
|
935
|
+
return 'desc'
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
if (current === 'desc') {
|
|
939
|
+
return null
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
return 'asc'
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
/**
|
|
946
|
+
* Handle a sortable header click. Cycles ascending → descending → cleared and
|
|
947
|
+
* emits the sort intent upward; the parent re-sorts and re-passes the parcel.
|
|
948
|
+
* A `null` direction restores the default (unsorted) order.
|
|
949
|
+
*
|
|
950
|
+
* @param {{
|
|
951
|
+
* column: FuroTableColumn
|
|
952
|
+
* }} params - Parameters of this method.
|
|
953
|
+
*/
|
|
954
|
+
onHeaderClick ({
|
|
955
|
+
column,
|
|
956
|
+
}) {
|
|
957
|
+
if (!this.isColumnSortable({ column })) {
|
|
958
|
+
return
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
const direction = this.resolveNextSortDirection({
|
|
962
|
+
current: this.extractSortDirectionFor({ column }),
|
|
963
|
+
})
|
|
964
|
+
|
|
965
|
+
this.emit(
|
|
966
|
+
this.EMIT_EVENT_NAME.SORT_CHANGE,
|
|
967
|
+
{
|
|
968
|
+
field: column.field,
|
|
969
|
+
direction,
|
|
970
|
+
}
|
|
971
|
+
)
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
/**
|
|
975
|
+
* Toggle selection of a single row. A `null` key (row without an identifier)
|
|
976
|
+
* is ignored.
|
|
977
|
+
*
|
|
978
|
+
* @param {{
|
|
979
|
+
* rowKey: string | number | null
|
|
980
|
+
* }} params - Parameters of this method.
|
|
981
|
+
*/
|
|
982
|
+
onToggleRow ({
|
|
983
|
+
rowKey,
|
|
984
|
+
}) {
|
|
985
|
+
if (rowKey === null) {
|
|
986
|
+
return
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
const selectedRowKeys = this.extractPendingSelectedRowKeys()
|
|
990
|
+
const nextSelectedRowKeys = selectedRowKeys.includes(rowKey)
|
|
991
|
+
? selectedRowKeys.filter(it => it !== rowKey)
|
|
992
|
+
: [
|
|
993
|
+
...selectedRowKeys,
|
|
994
|
+
rowKey,
|
|
995
|
+
]
|
|
996
|
+
|
|
997
|
+
this.applySelection({
|
|
998
|
+
selectedRowKeys: nextSelectedRowKeys,
|
|
999
|
+
})
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
/**
|
|
1003
|
+
* Toggle selection of every row (select-all / clear-all).
|
|
1004
|
+
*/
|
|
1005
|
+
onToggleSelectAll () {
|
|
1006
|
+
const nextSelectedRowKeys = this.hasAllPendingRowsSelected()
|
|
1007
|
+
? []
|
|
1008
|
+
: this.extractAllRowKeys()
|
|
1009
|
+
|
|
1010
|
+
this.applySelection({
|
|
1011
|
+
selectedRowKeys: nextSelectedRowKeys,
|
|
1012
|
+
})
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
/**
|
|
1016
|
+
* Extract the selection a toggle must build on.
|
|
1017
|
+
*
|
|
1018
|
+
* When controlled, `parcel.selectedRowKeys` lags a tick behind the emit, so
|
|
1019
|
+
* consecutive toggles must accumulate onto the last emitted selection instead.
|
|
1020
|
+
*
|
|
1021
|
+
* @returns {Array<string | number>}
|
|
1022
|
+
*/
|
|
1023
|
+
extractPendingSelectedRowKeys () {
|
|
1024
|
+
const selectedRowKeys = this.extractSelectedRowKeys()
|
|
1025
|
+
|
|
1026
|
+
const emittedSelection = this.pendingSelectionRef.value
|
|
1027
|
+
|
|
1028
|
+
if (emittedSelection?.derivedFrom !== selectedRowKeys) {
|
|
1029
|
+
return selectedRowKeys
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
return emittedSelection.selectedRowKeys
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
/**
|
|
1036
|
+
* Remember a just-emitted selection so a follow-up toggle builds on it.
|
|
1037
|
+
*
|
|
1038
|
+
* @param {{
|
|
1039
|
+
* selectedRowKeys: Array<string | number>
|
|
1040
|
+
* }} params - Parameters of this method.
|
|
1041
|
+
*/
|
|
1042
|
+
rememberPendingSelection ({
|
|
1043
|
+
selectedRowKeys,
|
|
1044
|
+
}) {
|
|
1045
|
+
this.pendingSelectionRef.value = {
|
|
1046
|
+
derivedFrom: this.extractSelectedRowKeys(),
|
|
1047
|
+
selectedRowKeys,
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
/**
|
|
1052
|
+
* Whether every row is selected in the pending selection.
|
|
1053
|
+
*
|
|
1054
|
+
* @returns {boolean}
|
|
1055
|
+
*/
|
|
1056
|
+
hasAllPendingRowsSelected () {
|
|
1057
|
+
const allRowKeys = this.extractAllRowKeys()
|
|
1058
|
+
|
|
1059
|
+
if (allRowKeys.length === 0) {
|
|
1060
|
+
return false
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
const pendingSelectedRowKeys = this.extractPendingSelectedRowKeys()
|
|
1064
|
+
|
|
1065
|
+
return allRowKeys.every(it => pendingSelectedRowKeys.includes(it))
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
/**
|
|
1069
|
+
* Apply a selection change: update internal state when uncontrolled, then
|
|
1070
|
+
* emit the change upward.
|
|
1071
|
+
*
|
|
1072
|
+
* @param {{
|
|
1073
|
+
* selectedRowKeys: Array<string | number>
|
|
1074
|
+
* }} params - Parameters of this method.
|
|
1075
|
+
*/
|
|
1076
|
+
applySelection ({
|
|
1077
|
+
selectedRowKeys,
|
|
1078
|
+
}) {
|
|
1079
|
+
if (!this.isSelectionControlled()) {
|
|
1080
|
+
this.selectedRowKeysRef.value = selectedRowKeys
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
this.rememberPendingSelection({
|
|
1084
|
+
selectedRowKeys,
|
|
1085
|
+
})
|
|
1086
|
+
|
|
1087
|
+
this.emit(
|
|
1088
|
+
this.EMIT_EVENT_NAME.UPDATE_SELECTED_ROW_KEYS,
|
|
1089
|
+
selectedRowKeys
|
|
1090
|
+
)
|
|
1091
|
+
|
|
1092
|
+
this.emit(
|
|
1093
|
+
this.EMIT_EVENT_NAME.SELECTION_CHANGE,
|
|
1094
|
+
{
|
|
1095
|
+
selectedRowKeys,
|
|
1096
|
+
}
|
|
1097
|
+
)
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
/**
|
|
1101
|
+
* Handle a click on a data row. Emits the row and its key so the consumer can
|
|
1102
|
+
* react (e.g. navigate to a detail view). The built-in selection cell stops
|
|
1103
|
+
* propagation; interactive cell-slot content should `@click.stop` to opt out.
|
|
1104
|
+
*
|
|
1105
|
+
* @param {{
|
|
1106
|
+
* rowContext: FuroTableRowItemContext
|
|
1107
|
+
* }} params - Parameters of this method.
|
|
1108
|
+
*/
|
|
1109
|
+
onRowClick ({
|
|
1110
|
+
rowContext,
|
|
1111
|
+
}) {
|
|
1112
|
+
this.emit(
|
|
1113
|
+
this.EMIT_EVENT_NAME.ROW_CLICK,
|
|
1114
|
+
{
|
|
1115
|
+
row: rowContext.row,
|
|
1116
|
+
rowKey: rowContext.extractKey(),
|
|
1117
|
+
}
|
|
1118
|
+
)
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
/**
|
|
1122
|
+
* Handle a click on a virtual-window row. Placeholder rows (no loaded context)
|
|
1123
|
+
* are ignored.
|
|
1124
|
+
*
|
|
1125
|
+
* @param {{
|
|
1126
|
+
* entry: {
|
|
1127
|
+
* index: number
|
|
1128
|
+
* rowContext: FuroTableRowItemContext | null
|
|
1129
|
+
* }
|
|
1130
|
+
* }} params - Parameters of this method.
|
|
1131
|
+
*/
|
|
1132
|
+
onVirtualRowClick ({
|
|
1133
|
+
entry,
|
|
1134
|
+
}) {
|
|
1135
|
+
if (entry.rowContext === null) {
|
|
1136
|
+
return
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
this.onRowClick({
|
|
1140
|
+
rowContext: entry.rowContext,
|
|
1141
|
+
})
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
/**
|
|
1145
|
+
* Whether fixed-height virtual scrolling is enabled.
|
|
1146
|
+
*
|
|
1147
|
+
* @returns {boolean}
|
|
1148
|
+
*/
|
|
1149
|
+
isVirtual () {
|
|
1150
|
+
return this.extractVirtualConfig() !== null
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
/**
|
|
1154
|
+
* Extract the virtual-scroll configuration, when enabled.
|
|
1155
|
+
*
|
|
1156
|
+
* @returns {FuroTableVirtual | null}
|
|
1157
|
+
*/
|
|
1158
|
+
extractVirtualConfig () {
|
|
1159
|
+
return this.parcel.virtual
|
|
1160
|
+
?? null
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
/**
|
|
1164
|
+
* Extract the fixed row height in pixels.
|
|
1165
|
+
*
|
|
1166
|
+
* @returns {number}
|
|
1167
|
+
*/
|
|
1168
|
+
extractItemSize () {
|
|
1169
|
+
return this.extractVirtualConfig()?.itemSize
|
|
1170
|
+
?? 0
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
/**
|
|
1174
|
+
* Extract the scroll viewport height in pixels.
|
|
1175
|
+
*
|
|
1176
|
+
* @returns {number}
|
|
1177
|
+
*/
|
|
1178
|
+
extractViewportHeight () {
|
|
1179
|
+
return this.extractVirtualConfig()?.height
|
|
1180
|
+
?? 0
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
/**
|
|
1184
|
+
* Extract how many extra rows render above and below the viewport.
|
|
1185
|
+
*
|
|
1186
|
+
* @returns {number}
|
|
1187
|
+
*/
|
|
1188
|
+
extractOverscan () {
|
|
1189
|
+
return this.extractVirtualConfig()?.overscan
|
|
1190
|
+
?? DEFAULT_OVERSCAN
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
/**
|
|
1194
|
+
* Extract the global index of the first loaded row (sparse windows).
|
|
1195
|
+
*
|
|
1196
|
+
* @returns {number}
|
|
1197
|
+
*/
|
|
1198
|
+
extractRowOffset () {
|
|
1199
|
+
return this.parcel.rowOffset
|
|
1200
|
+
?? 0
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
/**
|
|
1204
|
+
* Extract the total row count of the full dataset. Falls back to the loaded
|
|
1205
|
+
* row count when not provided.
|
|
1206
|
+
*
|
|
1207
|
+
* @returns {number}
|
|
1208
|
+
*/
|
|
1209
|
+
extractTotalRowCount () {
|
|
1210
|
+
return this.parcel.totalRecords
|
|
1211
|
+
?? this.extractRows().length
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
/**
|
|
1215
|
+
* Extract the global index one past the last loaded row.
|
|
1216
|
+
*
|
|
1217
|
+
* @returns {number}
|
|
1218
|
+
*/
|
|
1219
|
+
extractLoadedEnd () {
|
|
1220
|
+
return this.extractRowOffset()
|
|
1221
|
+
+ this.extractRows().length
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
/**
|
|
1225
|
+
* Extract how close (in rows) to the end of the loaded window a downward
|
|
1226
|
+
* scroll must get before `reach-end` fires.
|
|
1227
|
+
*
|
|
1228
|
+
* @returns {number}
|
|
1229
|
+
*/
|
|
1230
|
+
extractEndThreshold () {
|
|
1231
|
+
return this.extractVirtualConfig()?.endThreshold
|
|
1232
|
+
?? DEFAULT_END_THRESHOLD
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
/**
|
|
1236
|
+
* Extract the inline style for the scroll container.
|
|
1237
|
+
*
|
|
1238
|
+
* @returns {Record<string, string>}
|
|
1239
|
+
*/
|
|
1240
|
+
extractScrollStyle () {
|
|
1241
|
+
if (!this.isVirtual()) {
|
|
1242
|
+
return {}
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
return {
|
|
1246
|
+
height: `${this.extractViewportHeight()}px`,
|
|
1247
|
+
overflowY: 'auto',
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
/**
|
|
1252
|
+
* Extract the inline style for a fixed-height virtual row.
|
|
1253
|
+
*
|
|
1254
|
+
* @returns {Record<string, string>}
|
|
1255
|
+
*/
|
|
1256
|
+
extractRowStyle () {
|
|
1257
|
+
return {
|
|
1258
|
+
height: `${this.extractItemSize()}px`,
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
/**
|
|
1263
|
+
* Extract the number of rows that fit in the viewport.
|
|
1264
|
+
*
|
|
1265
|
+
* @returns {number}
|
|
1266
|
+
*/
|
|
1267
|
+
extractVisibleCount () {
|
|
1268
|
+
const itemSize = this.extractItemSize()
|
|
1269
|
+
|
|
1270
|
+
if (itemSize <= 0) {
|
|
1271
|
+
return 0
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
return Math.ceil(this.extractViewportHeight() / itemSize)
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
/**
|
|
1278
|
+
* Extract the first global row index to render (overscan applied).
|
|
1279
|
+
*
|
|
1280
|
+
* @returns {number}
|
|
1281
|
+
*/
|
|
1282
|
+
extractFirstIndex () {
|
|
1283
|
+
return this.computeFirstIndex(this.scrollTopRef.value)
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
/**
|
|
1287
|
+
* Compute the first global row index (overscan applied) for a scroll offset.
|
|
1288
|
+
*
|
|
1289
|
+
* Kept separate from {@link extractFirstIndex} so `onScroll` can test a
|
|
1290
|
+
* prospective offset against the current window without mutating state.
|
|
1291
|
+
*
|
|
1292
|
+
* @param {number} scrollTop - Scroll offset in pixels.
|
|
1293
|
+
* @returns {number}
|
|
1294
|
+
*/
|
|
1295
|
+
computeFirstIndex (scrollTop) {
|
|
1296
|
+
const itemSize = this.extractItemSize()
|
|
1297
|
+
|
|
1298
|
+
if (itemSize <= 0) {
|
|
1299
|
+
return 0
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
const rawFirst = Math.floor(scrollTop / itemSize)
|
|
1303
|
+
- this.extractOverscan()
|
|
1304
|
+
|
|
1305
|
+
return Math.max(0, rawFirst)
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
/**
|
|
1309
|
+
* Extract the last global row index to render (overscan applied).
|
|
1310
|
+
*
|
|
1311
|
+
* @returns {number}
|
|
1312
|
+
*/
|
|
1313
|
+
extractLastIndex () {
|
|
1314
|
+
const total = this.extractTotalRowCount()
|
|
1315
|
+
|
|
1316
|
+
if (total <= 0) {
|
|
1317
|
+
return -1
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
const rawLast = this.extractFirstIndex()
|
|
1321
|
+
+ this.extractVisibleCount()
|
|
1322
|
+
+ (this.extractOverscan() * 2)
|
|
1323
|
+
|
|
1324
|
+
return Math.min(total - 1, rawLast)
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
/**
|
|
1328
|
+
* Extract the leading spacer height (rows skipped above the window).
|
|
1329
|
+
*
|
|
1330
|
+
* @returns {number}
|
|
1331
|
+
*/
|
|
1332
|
+
extractLeadingSpacerHeight () {
|
|
1333
|
+
return this.extractFirstIndex()
|
|
1334
|
+
* this.extractItemSize()
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
/**
|
|
1338
|
+
* Extract the trailing spacer height (rows skipped below the window).
|
|
1339
|
+
*
|
|
1340
|
+
* @returns {number}
|
|
1341
|
+
*/
|
|
1342
|
+
extractTrailingSpacerHeight () {
|
|
1343
|
+
const trailingCount = this.extractTotalRowCount()
|
|
1344
|
+
- 1
|
|
1345
|
+
- this.extractLastIndex()
|
|
1346
|
+
|
|
1347
|
+
return Math.max(0, trailingCount)
|
|
1348
|
+
* this.extractItemSize()
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
/**
|
|
1352
|
+
* Create the per-row contexts for the visible virtual window. Entries whose
|
|
1353
|
+
* row is not in the loaded sparse window carry a `null` context (placeholder).
|
|
1354
|
+
*
|
|
1355
|
+
* @returns {Array<{
|
|
1356
|
+
* index: number
|
|
1357
|
+
* rowContext: FuroTableRowItemContext | null
|
|
1358
|
+
* }>}
|
|
1359
|
+
*/
|
|
1360
|
+
createVirtualRowItemContexts () {
|
|
1361
|
+
const firstIndex = this.extractFirstIndex()
|
|
1362
|
+
const lastIndex = this.extractLastIndex()
|
|
1363
|
+
|
|
1364
|
+
if (lastIndex < firstIndex) {
|
|
1365
|
+
return []
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
const rowKeyField = this.extractRowKey()
|
|
1369
|
+
const rowOffset = this.extractRowOffset()
|
|
1370
|
+
const selectedRowKeys = this.extractSelectedRowKeys()
|
|
1371
|
+
const rows = this.extractRows()
|
|
1372
|
+
|
|
1373
|
+
return Array.from(
|
|
1374
|
+
{
|
|
1375
|
+
length: lastIndex - firstIndex + 1,
|
|
1376
|
+
},
|
|
1377
|
+
(entry, position) => {
|
|
1378
|
+
const index = firstIndex + position
|
|
1379
|
+
const relativeIndex = index - rowOffset
|
|
1380
|
+
|
|
1381
|
+
const isWithinLoadedWindow = relativeIndex >= 0
|
|
1382
|
+
&& relativeIndex < rows.length
|
|
1383
|
+
|
|
1384
|
+
const row = isWithinLoadedWindow
|
|
1385
|
+
? (rows[relativeIndex] ?? null)
|
|
1386
|
+
: null
|
|
1387
|
+
|
|
1388
|
+
return {
|
|
1389
|
+
index,
|
|
1390
|
+
rowContext: row === null
|
|
1391
|
+
? null
|
|
1392
|
+
: FuroTableRowItemContext.create({
|
|
1393
|
+
row,
|
|
1394
|
+
rowKeyField,
|
|
1395
|
+
selectedRowKeys,
|
|
1396
|
+
}),
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
)
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
/**
|
|
1403
|
+
* Handle the raw scroll event from the viewport element.
|
|
1404
|
+
*
|
|
1405
|
+
* @param {Event} scrollEvent - The DOM scroll event.
|
|
1406
|
+
*/
|
|
1407
|
+
onScrollEvent (scrollEvent) {
|
|
1408
|
+
const { target } = scrollEvent
|
|
1409
|
+
const scrollElement = /** @type {HTMLElement | null} */ (target)
|
|
1410
|
+
const scrollTop = scrollElement?.scrollTop
|
|
1411
|
+
?? 0
|
|
1412
|
+
|
|
1413
|
+
this.pendingScrollTop = scrollTop
|
|
1414
|
+
|
|
1415
|
+
this.scheduleScrollFrame()
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
/**
|
|
1419
|
+
* Setup component lifecycle: announce the initial window, re-ask once the
|
|
1420
|
+
* loaded window changes, and cancel a pending scroll frame on unmount.
|
|
1421
|
+
*
|
|
1422
|
+
* @template {X extends FuroTableContext ? X : never} T, X
|
|
1423
|
+
* @returns {T} For method chaining.
|
|
1424
|
+
* @this {T}
|
|
1425
|
+
*/
|
|
1426
|
+
setupComponent () {
|
|
1427
|
+
onMounted(() => {
|
|
1428
|
+
this.emitInitialWindow()
|
|
1429
|
+
})
|
|
1430
|
+
|
|
1431
|
+
watch(
|
|
1432
|
+
() => this.extractLoadedEnd(),
|
|
1433
|
+
() => {
|
|
1434
|
+
this.onLoadedWindowChange()
|
|
1435
|
+
}
|
|
1436
|
+
)
|
|
1437
|
+
|
|
1438
|
+
onBeforeUnmount(() => {
|
|
1439
|
+
this.teardownScrollFrame()
|
|
1440
|
+
})
|
|
1441
|
+
|
|
1442
|
+
return this
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
/**
|
|
1446
|
+
* Coalesce raw scroll events into one `onScroll` per animation frame, so a
|
|
1447
|
+
* burst of scroll events triggers at most one windowing pass per frame.
|
|
1448
|
+
*/
|
|
1449
|
+
scheduleScrollFrame () {
|
|
1450
|
+
if (this.scrollFrameId !== null) {
|
|
1451
|
+
return
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
this.scrollFrameId = this.Ctor.requestFrame(() => {
|
|
1455
|
+
this.scrollFrameId = null
|
|
1456
|
+
|
|
1457
|
+
this.onScroll({
|
|
1458
|
+
scrollTop: this.pendingScrollTop,
|
|
1459
|
+
})
|
|
1460
|
+
})
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
/**
|
|
1464
|
+
* Cancel a pending coalesced scroll frame, if any. Called on unmount so the
|
|
1465
|
+
* frame callback cannot fire against a torn-down component.
|
|
1466
|
+
*
|
|
1467
|
+
* @returns {void}
|
|
1468
|
+
*/
|
|
1469
|
+
teardownScrollFrame () {
|
|
1470
|
+
if (this.scrollFrameId === null) {
|
|
1471
|
+
return
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
this.Ctor.cancelFrame(this.scrollFrameId)
|
|
1475
|
+
|
|
1476
|
+
this.scrollFrameId = null
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
/**
|
|
1480
|
+
* Handle a scroll on the virtual viewport: store the offset and emit a
|
|
1481
|
+
* range change when the visible window moves.
|
|
1482
|
+
*
|
|
1483
|
+
* @param {{
|
|
1484
|
+
* scrollTop: number
|
|
1485
|
+
* }} params - Parameters of this method.
|
|
1486
|
+
*/
|
|
1487
|
+
onScroll ({
|
|
1488
|
+
scrollTop,
|
|
1489
|
+
}) {
|
|
1490
|
+
if (!this.isVirtual()) {
|
|
1491
|
+
return
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
const previousFirstIndex = this.extractFirstIndex()
|
|
1495
|
+
const nextFirstIndex = this.computeFirstIndex(scrollTop)
|
|
1496
|
+
|
|
1497
|
+
if (nextFirstIndex === previousFirstIndex) {
|
|
1498
|
+
return
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
const isScrollingDown = nextFirstIndex > previousFirstIndex
|
|
1502
|
+
|
|
1503
|
+
this.scrollTopRef.value = scrollTop
|
|
1504
|
+
|
|
1505
|
+
this.emitRangeChange()
|
|
1506
|
+
|
|
1507
|
+
if (isScrollingDown) {
|
|
1508
|
+
this.emitReachEndIfNeeded()
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
/**
|
|
1513
|
+
* Emit `reach-end` once when a downward scroll brings the visible window
|
|
1514
|
+
* within `endThreshold` rows of the end of the currently loaded window, and
|
|
1515
|
+
* more records remain to load. Re-arms only when the loaded window grows, so
|
|
1516
|
+
* a consumer appending pages is asked exactly once per page — unlike
|
|
1517
|
+
* `range:change`, it never fires on upward scroll nor before the end.
|
|
1518
|
+
*
|
|
1519
|
+
* @returns {void}
|
|
1520
|
+
*/
|
|
1521
|
+
emitReachEndIfNeeded () {
|
|
1522
|
+
if (!this.isVirtual()) {
|
|
1523
|
+
return
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
const rows = this.extractRows()
|
|
1527
|
+
|
|
1528
|
+
if (rows.length === 0) {
|
|
1529
|
+
return
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
const loadedEnd = this.extractLoadedEnd()
|
|
1533
|
+
|
|
1534
|
+
if (loadedEnd >= this.extractTotalRowCount()) {
|
|
1535
|
+
return
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
const lastVisibleIndex = this.extractLastIndex()
|
|
1539
|
+
|
|
1540
|
+
if (lastVisibleIndex < loadedEnd - 1 - this.extractEndThreshold()) {
|
|
1541
|
+
return
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
if (this.lastReachEndLoadedEnd === loadedEnd) {
|
|
1545
|
+
return
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
this.lastReachEndLoadedEnd = loadedEnd
|
|
1549
|
+
|
|
1550
|
+
this.emit(
|
|
1551
|
+
this.EMIT_EVENT_NAME.REACH_END,
|
|
1552
|
+
{
|
|
1553
|
+
first: this.extractFirstIndex(),
|
|
1554
|
+
last: lastVisibleIndex,
|
|
1555
|
+
loadedCount: rows.length,
|
|
1556
|
+
totalRecords: this.extractTotalRowCount(),
|
|
1557
|
+
}
|
|
1558
|
+
)
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
/**
|
|
1562
|
+
* Release the `reach-end` guard once the loaded window shrank, so a refresh
|
|
1563
|
+
* landing back on an already seen end can ask for its next page again.
|
|
1564
|
+
*
|
|
1565
|
+
* @returns {void}
|
|
1566
|
+
*/
|
|
1567
|
+
releaseReachEndGuard () {
|
|
1568
|
+
if (this.lastReachEndLoadedEnd === null) {
|
|
1569
|
+
return
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
if (this.extractLoadedEnd() >= this.lastReachEndLoadedEnd) {
|
|
1573
|
+
return
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
this.lastReachEndLoadedEnd = null
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
/**
|
|
1580
|
+
* Emit `range:change` when the visible window differs from the last emitted.
|
|
1581
|
+
* Virtual mode only, because a plain table has no window to move.
|
|
1582
|
+
*/
|
|
1583
|
+
emitRangeChange () {
|
|
1584
|
+
if (!this.isVirtual()) {
|
|
1585
|
+
return
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
const first = this.extractFirstIndex()
|
|
1589
|
+
const last = this.extractLastIndex()
|
|
1590
|
+
|
|
1591
|
+
const hasSameRange = this.lastEmittedRange !== null
|
|
1592
|
+
&& this.lastEmittedRange.first === first
|
|
1593
|
+
&& this.lastEmittedRange.last === last
|
|
1594
|
+
|
|
1595
|
+
if (hasSameRange) {
|
|
1596
|
+
return
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
this.lastEmittedRange = {
|
|
1600
|
+
first,
|
|
1601
|
+
last,
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
this.emit(
|
|
1605
|
+
this.EMIT_EVENT_NAME.RANGE_CHANGE,
|
|
1606
|
+
{
|
|
1607
|
+
first,
|
|
1608
|
+
last,
|
|
1609
|
+
}
|
|
1610
|
+
)
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
/**
|
|
1614
|
+
* Announce the window the table renders on mount. That window can already
|
|
1615
|
+
* hold unloaded rows, which no scroll event would otherwise ask to fill.
|
|
1616
|
+
*
|
|
1617
|
+
* @returns {void}
|
|
1618
|
+
*/
|
|
1619
|
+
emitInitialWindow () {
|
|
1620
|
+
this.emitRangeChange()
|
|
1621
|
+
|
|
1622
|
+
this.emitReachEndIfNeeded()
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
/**
|
|
1626
|
+
* Ask for the next page again once the loaded window changed, so a window
|
|
1627
|
+
* still holding unloaded rows never waits for a scroll that may never come.
|
|
1628
|
+
*
|
|
1629
|
+
* @returns {void}
|
|
1630
|
+
*/
|
|
1631
|
+
onLoadedWindowChange () {
|
|
1632
|
+
this.releaseReachEndGuard()
|
|
1633
|
+
|
|
1634
|
+
this.emitReachEndIfNeeded()
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
/**
|
|
1638
|
+
* Resolve the field of the column immediately after the given one. The next
|
|
1639
|
+
* column absorbs the width change during a fit-mode resize.
|
|
1640
|
+
*
|
|
1641
|
+
* @param {{
|
|
1642
|
+
* field: string
|
|
1643
|
+
* }} params - Parameters of this method.
|
|
1644
|
+
* @returns {string | null}
|
|
1645
|
+
*/
|
|
1646
|
+
resolveNextColumnField ({
|
|
1647
|
+
field,
|
|
1648
|
+
}) {
|
|
1649
|
+
const columns = this.extractColumns()
|
|
1650
|
+
const index = columns.findIndex(column => column.field === field)
|
|
1651
|
+
|
|
1652
|
+
if (index === -1) {
|
|
1653
|
+
return null
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
return columns[index + 1]?.field
|
|
1657
|
+
?? null
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
/**
|
|
1661
|
+
* Whether a column's resize handle is shown. The last column has no neighbour
|
|
1662
|
+
* to trade width with in fit mode, so it is not resizable.
|
|
1663
|
+
*
|
|
1664
|
+
* @param {{
|
|
1665
|
+
* columnIndex: number
|
|
1666
|
+
* }} params - Parameters of this method.
|
|
1667
|
+
* @returns {boolean}
|
|
1668
|
+
*/
|
|
1669
|
+
canResizeColumn ({
|
|
1670
|
+
columnIndex,
|
|
1671
|
+
}) {
|
|
1672
|
+
if (!this.isResizable()) {
|
|
1673
|
+
return false
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1676
|
+
return columnIndex < this.extractColumns().length - 1
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
/**
|
|
1680
|
+
* Measure the current rendered width (px) of every data column from its
|
|
1681
|
+
* header cell. Pinning all widths on the first resize keeps the table's total
|
|
1682
|
+
* width fixed while resizing trades width between two adjacent columns.
|
|
1683
|
+
*
|
|
1684
|
+
* @param {{
|
|
1685
|
+
* handle: HTMLElement
|
|
1686
|
+
* }} params - Parameters of this method.
|
|
1687
|
+
* @returns {Record<string, number>}
|
|
1688
|
+
*/
|
|
1689
|
+
measureColumnWidths ({
|
|
1690
|
+
handle,
|
|
1691
|
+
}) {
|
|
1692
|
+
const row = handle.closest('tr')
|
|
1693
|
+
|
|
1694
|
+
if (row === null) {
|
|
1695
|
+
return {}
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
const headerCells = /** @type {NodeListOf<HTMLElement>} */ (
|
|
1699
|
+
row.querySelectorAll('.header-cell')
|
|
1700
|
+
)
|
|
1701
|
+
|
|
1702
|
+
const entries = this.extractColumns()
|
|
1703
|
+
.map((column, index) => [
|
|
1704
|
+
column.field,
|
|
1705
|
+
headerCells[index]?.offsetWidth
|
|
1706
|
+
?? 0,
|
|
1707
|
+
])
|
|
1708
|
+
|
|
1709
|
+
return Object.fromEntries(entries)
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
/**
|
|
1713
|
+
* Begin resizing a column (fit mode). On the first resize, all column widths
|
|
1714
|
+
* are pinned so the table's total width stays fixed; dragging then trades
|
|
1715
|
+
* width with the next column. Captures the pointer on the handle so move/end
|
|
1716
|
+
* events keep firing even when the cursor leaves it.
|
|
1717
|
+
*
|
|
1718
|
+
* @param {{
|
|
1719
|
+
* column: FuroTableColumn
|
|
1720
|
+
* pointerEvent: PointerEvent
|
|
1721
|
+
* }} params - Parameters of this method.
|
|
1722
|
+
*/
|
|
1723
|
+
onColumnResizeStart ({
|
|
1724
|
+
column,
|
|
1725
|
+
pointerEvent,
|
|
1726
|
+
}) {
|
|
1727
|
+
const handle = /** @type {HTMLElement} */ (pointerEvent.currentTarget)
|
|
1728
|
+
|
|
1729
|
+
if (Object.keys(this.columnWidthsRef.value).length === 0) {
|
|
1730
|
+
this.columnWidthsRef.value = this.measureColumnWidths({
|
|
1731
|
+
handle,
|
|
1732
|
+
})
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
const nextField = this.resolveNextColumnField({
|
|
1736
|
+
field: column.field,
|
|
1737
|
+
})
|
|
1738
|
+
|
|
1739
|
+
if (nextField === null) {
|
|
1740
|
+
return
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1743
|
+
const widths = this.columnWidthsRef.value
|
|
1744
|
+
const startWidth = widths[column.field]
|
|
1745
|
+
?? 0
|
|
1746
|
+
const nextStartWidth = widths[nextField]
|
|
1747
|
+
?? 0
|
|
1748
|
+
|
|
1749
|
+
this.resizeState = {
|
|
1750
|
+
field: column.field,
|
|
1751
|
+
startX: pointerEvent.clientX,
|
|
1752
|
+
startWidth,
|
|
1753
|
+
nextField,
|
|
1754
|
+
nextStartWidth,
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
handle.setPointerCapture(pointerEvent.pointerId)
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
/**
|
|
1761
|
+
* Trade width between the resized column and its neighbour as the pointer
|
|
1762
|
+
* moves. The shared total is preserved (fit mode) and both columns are
|
|
1763
|
+
* clamped to the minimum width, so the table never overflows from a resize.
|
|
1764
|
+
*
|
|
1765
|
+
* @param {{
|
|
1766
|
+
* pointerEvent: PointerEvent
|
|
1767
|
+
* }} params - Parameters of this method.
|
|
1768
|
+
*/
|
|
1769
|
+
onColumnResizeMove ({
|
|
1770
|
+
pointerEvent,
|
|
1771
|
+
}) {
|
|
1772
|
+
if (this.resizeState === null) {
|
|
1773
|
+
return
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1776
|
+
const {
|
|
1777
|
+
field,
|
|
1778
|
+
startX,
|
|
1779
|
+
startWidth,
|
|
1780
|
+
nextField,
|
|
1781
|
+
nextStartWidth,
|
|
1782
|
+
} = this.resizeState
|
|
1783
|
+
|
|
1784
|
+
const rawDelta = pointerEvent.clientX
|
|
1785
|
+
- startX
|
|
1786
|
+
const delta = Math.min(
|
|
1787
|
+
Math.max(rawDelta, MIN_COLUMN_WIDTH - startWidth),
|
|
1788
|
+
nextStartWidth - MIN_COLUMN_WIDTH
|
|
1789
|
+
)
|
|
1790
|
+
|
|
1791
|
+
const width = startWidth + delta
|
|
1792
|
+
const nextWidth = nextStartWidth - delta
|
|
1793
|
+
|
|
1794
|
+
this.columnWidthsRef.value = {
|
|
1795
|
+
...this.columnWidthsRef.value,
|
|
1796
|
+
[field]: width,
|
|
1797
|
+
[nextField]: nextWidth,
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
/**
|
|
1802
|
+
* Finish resizing a column and release the captured pointer.
|
|
1803
|
+
*
|
|
1804
|
+
* @param {{
|
|
1805
|
+
* pointerEvent: PointerEvent
|
|
1806
|
+
* }} params - Parameters of this method.
|
|
1807
|
+
*/
|
|
1808
|
+
onColumnResizeEnd ({
|
|
1809
|
+
pointerEvent,
|
|
1810
|
+
}) {
|
|
1811
|
+
if (this.resizeState === null) {
|
|
1812
|
+
return
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
const handle = /** @type {HTMLElement} */ (pointerEvent.currentTarget)
|
|
1816
|
+
|
|
1817
|
+
handle.releasePointerCapture(pointerEvent.pointerId)
|
|
1818
|
+
|
|
1819
|
+
this.resizeState = null
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1823
|
+
/**
|
|
1824
|
+
* @typedef {import('../../../contexts/BaseFuroContext.js').BaseFuroContextParams & {
|
|
1825
|
+
* props: Props
|
|
1826
|
+
* componentContext: ComponentContext
|
|
1827
|
+
* refHash: {
|
|
1828
|
+
* selectedRowKeysRef: import('vue').Ref<Array<string | number>>
|
|
1829
|
+
* pendingSelectionRef: import('vue').Ref<PendingSelection | null>
|
|
1830
|
+
* scrollTopRef: import('vue').Ref<number>
|
|
1831
|
+
* columnWidthsRef: import('vue').Ref<Record<string, number>>
|
|
1832
|
+
* }
|
|
1833
|
+
* }} FuroTableContextParams
|
|
1834
|
+
*/
|
|
1835
|
+
|
|
1836
|
+
/**
|
|
1837
|
+
* The last selection this table emitted.
|
|
1838
|
+
*
|
|
1839
|
+
* @typedef {{
|
|
1840
|
+
* derivedFrom: Array<string | number>
|
|
1841
|
+
* selectedRowKeys: Array<string | number>
|
|
1842
|
+
* }} PendingSelection
|
|
1843
|
+
*/
|
|
1844
|
+
|
|
1845
|
+
/**
|
|
1846
|
+
* @typedef {Omit<FuroTableContextParams, 'refHash'> & {
|
|
1847
|
+
* refHash?: FuroTableContextParams['refHash']
|
|
1848
|
+
* }} FuroTableContextFactoryParams
|
|
1849
|
+
*/
|
|
1850
|
+
|
|
1851
|
+
/**
|
|
1852
|
+
* @typedef {keyof typeof FuroTableContext.EMIT_EVENT_NAME} EmitEventName
|
|
1853
|
+
*/
|
|
1854
|
+
|
|
1855
|
+
/**
|
|
1856
|
+
* @typedef {import('vue').HTMLAttributes & {
|
|
1857
|
+
* parcel?: FuroTableParcel | null
|
|
1858
|
+
* }} Props
|
|
1859
|
+
*/
|
|
1860
|
+
|
|
1861
|
+
/**
|
|
1862
|
+
* Closed-set Furo behavior for `<FuroTable>`. JSON-serializable only.
|
|
1863
|
+
*
|
|
1864
|
+
* @typedef {{
|
|
1865
|
+
* columns?: Array<FuroTableColumn>
|
|
1866
|
+
* rows?: Array<Record<string, unknown>>
|
|
1867
|
+
* rowKey?: string
|
|
1868
|
+
* selectable?: boolean
|
|
1869
|
+
* selectedRowKeys?: Array<string | number> | null
|
|
1870
|
+
* sort?: FuroTableSort | null
|
|
1871
|
+
* loading?: boolean
|
|
1872
|
+
* errorMessage?: string | null
|
|
1873
|
+
* emptyText?: string
|
|
1874
|
+
* virtual?: FuroTableVirtual | null
|
|
1875
|
+
* totalRecords?: number | null
|
|
1876
|
+
* rowOffset?: number
|
|
1877
|
+
* resizable?: boolean
|
|
1878
|
+
* compact?: boolean
|
|
1879
|
+
* gridLines?: boolean
|
|
1880
|
+
* rowActionsLabel?: string
|
|
1881
|
+
* rowActionsWidth?: string
|
|
1882
|
+
* }} FuroTableParcel
|
|
1883
|
+
*/
|
|
1884
|
+
|
|
1885
|
+
/**
|
|
1886
|
+
* Fixed-height virtual-scroll configuration. When set, FuroTable windows the
|
|
1887
|
+
* rendered rows and emits `range:change` so the consumer can lazy-fetch the
|
|
1888
|
+
* visible window into a sparse `rows` (with `rowOffset` + `totalRecords`).
|
|
1889
|
+
*
|
|
1890
|
+
* For infinite-append consumers, prefer the `reach-end` event over
|
|
1891
|
+
* `range:change`: it fires once per loaded window when a downward scroll nears
|
|
1892
|
+
* the end (within `endThreshold` rows), never on upward scroll.
|
|
1893
|
+
*
|
|
1894
|
+
* @typedef {{
|
|
1895
|
+
* itemSize: number
|
|
1896
|
+
* height: number
|
|
1897
|
+
* overscan?: number
|
|
1898
|
+
* endThreshold?: number
|
|
1899
|
+
* }} FuroTableVirtual
|
|
1900
|
+
*/
|
|
1901
|
+
|
|
1902
|
+
/**
|
|
1903
|
+
* @typedef {{
|
|
1904
|
+
* field: string
|
|
1905
|
+
* label?: string
|
|
1906
|
+
* sortable?: boolean
|
|
1907
|
+
* align?: 'start' | 'center' | 'end'
|
|
1908
|
+
* width?: string
|
|
1909
|
+
* }} FuroTableColumn
|
|
1910
|
+
*/
|
|
1911
|
+
|
|
1912
|
+
/**
|
|
1913
|
+
* @typedef {{
|
|
1914
|
+
* field: string
|
|
1915
|
+
* direction: 'asc' | 'desc'
|
|
1916
|
+
* }} FuroTableSort
|
|
1917
|
+
*/
|
|
1918
|
+
|
|
1919
|
+
/**
|
|
1920
|
+
* @typedef {import('vue').SetupContext<FuroTableEmitHash, FuroTableSlots>} ComponentContext
|
|
1921
|
+
*/
|
|
1922
|
+
|
|
1923
|
+
/**
|
|
1924
|
+
* @typedef {typeof FuroTableContext.EMIT_EVENT_HASH} FuroTableEmitHash
|
|
1925
|
+
*/
|
|
1926
|
+
|
|
1927
|
+
/**
|
|
1928
|
+
* @typedef {import('vue').SlotsType<{
|
|
1929
|
+
* 'header-cell': {
|
|
1930
|
+
* column: FuroTableColumn
|
|
1931
|
+
* }
|
|
1932
|
+
* cell: {
|
|
1933
|
+
* row: Record<string, unknown>
|
|
1934
|
+
* column: FuroTableColumn
|
|
1935
|
+
* value: unknown
|
|
1936
|
+
* }
|
|
1937
|
+
* loading: {}
|
|
1938
|
+
* empty: {}
|
|
1939
|
+
* error: {
|
|
1940
|
+
* message: string | null
|
|
1941
|
+
* }
|
|
1942
|
+
* placeholder: {
|
|
1943
|
+
* index: number
|
|
1944
|
+
* }
|
|
1945
|
+
* 'row-actions': {
|
|
1946
|
+
* row: Record<string, unknown>
|
|
1947
|
+
* }
|
|
1948
|
+
* pagination: {}
|
|
1949
|
+
* footer: {
|
|
1950
|
+
* selection: {
|
|
1951
|
+
* keys: Array<string | number>
|
|
1952
|
+
* count: number
|
|
1953
|
+
* }
|
|
1954
|
+
* }
|
|
1955
|
+
* }>} FuroTableSlots
|
|
1956
|
+
*/
|
|
1957
|
+
|
|
1958
|
+
/**
|
|
1959
|
+
* @typedef {(
|
|
1960
|
+
* props: Props,
|
|
1961
|
+
* componentContext: ComponentContext
|
|
1962
|
+
* ) => {
|
|
1963
|
+
* context: FuroTableContext
|
|
1964
|
+
* }} FuroTableSetup
|
|
1965
|
+
*/
|