@flux-ui/components 3.0.0-next.0

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 (261) hide show
  1. package/README.md +45 -0
  2. package/package.json +77 -0
  3. package/src/component/FluxAction.vue +27 -0
  4. package/src/component/FluxActionBar.vue +94 -0
  5. package/src/component/FluxActionPane.vue +40 -0
  6. package/src/component/FluxActions.vue +27 -0
  7. package/src/component/FluxAlert.vue +37 -0
  8. package/src/component/FluxAnimatedColors.vue +141 -0
  9. package/src/component/FluxAspectRatio.vue +21 -0
  10. package/src/component/FluxAutoGrid.vue +27 -0
  11. package/src/component/FluxAvatar.vue +119 -0
  12. package/src/component/FluxBadge.vue +84 -0
  13. package/src/component/FluxBadgeStack.vue +18 -0
  14. package/src/component/FluxBorderShine.vue +36 -0
  15. package/src/component/FluxBoxedIcon.vue +36 -0
  16. package/src/component/FluxButton.vue +110 -0
  17. package/src/component/FluxButtonGroup.vue +15 -0
  18. package/src/component/FluxButtonStack.vue +28 -0
  19. package/src/component/FluxCalendar.vue +254 -0
  20. package/src/component/FluxCalendarEvent.vue +41 -0
  21. package/src/component/FluxCheckbox.vue +60 -0
  22. package/src/component/FluxChip.vue +57 -0
  23. package/src/component/FluxClickablePane.vue +61 -0
  24. package/src/component/FluxColorPicker.vue +265 -0
  25. package/src/component/FluxColorSelect.vue +81 -0
  26. package/src/component/FluxComment.vue +71 -0
  27. package/src/component/FluxConfirm.vue +42 -0
  28. package/src/component/FluxContainer.vue +23 -0
  29. package/src/component/FluxDataTable.vue +96 -0
  30. package/src/component/FluxDatePicker.vue +353 -0
  31. package/src/component/FluxDestructiveButton.vue +28 -0
  32. package/src/component/FluxDisabled.vue +22 -0
  33. package/src/component/FluxDivider.vue +37 -0
  34. package/src/component/FluxDotPattern.vue +72 -0
  35. package/src/component/FluxDropZone.vue +202 -0
  36. package/src/component/FluxDynamicView.vue +16 -0
  37. package/src/component/FluxExpandable.vue +119 -0
  38. package/src/component/FluxExpandableGroup.vue +53 -0
  39. package/src/component/FluxFader.vue +64 -0
  40. package/src/component/FluxFaderItem.vue +15 -0
  41. package/src/component/FluxFilter.vue +133 -0
  42. package/src/component/FluxFilterDate.vue +58 -0
  43. package/src/component/FluxFilterDateRange.vue +59 -0
  44. package/src/component/FluxFilterOption.vue +49 -0
  45. package/src/component/FluxFilterOptionAsync.vue +103 -0
  46. package/src/component/FluxFilterOptions.vue +62 -0
  47. package/src/component/FluxFilterOptionsAsync.vue +113 -0
  48. package/src/component/FluxFilterRange.vue +91 -0
  49. package/src/component/FluxFlickeringGrid.vue +141 -0
  50. package/src/component/FluxFlyout.vue +205 -0
  51. package/src/component/FluxFocalPointEditor.vue +137 -0
  52. package/src/component/FluxFocalPointImage.vue +29 -0
  53. package/src/component/FluxForm.vue +35 -0
  54. package/src/component/FluxFormColumn.vue +15 -0
  55. package/src/component/FluxFormDateInput.vue +92 -0
  56. package/src/component/FluxFormDateRangeInput.vue +87 -0
  57. package/src/component/FluxFormDateTimeInput.vue +120 -0
  58. package/src/component/FluxFormField.vue +98 -0
  59. package/src/component/FluxFormFieldAddition.vue +37 -0
  60. package/src/component/FluxFormInput.vue +223 -0
  61. package/src/component/FluxFormInputAddition.vue +31 -0
  62. package/src/component/FluxFormInputGroup.vue +25 -0
  63. package/src/component/FluxFormPinInput.vue +135 -0
  64. package/src/component/FluxFormRangeSlider.vue +179 -0
  65. package/src/component/FluxFormRow.vue +15 -0
  66. package/src/component/FluxFormSection.vue +23 -0
  67. package/src/component/FluxFormSelect.vue +59 -0
  68. package/src/component/FluxFormSelectAsync.vue +118 -0
  69. package/src/component/FluxFormSlider.vue +123 -0
  70. package/src/component/FluxFormTextArea.vue +53 -0
  71. package/src/component/FluxFormTimeZonePicker.vue +713 -0
  72. package/src/component/FluxGallery.vue +99 -0
  73. package/src/component/FluxGalleryItem.vue +49 -0
  74. package/src/component/FluxGrid.vue +28 -0
  75. package/src/component/FluxGridColumn.vue +31 -0
  76. package/src/component/FluxGridPattern.vue +60 -0
  77. package/src/component/FluxIcon.vue +79 -0
  78. package/src/component/FluxInfo.vue +28 -0
  79. package/src/component/FluxInfoStack.vue +17 -0
  80. package/src/component/FluxLegend.vue +27 -0
  81. package/src/component/FluxLink.vue +35 -0
  82. package/src/component/FluxMenu.vue +31 -0
  83. package/src/component/FluxMenuGroup.vue +21 -0
  84. package/src/component/FluxMenuItem.vue +84 -0
  85. package/src/component/FluxMenuOptions.vue +38 -0
  86. package/src/component/FluxMenuSubHeader.vue +33 -0
  87. package/src/component/FluxMenuTitle.vue +17 -0
  88. package/src/component/FluxNotice.vue +79 -0
  89. package/src/component/FluxNoticeStack.vue +17 -0
  90. package/src/component/FluxOverlay.vue +31 -0
  91. package/src/component/FluxPagination.vue +148 -0
  92. package/src/component/FluxPaginationBar.vue +81 -0
  93. package/src/component/FluxPane.vue +45 -0
  94. package/src/component/FluxPaneBody.vue +15 -0
  95. package/src/component/FluxPaneDeck.vue +24 -0
  96. package/src/component/FluxPaneFooter.vue +15 -0
  97. package/src/component/FluxPaneGroup.vue +15 -0
  98. package/src/component/FluxPaneHeader.vue +44 -0
  99. package/src/component/FluxPaneIllustration.vue +68 -0
  100. package/src/component/FluxPaneMedia.vue +31 -0
  101. package/src/component/FluxPercentageBar.vue +45 -0
  102. package/src/component/FluxPersona.vue +48 -0
  103. package/src/component/FluxPlaceholder.vue +56 -0
  104. package/src/component/FluxPressable.vue +77 -0
  105. package/src/component/FluxPrimaryButton.vue +28 -0
  106. package/src/component/FluxProgressBar.vue +75 -0
  107. package/src/component/FluxPrompt.vue +77 -0
  108. package/src/component/FluxPublishButton.vue +59 -0
  109. package/src/component/FluxQuantitySelector.vue +109 -0
  110. package/src/component/FluxRemove.vue +34 -0
  111. package/src/component/FluxRoot.vue +60 -0
  112. package/src/component/FluxSecondaryButton.vue +28 -0
  113. package/src/component/FluxSegmentedControl.vue +77 -0
  114. package/src/component/FluxSegmentedView.vue +15 -0
  115. package/src/component/FluxSeparator.vue +19 -0
  116. package/src/component/FluxSlideOver.vue +25 -0
  117. package/src/component/FluxSnackbar.vue +154 -0
  118. package/src/component/FluxSnackbarProvider.vue +34 -0
  119. package/src/component/FluxSpacer.vue +9 -0
  120. package/src/component/FluxSpacing.vue +32 -0
  121. package/src/component/FluxSpinner.vue +48 -0
  122. package/src/component/FluxSplitButton.vue +61 -0
  123. package/src/component/FluxStack.vue +40 -0
  124. package/src/component/FluxStatistic.vue +68 -0
  125. package/src/component/FluxStepper.vue +69 -0
  126. package/src/component/FluxStepperStep.vue +15 -0
  127. package/src/component/FluxStepperSteps.vue +62 -0
  128. package/src/component/FluxTab.vue +23 -0
  129. package/src/component/FluxTabBar.vue +87 -0
  130. package/src/component/FluxTabBarItem.vue +104 -0
  131. package/src/component/FluxTable.vue +68 -0
  132. package/src/component/FluxTableActions.vue +16 -0
  133. package/src/component/FluxTableCell.vue +47 -0
  134. package/src/component/FluxTableHeader.vue +111 -0
  135. package/src/component/FluxTableRow.vue +15 -0
  136. package/src/component/FluxTabs.vue +91 -0
  137. package/src/component/FluxTag.vue +85 -0
  138. package/src/component/FluxTagStack.vue +18 -0
  139. package/src/component/FluxTicks.vue +44 -0
  140. package/src/component/FluxTimeline.vue +17 -0
  141. package/src/component/FluxTimelineItem.vue +73 -0
  142. package/src/component/FluxToggle.vue +64 -0
  143. package/src/component/FluxToolbar.vue +32 -0
  144. package/src/component/FluxToolbarGroup.vue +18 -0
  145. package/src/component/FluxTooltip.vue +56 -0
  146. package/src/component/FluxTooltipProvider.vue +176 -0
  147. package/src/component/FluxWindow.vue +47 -0
  148. package/src/component/index.ts +142 -0
  149. package/src/component/primitive/Anchor.vue +17 -0
  150. package/src/component/primitive/AnchorPopup.vue +194 -0
  151. package/src/component/primitive/CoordinatePicker.vue +155 -0
  152. package/src/component/primitive/CoordinatePickerThumb.vue +71 -0
  153. package/src/component/primitive/FilterItem.vue +44 -0
  154. package/src/component/primitive/FilterMenuRenderer.ts +233 -0
  155. package/src/component/primitive/FilterOptionBase.vue +67 -0
  156. package/src/component/primitive/SelectBase.vue +340 -0
  157. package/src/component/primitive/SliderBase.vue +89 -0
  158. package/src/component/primitive/SliderThumb.vue +64 -0
  159. package/src/component/primitive/SliderTrack.vue +22 -0
  160. package/src/component/primitive/VNodeRenderer.ts +11 -0
  161. package/src/component/primitive/index.ts +10 -0
  162. package/src/composable/index.ts +9 -0
  163. package/src/composable/private/index.ts +3 -0
  164. package/src/composable/private/useFormSelect.ts +66 -0
  165. package/src/composable/private/useLoaded.ts +21 -0
  166. package/src/composable/private/useTranslate.ts +35 -0
  167. package/src/composable/useBreakpoints.ts +54 -0
  168. package/src/composable/useDisabled.ts +9 -0
  169. package/src/composable/useDisabledInjection.ts +6 -0
  170. package/src/composable/useExpandableGroupInjection.ts +10 -0
  171. package/src/composable/useFilterInjection.ts +22 -0
  172. package/src/composable/useFlyoutInjection.ts +10 -0
  173. package/src/composable/useFormFieldInjection.ts +8 -0
  174. package/src/composable/useTableInjection.ts +11 -0
  175. package/src/css/base.scss +33 -0
  176. package/src/css/component/Action.module.scss +107 -0
  177. package/src/css/component/Avatar.module.scss +177 -0
  178. package/src/css/component/Badge.module.scss +189 -0
  179. package/src/css/component/Button.module.scss +293 -0
  180. package/src/css/component/Calendar.module.scss +171 -0
  181. package/src/css/component/Chip.module.scss +58 -0
  182. package/src/css/component/Color.module.scss +184 -0
  183. package/src/css/component/Comment.module.scss +123 -0
  184. package/src/css/component/DatePicker.module.scss +193 -0
  185. package/src/css/component/Divider.module.scss +79 -0
  186. package/src/css/component/DropZone.module.scss +99 -0
  187. package/src/css/component/Expandable.module.scss +112 -0
  188. package/src/css/component/Fader.module.scss +38 -0
  189. package/src/css/component/Filter.module.scss +80 -0
  190. package/src/css/component/Flyout.module.scss +63 -0
  191. package/src/css/component/FocalPoint.module.scss +84 -0
  192. package/src/css/component/Form.module.scss +812 -0
  193. package/src/css/component/Gallery.module.scss +64 -0
  194. package/src/css/component/Grid.module.scss +24 -0
  195. package/src/css/component/Icon.module.scss +104 -0
  196. package/src/css/component/Info.module.scss +15 -0
  197. package/src/css/component/Layout.module.scss +63 -0
  198. package/src/css/component/Legend.module.scss +32 -0
  199. package/src/css/component/Menu.module.scss +314 -0
  200. package/src/css/component/Notice.module.scss +279 -0
  201. package/src/css/component/Overlay.module.scss +149 -0
  202. package/src/css/component/Pagination.module.scss +59 -0
  203. package/src/css/component/Pane.module.scss +218 -0
  204. package/src/css/component/PercentageBar.module.scss +31 -0
  205. package/src/css/component/Placeholder.module.scss +72 -0
  206. package/src/css/component/Progress.module.scss +84 -0
  207. package/src/css/component/Remove.module.scss +29 -0
  208. package/src/css/component/Root.module.scss +8 -0
  209. package/src/css/component/SegmentedControl.module.scss +82 -0
  210. package/src/css/component/Snackbar.module.scss +227 -0
  211. package/src/css/component/Spinner.module.scss +36 -0
  212. package/src/css/component/Statistic.module.scss +118 -0
  213. package/src/css/component/Stepper.module.scss +103 -0
  214. package/src/css/component/Tab.module.scss +162 -0
  215. package/src/css/component/Table.module.scss +164 -0
  216. package/src/css/component/Timeline.module.scss +173 -0
  217. package/src/css/component/Toolbar.module.scss +82 -0
  218. package/src/css/component/Tooltip.module.scss +62 -0
  219. package/src/css/component/Transition.module.scss +142 -0
  220. package/src/css/component/Visual.module.scss +70 -0
  221. package/src/css/component/base/Button.module.scss +87 -0
  222. package/src/css/component/base/Effect.module.scss +139 -0
  223. package/src/css/component/base/Grid.module.scss +8 -0
  224. package/src/css/component/base/Pane.module.scss +54 -0
  225. package/src/css/component/primitive/CoordinatePicker.module.scss +24 -0
  226. package/src/css/component/primitive/Slider.module.scss +116 -0
  227. package/src/css/index.scss +5 -0
  228. package/src/css/layers.scss +1 -0
  229. package/src/css/mixin/breakpoints.scss +112 -0
  230. package/src/css/mixin/focus-ring.scss +56 -0
  231. package/src/css/mixin/hover.scss +7 -0
  232. package/src/css/mixin/index.scss +3 -0
  233. package/src/css/reset.scss +169 -0
  234. package/src/css/typography.scss +87 -0
  235. package/src/css/variables.scss +214 -0
  236. package/src/data/di.ts +42 -0
  237. package/src/data/filter.ts +9 -0
  238. package/src/data/helper.ts +9 -0
  239. package/src/data/i18n.ts +55 -0
  240. package/src/data/iconRegistry.ts +21 -0
  241. package/src/data/index.ts +8 -0
  242. package/src/data/inputMask.ts +34 -0
  243. package/src/data/store.ts +233 -0
  244. package/src/image/avatar-mask.svg +3 -0
  245. package/src/index.ts +25 -0
  246. package/src/transition/FluxAutoHeightTransition.vue +59 -0
  247. package/src/transition/FluxAutoWidthTransition.vue +59 -0
  248. package/src/transition/FluxBreakthroughTransition.vue +23 -0
  249. package/src/transition/FluxFadeTransition.vue +24 -0
  250. package/src/transition/FluxOverlayTransition.vue +22 -0
  251. package/src/transition/FluxRouteTransition.vue +23 -0
  252. package/src/transition/FluxSlideOverTransition.vue +22 -0
  253. package/src/transition/FluxTooltipTransition.vue +22 -0
  254. package/src/transition/FluxVerticalWindowTransition.vue +23 -0
  255. package/src/transition/FluxWindowTransition.vue +23 -0
  256. package/src/transition/index.ts +10 -0
  257. package/src/util/createDialogRenderer.ts +64 -0
  258. package/src/util/createLabelForDateRange.ts +61 -0
  259. package/src/util/index.ts +2 -0
  260. package/src/vite.d.ts +13 -0
  261. package/tsconfig.json +45 -0
@@ -0,0 +1,202 @@
1
+ <template>
2
+ <div
3
+ :class="[
4
+ $style.dropZone,
5
+ isDragging && $style.isDragging,
6
+ isDraggingOver && $style.isDraggingOver
7
+ ]"
8
+ :aria-disabled="disabled ? true : undefined">
9
+ <div
10
+ :class="$style.dropZoneContent"
11
+ @dragleave.capture="onDragLeave"
12
+ @dragover.capture="onDragEnter"
13
+ @drop="onDrop">
14
+ <svg
15
+ ref="content"
16
+ :class="$style.dropZoneBorder"
17
+ role="presentation">
18
+ <rect
19
+ height="100%"
20
+ width="100%"
21
+ stroke-linecap="round"
22
+ stroke-linejoin="round"
23
+ :pathLength="pathLength"/>
24
+ </svg>
25
+
26
+ <slot v-bind="{isDragging, isDraggingOver, showPicker}"/>
27
+
28
+ <FluxFadeTransition>
29
+ <div
30
+ v-if="isLoading"
31
+ :class="$style.dropZoneLoader">
32
+ <FluxSpinner/>
33
+ </div>
34
+ </FluxFadeTransition>
35
+ </div>
36
+
37
+ <div
38
+ v-if="slots.actions"
39
+ :class="$style.dropZoneActions">
40
+ <slot
41
+ name="actions"
42
+ v-bind="{isDragging, isDraggingOver, showPicker}"/>
43
+ </div>
44
+
45
+ <slot
46
+ name="extra"
47
+ v-bind="{isDragging, isDraggingOver, showPicker}"/>
48
+ </div>
49
+ </template>
50
+
51
+ <script
52
+ lang="ts"
53
+ setup>
54
+ import { roundStep } from '@basmilius/utils';
55
+ import { onMounted, onUnmounted, ref, toRef, unref, useTemplateRef, watch } from 'vue';
56
+ import { useDisabled } from '$flux/composable';
57
+ import { FluxFadeTransition } from '$flux/transition';
58
+ import FluxSpinner from './FluxSpinner.vue';
59
+ import $style from '$flux/css/component/DropZone.module.scss';
60
+
61
+ const emit = defineEmits<{
62
+ select: [File];
63
+ selectMultiple: [FileList];
64
+ }>();
65
+
66
+ const {
67
+ accept,
68
+ disabled: componentDisabled,
69
+ isMultiple
70
+ } = defineProps<{
71
+ readonly accept?: string;
72
+ readonly disabled?: boolean;
73
+ readonly isLoading?: boolean;
74
+ readonly isMultiple?: boolean;
75
+ }>();
76
+
77
+ const slots = defineSlots<{
78
+ default?(props: {
79
+ readonly isDragging: boolean;
80
+ readonly isDraggingOver: boolean;
81
+
82
+ showPicker(): void;
83
+ }): any;
84
+
85
+ actions?(props: {
86
+ readonly isDragging: boolean;
87
+ readonly isDraggingOver: boolean;
88
+
89
+ showPicker(): void;
90
+ }): any;
91
+
92
+ extra?(props: {
93
+ readonly isDragging: boolean;
94
+ readonly isDraggingOver: boolean;
95
+
96
+ showPicker(): void;
97
+ }): any;
98
+ }>();
99
+
100
+ const contentRef = useTemplateRef('content');
101
+ const disabled = useDisabled(toRef(() => componentDisabled));
102
+
103
+ const isDragging = ref(false);
104
+ const isDraggingOver = ref(false);
105
+ const pathLength = ref(0);
106
+
107
+ onMounted(() => {
108
+ window.addEventListener('dragleave', onWindowDragEnd, {capture: true});
109
+ window.addEventListener('dragover', onWindowDragStart, {capture: true});
110
+ window.addEventListener('drop', onWindowDrop, {capture: true});
111
+ });
112
+
113
+ onUnmounted(() => {
114
+ window.removeEventListener('dragleave', onWindowDragEnd);
115
+ window.removeEventListener('dragover', onWindowDragStart);
116
+ window.removeEventListener('drop', onWindowDrop);
117
+ });
118
+
119
+ function onDragEnter(evt: DragEvent): void {
120
+ isDraggingOver.value = true;
121
+ evt.preventDefault();
122
+ }
123
+
124
+ function onDragLeave(): void {
125
+ isDraggingOver.value = false;
126
+ }
127
+
128
+ function onDrop(evt: DragEvent): void {
129
+ isDragging.value = false;
130
+ isDraggingOver.value = false;
131
+
132
+ if (!evt.dataTransfer) {
133
+ return;
134
+ }
135
+
136
+ const files = evt.dataTransfer.files;
137
+
138
+ if (files.length === 0) {
139
+ return;
140
+ }
141
+
142
+ if (isMultiple) {
143
+ emit('selectMultiple', files);
144
+ } else {
145
+ emit('select', files[0]);
146
+ }
147
+
148
+ evt.preventDefault();
149
+ evt.stopPropagation();
150
+ }
151
+
152
+ function onWindowDragEnd(): void {
153
+ isDragging.value = false;
154
+ }
155
+
156
+ function onWindowDragStart(): void {
157
+ isDragging.value = true;
158
+ }
159
+
160
+ function onWindowDrop(): void {
161
+ isDragging.value = false;
162
+ isDraggingOver.value = false;
163
+ }
164
+
165
+ function onFileSelected(evt: Event): void {
166
+ const files = (evt.target as HTMLInputElement).files;
167
+
168
+ if (!files || files.length === 0) {
169
+ return;
170
+ }
171
+
172
+ if (isMultiple) {
173
+ emit('selectMultiple', files);
174
+ } else {
175
+ emit('select', files[0]);
176
+ }
177
+ }
178
+
179
+ function showPicker(): void {
180
+ if (unref(disabled)) {
181
+ return;
182
+ }
183
+
184
+ let input: HTMLInputElement | undefined = document.createElement('input');
185
+ input.accept = accept ?? '*';
186
+ input.multiple = isMultiple ?? false;
187
+ input.type = 'file';
188
+
189
+ input.addEventListener('change', onFileSelected, {once: true});
190
+ input.showPicker();
191
+ }
192
+
193
+ watch(contentRef, content => {
194
+ if (!content) {
195
+ return;
196
+ }
197
+
198
+ const {width, height} = content.getBoundingClientRect();
199
+ pathLength.value = roundStep(width * 2 + height * 2, 6);
200
+ }, {immediate: true});
201
+ </script>
202
+
@@ -0,0 +1,16 @@
1
+ <template>
2
+ <VNodeRenderer
3
+ v-if="vnode"
4
+ :vnode="vnode"/>
5
+ </template>
6
+
7
+ <script
8
+ setup
9
+ lang="ts">
10
+ import { VNode } from 'vue';
11
+ import { VNodeRenderer } from './primitive';
12
+
13
+ defineProps<{
14
+ readonly vnode?: VNode;
15
+ }>();
16
+ </script>
@@ -0,0 +1,119 @@
1
+ <template>
2
+ <div
3
+ :class="isOpen ? $style.expandableOpened : $style.expandable"
4
+ :id="headerId"
5
+ :aria-controls="contentId"
6
+ :aria-expanded="isOpen">
7
+ <slot
8
+ v-bind="{label, isOpen, close, open, toggle}"
9
+ name="header">
10
+ <button
11
+ :class="$style.expandableHeader"
12
+ type="button"
13
+ @click="toggle">
14
+ <FluxFadeTransition>
15
+ <FluxIcon
16
+ v-if="icon"
17
+ :key="icon"
18
+ :name="icon"/>
19
+ </FluxFadeTransition>
20
+
21
+ <span>{{ label }}</span>
22
+
23
+ <FluxFadeTransition>
24
+ <FluxIcon
25
+ :key="expandIcon"
26
+ :name="expandIcon"
27
+ :size="16"/>
28
+ </FluxFadeTransition>
29
+ </button>
30
+ </slot>
31
+
32
+ <FluxAutoHeightTransition>
33
+ <div
34
+ v-if="isOpen"
35
+ :class="$style.expandableBody"
36
+ :id="contentId"
37
+ :aria-labelledby="headerId">
38
+ <slot
39
+ v-bind="{label, close}"
40
+ name="body">
41
+ <div :class="$style.expandableContent">
42
+ <slot v-bind="{label, close}"/>
43
+ </div>
44
+ </slot>
45
+ </div>
46
+ </FluxAutoHeightTransition>
47
+ </div>
48
+ </template>
49
+
50
+ <script
51
+ lang="ts"
52
+ setup>
53
+ import { useComponentId } from '@flux-ui/internals';
54
+ import type { FluxIconName } from '@flux-ui/types';
55
+ import { computed, getCurrentInstance, onBeforeMount, onUnmounted, ref, unref, useId, watch } from 'vue';
56
+ import { useExpandableGroupInjection } from '$flux/composable';
57
+ import { FluxAutoHeightTransition, FluxFadeTransition } from '$flux/transition';
58
+ import FluxIcon from './FluxIcon.vue';
59
+ import $style from '$flux/css/component/Expandable.module.scss';
60
+
61
+ const emit = defineEmits<{
62
+ toggle: [boolean];
63
+ }>();
64
+
65
+ const {
66
+ isOpened
67
+ } = defineProps<{
68
+ readonly icon?: FluxIconName;
69
+ readonly isOpened?: boolean;
70
+ readonly label?: string;
71
+ }>();
72
+
73
+ const componentId = useComponentId();
74
+ const contentId = useId();
75
+ const headerId = useId();
76
+ const instance = getCurrentInstance()!;
77
+ const isOpen = ref(false);
78
+
79
+ const {closeAll, register, unregister} = useExpandableGroupInjection();
80
+
81
+ const expandIcon = computed<FluxIconName>(() => unref(isOpen) ? 'minus' : 'plus');
82
+
83
+ onBeforeMount(() => register?.(unref(componentId), instance));
84
+ onUnmounted(() => unregister?.(unref(componentId)));
85
+
86
+ function close(): void {
87
+ isOpen.value = false;
88
+ emit('toggle', isOpen.value);
89
+ }
90
+
91
+ function open(): void {
92
+ closeAll?.();
93
+ isOpen.value = true;
94
+ emit('toggle', isOpen.value);
95
+ }
96
+
97
+ function toggle(): void {
98
+ if (isOpen.value) {
99
+ close();
100
+ } else {
101
+ open();
102
+ }
103
+ }
104
+
105
+ watch(() => isOpened, () => {
106
+ if (isOpened) {
107
+ open();
108
+ } else {
109
+ close();
110
+ }
111
+ }, {immediate: true});
112
+
113
+ defineExpose({
114
+ isOpen,
115
+ close,
116
+ open,
117
+ toggle
118
+ });
119
+ </script>
@@ -0,0 +1,53 @@
1
+ <template>
2
+ <div :class="$style.expandableGroup">
3
+ <slot/>
4
+ </div>
5
+ </template>
6
+
7
+ <script
8
+ lang="ts"
9
+ setup>
10
+ import { getExposedRef } from '@flux-ui/internals';
11
+ import { ComponentInternalInstance, provide } from 'vue';
12
+ import { FluxExpandableGroupInjectionKey } from '$flux/data';
13
+ import $style from '$flux/css/component/Expandable.module.scss';
14
+
15
+ const {
16
+ isControlled
17
+ } = defineProps<{
18
+ readonly isControlled?: boolean;
19
+ }>();
20
+
21
+
22
+ defineSlots<{
23
+ default(): any;
24
+ }>();
25
+
26
+ const expandables: { [key: number]: ComponentInternalInstance; } = {};
27
+
28
+ function closeAll(): void {
29
+ Object.values(expandables).forEach(expandable => {
30
+ const isOpenRef = getExposedRef<boolean>(expandable, 'isOpen');
31
+ isOpenRef.value = false;
32
+ });
33
+ }
34
+
35
+ function register(uid: number, expandable: ComponentInternalInstance): void {
36
+ expandables[uid] = expandable;
37
+
38
+ if (!isControlled && Object.values(expandables).length === 1) {
39
+ const isOpenRef = getExposedRef<boolean>(expandable, 'isOpen');
40
+ isOpenRef.value = true;
41
+ }
42
+ }
43
+
44
+ function unregister(uid: number): void {
45
+ delete expandables[uid];
46
+ }
47
+
48
+ provide(FluxExpandableGroupInjectionKey, {
49
+ closeAll,
50
+ register,
51
+ unregister
52
+ });
53
+ </script>
@@ -0,0 +1,64 @@
1
+ <template>
2
+ <div
3
+ ref="fader"
4
+ :class="$style.fader">
5
+ <slot v-bind="{current, next, previous}"/>
6
+ </div>
7
+ </template>
8
+
9
+ <script
10
+ lang="ts"
11
+ setup>
12
+ import { unrefTemplateElement, useInterval } from '@flux-ui/internals';
13
+ import { computed, ref, unref, useTemplateRef, watch } from 'vue';
14
+ import $style from '$flux/css/component/Fader.module.scss';
15
+
16
+ const emit = defineEmits<{
17
+ update: [number];
18
+ }>();
19
+
20
+ const {
21
+ interval = 9000
22
+ } = defineProps<{
23
+ readonly interval?: number;
24
+ }>();
25
+
26
+ defineSlots<{
27
+ default(props: {
28
+ next(): void;
29
+ previous(): void;
30
+
31
+ readonly current: number;
32
+ }): any;
33
+ }>();
34
+
35
+ const faderRef = useTemplateRef('fader');
36
+ useInterval(interval, () => next());
37
+
38
+ const current = ref(-1);
39
+
40
+ const count = computed(() => {
41
+ const fader = unrefTemplateElement(faderRef);
42
+ return fader?.children.length ?? 0;
43
+ });
44
+
45
+ function next(): void {
46
+ current.value = unref(current) + 1 >= unref(count) ? 0 : unref(current) + 1;
47
+ }
48
+
49
+ function previous(): void {
50
+ current.value = unref(current) - 1 <= -1 ? unref(count) - 1 : unref(current) + 1;
51
+ }
52
+
53
+ watch(current, current => {
54
+ const fader = unrefTemplateElement(faderRef);
55
+
56
+ if (!fader || fader.children.length === 0) {
57
+ return;
58
+ }
59
+
60
+ Array.from(fader.children).forEach(item => item.classList.remove($style.isCurrent));
61
+ fader.children[current].classList.add($style.isCurrent);
62
+ emit('update', current);
63
+ }, {immediate: true});
64
+ </script>
@@ -0,0 +1,15 @@
1
+ <template>
2
+ <div :class="$style.faderItem">
3
+ <slot/>
4
+ </div>
5
+ </template>
6
+
7
+ <script
8
+ lang="ts"
9
+ setup>
10
+ import $style from '$flux/css/component/Fader.module.scss';
11
+
12
+ defineSlots<{
13
+ default(): any;
14
+ }>();
15
+ </script>
@@ -0,0 +1,133 @@
1
+ <template>
2
+ <div
3
+ v-height-transition
4
+ :class="$style.filter">
5
+ <FluxWindow ref="window">
6
+ <template #default="{navigate}">
7
+ <FilterMenuRenderer
8
+ :navigate="navigate"
9
+ :state="modelValue">
10
+ <slot/>
11
+ </FilterMenuRenderer>
12
+ </template>
13
+
14
+ <template
15
+ v-for="(filter, name) of filters"
16
+ #[name]>
17
+ <FluxMenu>
18
+ <FluxMenuGroup
19
+ :class="$style.filterHeader"
20
+ is-horizontal>
21
+ <FluxMenuItem
22
+ :class="$style.filterBack"
23
+ :label="translate('flux.back')"
24
+ icon-leading="angle-left"
25
+ @click="back()"/>
26
+
27
+ <FluxMenuItem
28
+ v-if="resettable?.includes(name)"
29
+ :class="$style.filterReset"
30
+ icon-leading="trash"
31
+ is-destructive
32
+ @click="reset(name)"/>
33
+ </FluxMenuGroup>
34
+
35
+ <VNodeRenderer :vnode="filter"/>
36
+ </FluxMenu>
37
+ </template>
38
+ </FluxWindow>
39
+ </div>
40
+ </template>
41
+
42
+ <script
43
+ lang="ts"
44
+ setup>
45
+ import { flattenVNodeTree, getComponentName, getComponentProps, vHeightTransition } from '@flux-ui/internals';
46
+ import type { FluxFilterOptionItem, FluxFilterState } from '@flux-ui/types';
47
+ import { computed, provide, unref, useTemplateRef, VNode } from 'vue';
48
+ import { useTranslate } from '$flux/composable/private';
49
+ import { FluxFilterInjectionKey } from '$flux/data';
50
+ import { FilterMenuRenderer, VNodeRenderer } from './primitive';
51
+ import FluxMenu from './FluxMenu.vue';
52
+ import FluxMenuGroup from './FluxMenuGroup.vue';
53
+ import FluxMenuItem from './FluxMenuItem.vue';
54
+ import FluxWindow from './FluxWindow.vue';
55
+ import $style from '$flux/css/component/Filter.module.scss';
56
+
57
+ const emit = defineEmits<{
58
+ reset: [string]
59
+ }>();
60
+
61
+ const modelValue = defineModel<FluxFilterState>({
62
+ required: true
63
+ });
64
+
65
+ defineProps<{
66
+ readonly resettable?: string[];
67
+ }>();
68
+
69
+ const slots = defineSlots<{
70
+ default(): any;
71
+ }>();
72
+
73
+ const translate = useTranslate();
74
+
75
+ const windowRef = useTemplateRef<{ back(to: string): void; }>('window');
76
+
77
+ const filters = computed<Record<string, VNode>>(() => {
78
+ const filters: { [key: string]: VNode; } = {};
79
+ const items = flattenVNodeTree(slots.default?.() ?? []);
80
+
81
+ for (const item of items) {
82
+ const name = getComponentName(item);
83
+
84
+ if (!name.startsWith('FluxFilter')) {
85
+ continue;
86
+ }
87
+
88
+ const props = getComponentProps<{ name: string; }>(item);
89
+
90
+ if (!props.name) {
91
+ continue;
92
+ }
93
+
94
+ filters[props.name] = item;
95
+ }
96
+
97
+ return filters;
98
+ });
99
+
100
+ function back(): void {
101
+ unref(windowRef)?.back('default');
102
+ }
103
+
104
+ function reset(name: string): void {
105
+ back();
106
+ emit('reset', name);
107
+ }
108
+
109
+ function getValue(name: string): FluxFilterOptionItem['value'] | undefined {
110
+ if (!hasValue(name)) {
111
+ return undefined;
112
+ }
113
+
114
+ return unref(modelValue)[name] as FluxFilterOptionItem['value'];
115
+ }
116
+
117
+ function hasValue(name: string): boolean {
118
+ return name in unref(modelValue);
119
+ }
120
+
121
+ function setValue(name: string, value: FluxFilterOptionItem['value']): void {
122
+ modelValue.value[name] = value;
123
+ }
124
+
125
+ provide(FluxFilterInjectionKey, {
126
+ state: modelValue,
127
+ back,
128
+ reset,
129
+ getValue,
130
+ hasValue,
131
+ setValue
132
+ });
133
+ </script>
@@ -0,0 +1,58 @@
1
+ <template>
2
+ <FluxDatePicker
3
+ :class="$style.filterDatePicker"
4
+ :max="max"
5
+ :min="min"
6
+ :model-value="currentValue"
7
+ @update:model-value="onDatePicked"/>
8
+ </template>
9
+
10
+ <script
11
+ lang="ts"
12
+ setup>
13
+ import type { FluxIconName } from '@flux-ui/types';
14
+ import { DateTime } from 'luxon';
15
+ import { computed, unref } from 'vue';
16
+ import { useFilterInjection } from '$flux/composable';
17
+ import FluxDatePicker from './FluxDatePicker.vue';
18
+ import $style from '$flux/css/component/Filter.module.scss';
19
+
20
+ const {
21
+ name
22
+ } = defineProps<{
23
+ readonly icon?: FluxIconName;
24
+ readonly label: string;
25
+ readonly max?: DateTime;
26
+ readonly min?: DateTime;
27
+ readonly name: string;
28
+ }>();
29
+
30
+ const {back, state, setValue} = useFilterInjection();
31
+
32
+ const currentValue = computed(() => {
33
+ const value = unref(state)[name];
34
+
35
+ if (!value) {
36
+ return null;
37
+ }
38
+
39
+ if (!DateTime.isDateTime(value)) {
40
+ if (typeof value === 'string') {
41
+ return DateTime.fromISO(value);
42
+ }
43
+
44
+ return null;
45
+ }
46
+
47
+ return value;
48
+ });
49
+
50
+ function onDatePicked(date: DateTime | DateTime[] | null): void {
51
+ if (!DateTime.isDateTime(date)) {
52
+ return;
53
+ }
54
+
55
+ setValue(name, date);
56
+ back();
57
+ }
58
+ </script>