@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.
Files changed (310) hide show
  1. package/LICENSE +9 -0
  2. package/README.ja.md +334 -0
  3. package/README.md +333 -0
  4. package/index.js +11 -0
  5. package/lib/assets/css/furo/0010.variables-palette-color-scale.css +298 -0
  6. package/lib/assets/css/furo/0020.variables-z-index.css +10 -0
  7. package/lib/assets/css/furo/0030.variables-semantic-color.css +178 -0
  8. package/lib/assets/css/furo/0035.variables-semantic-dimension.css +104 -0
  9. package/lib/assets/css/furo/0040.variables-component-furo.css +28 -0
  10. package/lib/assets/css/furo/0050.furo-editor-content.css +22 -0
  11. package/lib/assets/css/furo.css +15 -0
  12. package/lib/components/atoms/FuroAvatar/FuroAvatar.vue +135 -0
  13. package/lib/components/atoms/FuroAvatar/FuroAvatarContext.js +147 -0
  14. package/lib/components/atoms/FuroBadge/FuroBadge.vue +240 -0
  15. package/lib/components/atoms/FuroBadge/FuroBadgeContext.js +298 -0
  16. package/lib/components/atoms/FuroButton/ButtonEmitPayload.js +44 -0
  17. package/lib/components/atoms/FuroButton/FuroButton.vue +301 -0
  18. package/lib/components/atoms/FuroButton/FuroButtonContext.js +328 -0
  19. package/lib/components/atoms/FuroCheckbox/CheckboxEmitPayload.js +106 -0
  20. package/lib/components/atoms/FuroCheckbox/FuroCheckbox.vue +173 -0
  21. package/lib/components/atoms/FuroCheckbox/FuroCheckboxContext.js +682 -0
  22. package/lib/components/atoms/FuroEmailField/EmailFieldEmitPayload.js +74 -0
  23. package/lib/components/atoms/FuroEmailField/FuroEmailField.vue +119 -0
  24. package/lib/components/atoms/FuroEmailField/FuroEmailFieldContext.js +311 -0
  25. package/lib/components/atoms/FuroFileField/FileFieldEmitPayload.js +80 -0
  26. package/lib/components/atoms/FuroFileField/FuroFileField.vue +128 -0
  27. package/lib/components/atoms/FuroFileField/FuroFileFieldContext.js +195 -0
  28. package/lib/components/atoms/FuroIcon/FuroIcon.vue +74 -0
  29. package/lib/components/atoms/FuroIcon/FuroIconContext.js +293 -0
  30. package/lib/components/atoms/FuroIcon/FuroIconRegistry.js +617 -0
  31. package/lib/components/atoms/FuroIcon/FuroIconifyRendererContext.js +101 -0
  32. package/lib/components/atoms/FuroIcon/furoIconData.js +164 -0
  33. package/lib/components/atoms/FuroIcon/iconRenderers.js +62 -0
  34. package/lib/components/atoms/FuroIcon/icons.js +7 -0
  35. package/lib/components/atoms/FuroNumberField/FuroNumberField.vue +213 -0
  36. package/lib/components/atoms/FuroNumberField/FuroNumberFieldContext.js +442 -0
  37. package/lib/components/atoms/FuroNumberField/NumberFieldEmitPayload.js +162 -0
  38. package/lib/components/atoms/FuroPasswordField/FuroPasswordField.vue +119 -0
  39. package/lib/components/atoms/FuroPasswordField/FuroPasswordFieldContext.js +311 -0
  40. package/lib/components/atoms/FuroPasswordField/PasswordFieldEmitPayload.js +74 -0
  41. package/lib/components/atoms/FuroRadio/FuroRadio.vue +193 -0
  42. package/lib/components/atoms/FuroRadio/FuroRadioContext.js +557 -0
  43. package/lib/components/atoms/FuroRadio/RadioEmitPayload.js +72 -0
  44. package/lib/components/atoms/FuroSwitch/FuroSwitch.vue +165 -0
  45. package/lib/components/atoms/FuroSwitch/FuroSwitchContext.js +440 -0
  46. package/lib/components/atoms/FuroSwitch/SwitchEmitPayload.js +86 -0
  47. package/lib/components/atoms/FuroTextField/FuroTextField.vue +119 -0
  48. package/lib/components/atoms/FuroTextField/FuroTextFieldContext.js +311 -0
  49. package/lib/components/atoms/FuroTextField/TextFieldEmitPayload.js +83 -0
  50. package/lib/components/atoms/FuroTextarea/FuroTextarea.vue +121 -0
  51. package/lib/components/atoms/FuroTextarea/FuroTextareaContext.js +322 -0
  52. package/lib/components/atoms/FuroTextarea/TextareaEmitPayload.js +83 -0
  53. package/lib/components/atoms/FuroToggle/FuroToggle.vue +172 -0
  54. package/lib/components/atoms/FuroToggle/FuroToggleContext.js +410 -0
  55. package/lib/components/atoms/FuroToggle/ToggleEmitPayload.js +86 -0
  56. package/lib/components/molecules/FuroAccordion/AccordionEmitPayload.js +80 -0
  57. package/lib/components/molecules/FuroAccordion/FuroAccordion.vue +219 -0
  58. package/lib/components/molecules/FuroAccordion/FuroAccordionContext.js +547 -0
  59. package/lib/components/molecules/FuroAttachment/FuroAttachment.vue +376 -0
  60. package/lib/components/molecules/FuroAttachment/FuroAttachmentContext.js +628 -0
  61. package/lib/components/molecules/FuroAutocompleteField/AutocompleteEmitPayload.js +225 -0
  62. package/lib/components/molecules/FuroAutocompleteField/FuroAutocompleteField.vue +669 -0
  63. package/lib/components/molecules/FuroAutocompleteField/FuroAutocompleteFieldContext.js +1525 -0
  64. package/lib/components/molecules/FuroAvatarGroup/FuroAvatarGroup.vue +126 -0
  65. package/lib/components/molecules/FuroAvatarGroup/FuroAvatarGroupContext.js +245 -0
  66. package/lib/components/molecules/FuroBreadcrumb/FuroBreadcrumb.vue +219 -0
  67. package/lib/components/molecules/FuroBreadcrumb/FuroBreadcrumbContext.js +257 -0
  68. package/lib/components/molecules/FuroCollapsible/CollapsibleEmitPayload.js +69 -0
  69. package/lib/components/molecules/FuroCollapsible/FuroCollapsible.vue +177 -0
  70. package/lib/components/molecules/FuroCollapsible/FuroCollapsibleContext.js +389 -0
  71. package/lib/components/molecules/FuroControlBlock/FuroControlBlock.vue +149 -0
  72. package/lib/components/molecules/FuroControlBlock/FuroControlBlockContext.js +81 -0
  73. package/lib/components/molecules/FuroDatePicker/DatePickerEmitPayload.js +74 -0
  74. package/lib/components/molecules/FuroDatePicker/FuroDatePicker.vue +661 -0
  75. package/lib/components/molecules/FuroDatePicker/FuroDatePickerContext.js +1038 -0
  76. package/lib/components/molecules/FuroDateTimePicker/DateTimePickerEmitPayload.js +96 -0
  77. package/lib/components/molecules/FuroDateTimePicker/FuroDateTimePicker.vue +153 -0
  78. package/lib/components/molecules/FuroDateTimePicker/FuroDateTimePickerContext.js +840 -0
  79. package/lib/components/molecules/FuroDropdownMenu/DropdownMenuOptionSelectEmitPayload.js +100 -0
  80. package/lib/components/molecules/FuroDropdownMenu/FuroDropdownMenu.vue +348 -0
  81. package/lib/components/molecules/FuroDropdownMenu/FuroDropdownMenuContext.js +714 -0
  82. package/lib/components/molecules/FuroDropdownMenu/FuroDropdownMenuOptionRows.vue +222 -0
  83. package/lib/components/molecules/FuroEditableField/EditableEmitPayload.js +89 -0
  84. package/lib/components/molecules/FuroEditableField/FuroEditableField.vue +314 -0
  85. package/lib/components/molecules/FuroEditableField/FuroEditableFieldContext.js +1018 -0
  86. package/lib/components/molecules/FuroEditableField/processors/EditorCollisionProcessor.js +236 -0
  87. package/lib/components/molecules/FuroEditableField/processors/ObjectProcessor.js +101 -0
  88. package/lib/components/molecules/FuroEmptyState/FuroEmptyState.vue +124 -0
  89. package/lib/components/molecules/FuroEmptyState/FuroEmptyStateContext.js +42 -0
  90. package/lib/components/molecules/FuroErrorState/FuroErrorState.vue +125 -0
  91. package/lib/components/molecules/FuroErrorState/FuroErrorStateContext.js +42 -0
  92. package/lib/components/molecules/FuroPagination/FuroPagination.vue +246 -0
  93. package/lib/components/molecules/FuroPagination/FuroPaginationContext.js +489 -0
  94. package/lib/components/molecules/FuroPopover/FuroPopover.vue +171 -0
  95. package/lib/components/molecules/FuroPopover/FuroPopoverContext.js +279 -0
  96. package/lib/components/molecules/FuroScrollArea/FuroScrollArea.vue +181 -0
  97. package/lib/components/molecules/FuroScrollArea/FuroScrollAreaContext.js +149 -0
  98. package/lib/components/molecules/FuroSearchField/FuroSearchField.vue +155 -0
  99. package/lib/components/molecules/FuroSearchField/FuroSearchFieldContext.js +231 -0
  100. package/lib/components/molecules/FuroSectionHeader/FuroSectionHeader.vue +140 -0
  101. package/lib/components/molecules/FuroSectionHeader/FuroSectionHeaderContext.js +204 -0
  102. package/lib/components/molecules/FuroSelect/FuroSelect.vue +474 -0
  103. package/lib/components/molecules/FuroSelect/FuroSelectContext.js +743 -0
  104. package/lib/components/molecules/FuroSelect/SelectEmitPayload.js +181 -0
  105. package/lib/components/molecules/FuroSeparator/FuroSeparator.vue +88 -0
  106. package/lib/components/molecules/FuroSeparator/FuroSeparatorContext.js +40 -0
  107. package/lib/components/molecules/FuroSkeleton/FuroSkeleton.vue +101 -0
  108. package/lib/components/molecules/FuroSkeleton/FuroSkeletonContext.js +82 -0
  109. package/lib/components/molecules/FuroSplitter/FuroSplitter.vue +237 -0
  110. package/lib/components/molecules/FuroSplitter/FuroSplitterContext.js +429 -0
  111. package/lib/components/molecules/FuroSplitter/SplitterEmitPayload.js +69 -0
  112. package/lib/components/molecules/FuroTimeField/FuroTimeField.vue +251 -0
  113. package/lib/components/molecules/FuroTimeField/FuroTimeFieldContext.js +1138 -0
  114. package/lib/components/molecules/FuroTimeField/TimeFieldEmitPayload.js +74 -0
  115. package/lib/components/molecules/FuroToggleGroup/FuroToggleGroup.vue +211 -0
  116. package/lib/components/molecules/FuroToggleGroup/FuroToggleGroupContext.js +551 -0
  117. package/lib/components/molecules/FuroToggleGroup/ToggleGroupEmitPayload.js +80 -0
  118. package/lib/components/molecules/FuroToolBar/FuroToolBar.vue +91 -0
  119. package/lib/components/molecules/FuroToolBar/FuroToolBarContext.js +75 -0
  120. package/lib/components/molecules/FuroTooltip/FuroTooltip.vue +183 -0
  121. package/lib/components/molecules/FuroTooltip/FuroTooltipContext.js +290 -0
  122. package/lib/components/organisms/FuroAlertDialog/FuroAlertDialog.vue +324 -0
  123. package/lib/components/organisms/FuroAlertDialog/FuroAlertDialogContext.js +608 -0
  124. package/lib/components/organisms/FuroDialog/FuroDialog.vue +362 -0
  125. package/lib/components/organisms/FuroDialog/FuroDialogContext.js +535 -0
  126. package/lib/components/organisms/FuroDrawer/FuroDrawer.vue +618 -0
  127. package/lib/components/organisms/FuroDrawer/FuroDrawerContext.js +676 -0
  128. package/lib/components/organisms/FuroEditor/FuroEditor.vue +613 -0
  129. package/lib/components/organisms/FuroEditor/FuroEditorCommandContext.js +420 -0
  130. package/lib/components/organisms/FuroEditor/FuroEditorContext.js +2048 -0
  131. package/lib/components/organisms/FuroEditor/FuroEditorMentionMenu.vue +120 -0
  132. package/lib/components/organisms/FuroEditor/FuroEditorMentionMenuContext.js +261 -0
  133. package/lib/components/organisms/FuroEditor/PreserveNewlinesPasteExtension.js +267 -0
  134. package/lib/components/organisms/FuroEditor/build-editor-extensions.js +97 -0
  135. package/lib/components/organisms/FuroEditor/mention-suggestion.js +320 -0
  136. package/lib/components/organisms/FuroEditor/paste-link-segments.js +124 -0
  137. package/lib/components/organisms/FuroSidebar/FuroSidebar.vue +473 -0
  138. package/lib/components/organisms/FuroSidebar/FuroSidebarContext.js +1014 -0
  139. package/lib/components/organisms/FuroStepper/FuroStepper.vue +345 -0
  140. package/lib/components/organisms/FuroStepper/FuroStepperContext.js +481 -0
  141. package/lib/components/organisms/FuroTable/FuroTable.vue +707 -0
  142. package/lib/components/organisms/FuroTable/FuroTableContext.js +1965 -0
  143. package/lib/components/organisms/FuroTable/FuroTableRowItemContext.js +111 -0
  144. package/lib/components/organisms/FuroTabs/FuroTabs.vue +403 -0
  145. package/lib/components/organisms/FuroTabs/FuroTabsContext.js +846 -0
  146. package/lib/components/organisms/FuroToast/FuroToast.vue +624 -0
  147. package/lib/components/organisms/FuroToast/FuroToastContext.js +1089 -0
  148. package/lib/components/organisms/FuroToast/FuroToaster.vue +44 -0
  149. package/lib/components/organisms/FuroToast/FuroToasterContext.js +113 -0
  150. package/lib/components/organisms/FuroToast/ToastQueue.js +325 -0
  151. package/lib/components/organisms/FuroToast/toast.js +64 -0
  152. package/lib/contexts/BaseFuroContext.js +318 -0
  153. package/lib/contexts/BaseFuroContextAccessor.js +47 -0
  154. package/lib/emit-payload/BaseEmitPayload.js +91 -0
  155. package/lib/index.components.js +61 -0
  156. package/lib/index.core.js +18 -0
  157. package/lib/tools/TimerClerk.js +75 -0
  158. package/nuxt/index.js +109 -0
  159. package/nuxt/runtime/furo-icon-renderer.js +24 -0
  160. package/package.json +137 -0
  161. package/public/furo-vue/components.json +4619 -0
  162. package/public/furo-vue/llms.txt +66 -0
  163. package/types/furo-nuxt.d.ts +48 -0
  164. package/types/generated/components/atoms/FuroAvatar/FuroAvatar.vue.d.ts +74 -0
  165. package/types/generated/components/atoms/FuroAvatar/FuroAvatarContext.d.ts +82 -0
  166. package/types/generated/components/atoms/FuroBadge/FuroBadge.vue.d.ts +12 -0
  167. package/types/generated/components/atoms/FuroBadge/FuroBadgeContext.d.ts +160 -0
  168. package/types/generated/components/atoms/FuroButton/ButtonEmitPayload.d.ts +19 -0
  169. package/types/generated/components/atoms/FuroButton/FuroButton.vue.d.ts +53 -0
  170. package/types/generated/components/atoms/FuroButton/FuroButtonContext.d.ts +156 -0
  171. package/types/generated/components/atoms/FuroCheckbox/CheckboxEmitPayload.d.ts +55 -0
  172. package/types/generated/components/atoms/FuroCheckbox/FuroCheckbox.vue.d.ts +87 -0
  173. package/types/generated/components/atoms/FuroCheckbox/FuroCheckboxContext.d.ts +342 -0
  174. package/types/generated/components/atoms/FuroEmailField/EmailFieldEmitPayload.d.ts +46 -0
  175. package/types/generated/components/atoms/FuroEmailField/FuroEmailField.vue.d.ts +458 -0
  176. package/types/generated/components/atoms/FuroEmailField/FuroEmailFieldContext.d.ts +161 -0
  177. package/types/generated/components/atoms/FuroFileField/FileFieldEmitPayload.d.ts +46 -0
  178. package/types/generated/components/atoms/FuroFileField/FuroFileField.vue.d.ts +454 -0
  179. package/types/generated/components/atoms/FuroFileField/FuroFileFieldContext.d.ts +109 -0
  180. package/types/generated/components/atoms/FuroIcon/FuroIcon.vue.d.ts +14 -0
  181. package/types/generated/components/atoms/FuroIcon/FuroIconContext.d.ts +152 -0
  182. package/types/generated/components/atoms/FuroIcon/FuroIconRegistry.d.ts +457 -0
  183. package/types/generated/components/atoms/FuroIcon/FuroIconifyRendererContext.d.ts +65 -0
  184. package/types/generated/components/atoms/FuroIcon/furoIconData.d.ts +154 -0
  185. package/types/generated/components/atoms/FuroIcon/iconRenderers.d.ts +6 -0
  186. package/types/generated/components/atoms/FuroIcon/icons.d.ts +6 -0
  187. package/types/generated/components/atoms/FuroNumberField/FuroNumberField.vue.d.ts +500 -0
  188. package/types/generated/components/atoms/FuroNumberField/FuroNumberFieldContext.d.ts +243 -0
  189. package/types/generated/components/atoms/FuroNumberField/NumberFieldEmitPayload.d.ts +74 -0
  190. package/types/generated/components/atoms/FuroPasswordField/FuroPasswordField.vue.d.ts +458 -0
  191. package/types/generated/components/atoms/FuroPasswordField/FuroPasswordFieldContext.d.ts +161 -0
  192. package/types/generated/components/atoms/FuroPasswordField/PasswordFieldEmitPayload.d.ts +46 -0
  193. package/types/generated/components/atoms/FuroRadio/FuroRadio.vue.d.ts +96 -0
  194. package/types/generated/components/atoms/FuroRadio/FuroRadioContext.d.ts +290 -0
  195. package/types/generated/components/atoms/FuroRadio/RadioEmitPayload.d.ts +39 -0
  196. package/types/generated/components/atoms/FuroSwitch/FuroSwitch.vue.d.ts +74 -0
  197. package/types/generated/components/atoms/FuroSwitch/FuroSwitchContext.d.ts +218 -0
  198. package/types/generated/components/atoms/FuroSwitch/SwitchEmitPayload.d.ts +42 -0
  199. package/types/generated/components/atoms/FuroTextField/FuroTextField.vue.d.ts +458 -0
  200. package/types/generated/components/atoms/FuroTextField/FuroTextFieldContext.d.ts +161 -0
  201. package/types/generated/components/atoms/FuroTextField/TextFieldEmitPayload.d.ts +52 -0
  202. package/types/generated/components/atoms/FuroTextarea/FuroTextarea.vue.d.ts +422 -0
  203. package/types/generated/components/atoms/FuroTextarea/FuroTextareaContext.d.ts +161 -0
  204. package/types/generated/components/atoms/FuroTextarea/TextareaEmitPayload.d.ts +52 -0
  205. package/types/generated/components/atoms/FuroToggle/FuroToggle.vue.d.ts +51 -0
  206. package/types/generated/components/atoms/FuroToggle/FuroToggleContext.d.ts +191 -0
  207. package/types/generated/components/atoms/FuroToggle/ToggleEmitPayload.d.ts +42 -0
  208. package/types/generated/components/molecules/FuroAccordion/AccordionEmitPayload.d.ts +41 -0
  209. package/types/generated/components/molecules/FuroAccordion/FuroAccordion.vue.d.ts +133 -0
  210. package/types/generated/components/molecules/FuroAccordion/FuroAccordionContext.d.ts +279 -0
  211. package/types/generated/components/molecules/FuroAttachment/FuroAttachment.vue.d.ts +79 -0
  212. package/types/generated/components/molecules/FuroAttachment/FuroAttachmentContext.d.ts +363 -0
  213. package/types/generated/components/molecules/FuroAutocompleteField/AutocompleteEmitPayload.d.ts +129 -0
  214. package/types/generated/components/molecules/FuroAutocompleteField/FuroAutocompleteField.vue.d.ts +751 -0
  215. package/types/generated/components/molecules/FuroAutocompleteField/FuroAutocompleteFieldContext.d.ts +722 -0
  216. package/types/generated/components/molecules/FuroAvatarGroup/FuroAvatarGroup.vue.d.ts +84 -0
  217. package/types/generated/components/molecules/FuroAvatarGroup/FuroAvatarGroupContext.d.ts +150 -0
  218. package/types/generated/components/molecules/FuroBreadcrumb/FuroBreadcrumb.vue.d.ts +24 -0
  219. package/types/generated/components/molecules/FuroBreadcrumb/FuroBreadcrumbContext.d.ts +152 -0
  220. package/types/generated/components/molecules/FuroCollapsible/CollapsibleEmitPayload.d.ts +36 -0
  221. package/types/generated/components/molecules/FuroCollapsible/FuroCollapsible.vue.d.ts +98 -0
  222. package/types/generated/components/molecules/FuroCollapsible/FuroCollapsibleContext.d.ts +191 -0
  223. package/types/generated/components/molecules/FuroControlBlock/FuroControlBlock.vue.d.ts +30 -0
  224. package/types/generated/components/molecules/FuroControlBlock/FuroControlBlockContext.d.ts +57 -0
  225. package/types/generated/components/molecules/FuroDatePicker/DatePickerEmitPayload.d.ts +41 -0
  226. package/types/generated/components/molecules/FuroDatePicker/FuroDatePicker.vue.d.ts +456 -0
  227. package/types/generated/components/molecules/FuroDatePicker/FuroDatePickerContext.d.ts +516 -0
  228. package/types/generated/components/molecules/FuroDateTimePicker/DateTimePickerEmitPayload.d.ts +55 -0
  229. package/types/generated/components/molecules/FuroDateTimePicker/FuroDateTimePicker.vue.d.ts +1365 -0
  230. package/types/generated/components/molecules/FuroDateTimePicker/FuroDateTimePickerContext.d.ts +435 -0
  231. package/types/generated/components/molecules/FuroDropdownMenu/DropdownMenuOptionSelectEmitPayload.d.ts +48 -0
  232. package/types/generated/components/molecules/FuroDropdownMenu/FuroDropdownMenu.vue.d.ts +773 -0
  233. package/types/generated/components/molecules/FuroDropdownMenu/FuroDropdownMenuContext.d.ts +380 -0
  234. package/types/generated/components/molecules/FuroDropdownMenu/FuroDropdownMenuOptionRows.vue.d.ts +261 -0
  235. package/types/generated/components/molecules/FuroEditableField/EditableEmitPayload.d.ts +39 -0
  236. package/types/generated/components/molecules/FuroEditableField/FuroEditableField.vue.d.ts +646 -0
  237. package/types/generated/components/molecules/FuroEditableField/FuroEditableFieldContext.d.ts +477 -0
  238. package/types/generated/components/molecules/FuroEditableField/processors/EditorCollisionProcessor.d.ts +121 -0
  239. package/types/generated/components/molecules/FuroEditableField/processors/ObjectProcessor.d.ts +45 -0
  240. package/types/generated/components/molecules/FuroEmptyState/FuroEmptyState.vue.d.ts +21 -0
  241. package/types/generated/components/molecules/FuroEmptyState/FuroEmptyStateContext.d.ts +32 -0
  242. package/types/generated/components/molecules/FuroErrorState/FuroErrorState.vue.d.ts +21 -0
  243. package/types/generated/components/molecules/FuroErrorState/FuroErrorStateContext.d.ts +32 -0
  244. package/types/generated/components/molecules/FuroPagination/FuroPagination.vue.d.ts +534 -0
  245. package/types/generated/components/molecules/FuroPagination/FuroPaginationContext.d.ts +271 -0
  246. package/types/generated/components/molecules/FuroPopover/FuroPopover.vue.d.ts +513 -0
  247. package/types/generated/components/molecules/FuroPopover/FuroPopoverContext.d.ts +160 -0
  248. package/types/generated/components/molecules/FuroScrollArea/FuroScrollArea.vue.d.ts +124 -0
  249. package/types/generated/components/molecules/FuroScrollArea/FuroScrollAreaContext.d.ts +86 -0
  250. package/types/generated/components/molecules/FuroSearchField/FuroSearchField.vue.d.ts +487 -0
  251. package/types/generated/components/molecules/FuroSearchField/FuroSearchFieldContext.d.ts +137 -0
  252. package/types/generated/components/molecules/FuroSectionHeader/FuroSectionHeader.vue.d.ts +49 -0
  253. package/types/generated/components/molecules/FuroSectionHeader/FuroSectionHeaderContext.d.ts +121 -0
  254. package/types/generated/components/molecules/FuroSelect/FuroSelect.vue.d.ts +719 -0
  255. package/types/generated/components/molecules/FuroSelect/FuroSelectContext.d.ts +402 -0
  256. package/types/generated/components/molecules/FuroSelect/SelectEmitPayload.d.ts +102 -0
  257. package/types/generated/components/molecules/FuroSeparator/FuroSeparator.vue.d.ts +39 -0
  258. package/types/generated/components/molecules/FuroSeparator/FuroSeparatorContext.d.ts +31 -0
  259. package/types/generated/components/molecules/FuroSkeleton/FuroSkeleton.vue.d.ts +27 -0
  260. package/types/generated/components/molecules/FuroSkeleton/FuroSkeletonContext.d.ts +61 -0
  261. package/types/generated/components/molecules/FuroSplitter/FuroSplitter.vue.d.ts +139 -0
  262. package/types/generated/components/molecules/FuroSplitter/FuroSplitterContext.d.ts +226 -0
  263. package/types/generated/components/molecules/FuroSplitter/SplitterEmitPayload.d.ts +41 -0
  264. package/types/generated/components/molecules/FuroTimeField/FuroTimeField.vue.d.ts +498 -0
  265. package/types/generated/components/molecules/FuroTimeField/FuroTimeFieldContext.d.ts +555 -0
  266. package/types/generated/components/molecules/FuroTimeField/TimeFieldEmitPayload.d.ts +41 -0
  267. package/types/generated/components/molecules/FuroToggleGroup/FuroToggleGroup.vue.d.ts +70 -0
  268. package/types/generated/components/molecules/FuroToggleGroup/FuroToggleGroupContext.d.ts +270 -0
  269. package/types/generated/components/molecules/FuroToggleGroup/ToggleGroupEmitPayload.d.ts +41 -0
  270. package/types/generated/components/molecules/FuroToolBar/FuroToolBar.vue.d.ts +30 -0
  271. package/types/generated/components/molecules/FuroToolBar/FuroToolBarContext.d.ts +48 -0
  272. package/types/generated/components/molecules/FuroTooltip/FuroTooltip.vue.d.ts +524 -0
  273. package/types/generated/components/molecules/FuroTooltip/FuroTooltipContext.d.ts +166 -0
  274. package/types/generated/components/organisms/FuroAlertDialog/FuroAlertDialog.vue.d.ts +651 -0
  275. package/types/generated/components/organisms/FuroAlertDialog/FuroAlertDialogContext.d.ts +299 -0
  276. package/types/generated/components/organisms/FuroDialog/FuroDialog.vue.d.ts +583 -0
  277. package/types/generated/components/organisms/FuroDialog/FuroDialogContext.d.ts +264 -0
  278. package/types/generated/components/organisms/FuroDrawer/FuroDrawer.vue.d.ts +635 -0
  279. package/types/generated/components/organisms/FuroDrawer/FuroDrawerContext.d.ts +347 -0
  280. package/types/generated/components/organisms/FuroEditor/FuroEditor.vue.d.ts +2487 -0
  281. package/types/generated/components/organisms/FuroEditor/FuroEditorCommandContext.d.ts +192 -0
  282. package/types/generated/components/organisms/FuroEditor/FuroEditorContext.d.ts +904 -0
  283. package/types/generated/components/organisms/FuroEditor/FuroEditorMentionMenu.vue.d.ts +43 -0
  284. package/types/generated/components/organisms/FuroEditor/FuroEditorMentionMenuContext.d.ts +159 -0
  285. package/types/generated/components/organisms/FuroEditor/PreserveNewlinesPasteExtension.d.ts +43 -0
  286. package/types/generated/components/organisms/FuroEditor/build-editor-extensions.d.ts +22 -0
  287. package/types/generated/components/organisms/FuroEditor/mention-suggestion.d.ts +25 -0
  288. package/types/generated/components/organisms/FuroEditor/paste-link-segments.d.ts +31 -0
  289. package/types/generated/components/organisms/FuroSidebar/FuroSidebar.vue.d.ts +898 -0
  290. package/types/generated/components/organisms/FuroSidebar/FuroSidebarContext.d.ts +596 -0
  291. package/types/generated/components/organisms/FuroStepper/FuroStepper.vue.d.ts +589 -0
  292. package/types/generated/components/organisms/FuroStepper/FuroStepperContext.d.ts +266 -0
  293. package/types/generated/components/organisms/FuroTable/FuroTable.vue.d.ts +528 -0
  294. package/types/generated/components/organisms/FuroTable/FuroTableContext.d.ts +1035 -0
  295. package/types/generated/components/organisms/FuroTable/FuroTableRowItemContext.d.ts +62 -0
  296. package/types/generated/components/organisms/FuroTabs/FuroTabs.vue.d.ts +515 -0
  297. package/types/generated/components/organisms/FuroTabs/FuroTabsContext.d.ts +438 -0
  298. package/types/generated/components/organisms/FuroToast/FuroToast.vue.d.ts +725 -0
  299. package/types/generated/components/organisms/FuroToast/FuroToastContext.d.ts +543 -0
  300. package/types/generated/components/organisms/FuroToast/FuroToaster.vue.d.ts +729 -0
  301. package/types/generated/components/organisms/FuroToast/FuroToasterContext.d.ts +62 -0
  302. package/types/generated/components/organisms/FuroToast/ToastQueue.d.ts +201 -0
  303. package/types/generated/components/organisms/FuroToast/toast.d.ts +23 -0
  304. package/types/generated/contexts/BaseFuroContext.d.ts +207 -0
  305. package/types/generated/contexts/BaseFuroContextAccessor.d.ts +28 -0
  306. package/types/generated/emit-payload/BaseEmitPayload.d.ts +62 -0
  307. package/types/generated/index.components.d.ts +50 -0
  308. package/types/generated/index.core.d.ts +6 -0
  309. package/types/generated/tools/TimerClerk.d.ts +50 -0
  310. 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
+ */