@flux-ui/components 3.0.0-next.61 → 3.0.0-next.63
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/component/FluxActionStack.vue.d.ts +25 -19
- package/dist/component/FluxAspectRatio.vue.d.ts +4 -3
- package/dist/component/FluxBorderShine.vue.d.ts +1 -1
- package/dist/component/FluxCalendar.vue.d.ts +2 -6
- package/dist/component/FluxContainer.vue.d.ts +3 -2
- package/dist/component/FluxFilter.vue.d.ts +6 -7
- package/dist/component/FluxFilterBar.vue.d.ts +5 -4
- package/dist/component/FluxFilterBase.vue.d.ts +14 -11
- package/dist/component/FluxFilterDate.vue.d.ts +3 -6
- package/dist/component/FluxFilterDateRange.vue.d.ts +3 -6
- package/dist/component/FluxFilterOption.vue.d.ts +3 -6
- package/dist/component/FluxFilterOptionAsync.vue.d.ts +3 -6
- package/dist/component/FluxFilterOptions.vue.d.ts +3 -6
- package/dist/component/FluxFilterOptionsAsync.vue.d.ts +3 -6
- package/dist/component/FluxFilterRange.vue.d.ts +3 -7
- package/dist/component/FluxFilterWindow.vue.d.ts +3 -8
- package/dist/component/FluxFlex.vue.d.ts +30 -0
- package/dist/component/{FluxRow.vue.d.ts → FluxFlexItem.vue.d.ts} +5 -3
- package/dist/component/FluxGrid.vue.d.ts +3 -2
- package/dist/component/FluxGridColumn.vue.d.ts +3 -2
- package/dist/component/FluxKanbanColumn.vue.d.ts +3 -0
- package/dist/component/FluxScroller.vue.d.ts +32 -0
- package/dist/component/{FluxStack.vue.d.ts → FluxSplitView.vue.d.ts} +7 -6
- package/dist/component/{FluxColumn.vue.d.ts → FluxSplitViewPane.vue.d.ts} +4 -1
- package/dist/component/FluxSticky.vue.d.ts +34 -0
- package/dist/component/index.d.ts +6 -3
- package/dist/component/primitive/FilterBadge.vue.d.ts +2 -2
- package/dist/component/primitive/FilterItem.vue.d.ts +3 -2
- package/dist/component/primitive/SelectBase.vue.d.ts +4 -4
- package/dist/composable/private/index.d.ts +1 -0
- package/dist/composable/private/useSplitView.d.ts +23 -0
- package/dist/data/di.d.ts +19 -2
- package/dist/data/index.d.ts +0 -1
- package/dist/index.css +583 -331
- package/dist/index.d.ts +2 -0
- package/dist/index.js +8755 -8109
- package/dist/index.js.map +1 -1
- package/dist/util/defineFilter.d.ts +3 -0
- package/dist/util/filter.d.ts +7 -0
- package/dist/util/index.d.ts +2 -0
- package/dist/vite/defineFilterMacro.d.ts +3 -0
- package/dist/vite/index.d.ts +1 -0
- package/dist/vite.js +217 -0
- package/dist/vite.js.map +1 -0
- package/package.json +11 -7
- package/src/component/FluxActionBar.vue +3 -4
- package/src/component/FluxActionStack.vue +3 -3
- package/src/component/FluxAspectRatio.vue +5 -3
- package/src/component/FluxBadgeStack.vue +4 -4
- package/src/component/FluxButtonStack.vue +6 -4
- package/src/component/FluxCalendar.vue +160 -157
- package/src/component/FluxContainer.vue +4 -2
- package/src/component/FluxFilter.vue +10 -11
- package/src/component/FluxFilterBar.vue +71 -15
- package/src/component/FluxFilterBase.vue +65 -51
- package/src/component/FluxFilterDate.vue +24 -8
- package/src/component/FluxFilterDateRange.vue +27 -9
- package/src/component/FluxFilterOption.vue +20 -10
- package/src/component/FluxFilterOptionAsync.vue +19 -11
- package/src/component/FluxFilterOptions.vue +26 -11
- package/src/component/FluxFilterOptionsAsync.vue +28 -12
- package/src/component/FluxFilterRange.vue +25 -11
- package/src/component/FluxFilterWindow.vue +25 -11
- package/src/component/FluxFlex.vue +53 -0
- package/src/component/FluxFlexItem.vue +40 -0
- package/src/component/FluxFormDateTimeInput.vue +3 -4
- package/src/component/FluxGrid.vue +4 -2
- package/src/component/FluxGridColumn.vue +4 -2
- package/src/component/FluxInfoStack.vue +3 -3
- package/src/component/FluxItemStack.vue +4 -4
- package/src/component/FluxKanbanColumn.vue +16 -3
- package/src/component/FluxNoticeStack.vue +3 -3
- package/src/component/FluxProgressBar.vue +4 -3
- package/src/component/FluxScroller.vue +63 -0
- package/src/component/FluxSplitView.vue +101 -0
- package/src/component/FluxSplitViewPane.vue +23 -0
- package/src/component/FluxSticky.vue +67 -0
- package/src/component/FluxTagStack.vue +4 -4
- package/src/component/FluxToolbar.vue +3 -4
- package/src/component/FluxToolbarGroup.vue +3 -4
- package/src/component/FluxTooltipProvider.vue +56 -25
- package/src/component/index.ts +6 -3
- package/src/component/primitive/FilterBadge.vue +2 -2
- package/src/component/primitive/FilterItem.vue +4 -2
- package/src/component/primitive/FilterMenuRenderer.ts +10 -5
- package/src/component/primitive/FilterOptionBase.vue +1 -1
- package/src/composable/private/index.ts +1 -0
- package/src/composable/private/useAsyncFilterOptions.ts +1 -1
- package/src/composable/private/useFilterOption.ts +1 -1
- package/src/composable/private/useSplitView.ts +249 -0
- package/src/composable/useFilterInjection.ts +3 -1
- package/src/css/component/Calendar.module.scss +11 -17
- package/src/css/component/Comment.module.scss +3 -11
- package/src/css/component/Filter.module.scss +6 -2
- package/src/css/component/Flex.module.scss +84 -0
- package/src/css/component/Flyout.module.scss +1 -0
- package/src/css/component/Kanban.module.scss +31 -28
- package/src/css/component/LayerPane.module.scss +5 -0
- package/src/css/component/Layout.module.scss +0 -41
- package/src/css/component/Legend.module.scss +3 -4
- package/src/css/component/Menu.module.scss +1 -0
- package/src/css/component/Pagination.module.scss +1 -1
- package/src/css/component/Pane.module.scss +1 -1
- package/src/css/component/Progress.module.scss +2 -2
- package/src/css/component/Scroller.module.scss +109 -0
- package/src/css/component/SplitView.module.scss +78 -0
- package/src/css/component/Sticky.module.scss +35 -0
- package/src/css/component/Tab.module.scss +1 -0
- package/src/css/component/Table.module.scss +1 -0
- package/src/css/component/Tooltip.module.scss +14 -0
- package/src/data/di.ts +22 -2
- package/src/data/index.ts +0 -1
- package/src/index.ts +11 -0
- package/src/util/defineFilter.ts +10 -0
- package/src/util/filter.ts +63 -0
- package/src/util/index.ts +2 -0
- package/src/vite/defineFilterMacro.ts +335 -0
- package/src/vite/index.ts +1 -0
- package/dist/data/filter.d.ts +0 -7
- package/src/component/FluxColumn.vue +0 -24
- package/src/component/FluxRow.vue +0 -24
- package/src/component/FluxStack.vue +0 -41
- package/src/data/filter.ts +0 -165
|
@@ -1,109 +1,93 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<slot v-bind="{buttons, filters, menuItems}"/>
|
|
2
|
+
<slot v-bind="{buttons, filters, menuItems, clear, reset}"/>
|
|
3
3
|
</template>
|
|
4
4
|
|
|
5
5
|
<script
|
|
6
6
|
lang="ts"
|
|
7
7
|
setup>
|
|
8
8
|
import { flattenVNodeTree, getComponentName, getComponentProps } from '@flux-ui/internals';
|
|
9
|
-
import type {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import { filterParsers, FluxFilterInjectionKey } from '~flux/components/data';
|
|
13
|
-
|
|
14
|
-
type FluxFilterType = keyof FluxFilterSpecMap;
|
|
15
|
-
|
|
16
|
-
function applyParser<K extends FluxFilterType>(type: K, spec: FluxFilterSpecMap[K]): FluxFilterItem {
|
|
17
|
-
const parser = filterParsers[type] as (spec: FluxFilterSpecMap[K]) => FluxFilterItem;
|
|
18
|
-
|
|
19
|
-
return parser(spec);
|
|
20
|
-
}
|
|
9
|
+
import type { FluxFilterDefinition, FluxFilterState, FluxFilterValue } from '@flux-ui/types';
|
|
10
|
+
import { computed, provide, unref, watchEffect, type VNode } from 'vue';
|
|
11
|
+
import { FluxFilterInjectionKey } from '~flux/components/data';
|
|
21
12
|
|
|
22
13
|
const emit = defineEmits<{
|
|
23
14
|
back: [];
|
|
24
|
-
|
|
15
|
+
clear: [string];
|
|
16
|
+
reset: [string];
|
|
25
17
|
}>();
|
|
26
18
|
|
|
27
19
|
const modelValue = defineModel<FluxFilterState>({
|
|
28
20
|
required: true
|
|
29
21
|
});
|
|
30
22
|
|
|
31
|
-
defineProps<{
|
|
32
|
-
readonly resettable?: string[];
|
|
33
|
-
}>();
|
|
34
|
-
|
|
35
23
|
const slots = defineSlots<{
|
|
36
24
|
default(props: {
|
|
37
|
-
readonly buttons: Record<string,
|
|
25
|
+
readonly buttons: Record<string, FluxFilterDefinition>;
|
|
38
26
|
readonly filters: Record<string, VNode>;
|
|
39
|
-
readonly menuItems: (
|
|
27
|
+
readonly menuItems: (FluxFilterDefinition | VNode)[][];
|
|
28
|
+
clear(name: string): void;
|
|
29
|
+
reset(name: string): void;
|
|
40
30
|
}): VNode[];
|
|
41
31
|
|
|
42
32
|
filters(): VNode[];
|
|
43
33
|
}>();
|
|
44
34
|
|
|
35
|
+
function resolveDefinition(vnode: VNode): FluxFilterDefinition | null {
|
|
36
|
+
const factory = (vnode.type as { __filterDefinitionFactory?: (props: unknown) => FluxFilterDefinition })?.__filterDefinitionFactory;
|
|
37
|
+
|
|
38
|
+
return typeof factory === 'function' ? factory(getComponentProps(vnode)) : null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const flattenedFilters = computed(() => flattenVNodeTree(slots.filters?.() ?? []));
|
|
42
|
+
|
|
45
43
|
const buttons = computed(() => {
|
|
46
|
-
const buttons: Record<string,
|
|
44
|
+
const buttons: Record<string, FluxFilterDefinition> = {};
|
|
47
45
|
const items = unref(flattenedFilters);
|
|
48
46
|
|
|
49
47
|
for (const item of items) {
|
|
50
|
-
const
|
|
48
|
+
const definition = resolveDefinition(item);
|
|
51
49
|
|
|
52
|
-
if (!
|
|
50
|
+
if (!definition) {
|
|
53
51
|
continue;
|
|
54
52
|
}
|
|
55
53
|
|
|
56
|
-
|
|
57
|
-
const props = getComponentProps<FluxFilterSpecMap[FluxFilterType]>(item);
|
|
58
|
-
|
|
59
|
-
buttons[props.name] = applyParser(type, props);
|
|
54
|
+
buttons[definition.name] = definition;
|
|
60
55
|
}
|
|
61
56
|
|
|
62
57
|
return buttons;
|
|
63
58
|
});
|
|
64
59
|
|
|
65
|
-
const flattenedFilters = computed(() => flattenVNodeTree(slots.filters?.() ?? []));
|
|
66
|
-
|
|
67
60
|
const filters = computed<Record<string, VNode>>(() => {
|
|
68
61
|
const filters: { [key: string]: VNode; } = {};
|
|
69
62
|
const items = unref(flattenedFilters);
|
|
70
63
|
|
|
71
64
|
for (const item of items) {
|
|
72
|
-
const
|
|
65
|
+
const definition = resolveDefinition(item);
|
|
73
66
|
|
|
74
|
-
if (!
|
|
67
|
+
if (!definition) {
|
|
75
68
|
continue;
|
|
76
69
|
}
|
|
77
70
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (!props.name) {
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
filters[props.name] = item;
|
|
71
|
+
filters[definition.name] = item;
|
|
85
72
|
}
|
|
86
73
|
|
|
87
74
|
return filters;
|
|
88
75
|
});
|
|
89
76
|
|
|
90
|
-
const menuItems = computed<(
|
|
91
|
-
const menuItems: (
|
|
77
|
+
const menuItems = computed<(FluxFilterDefinition | VNode)[][]>(() => {
|
|
78
|
+
const menuItems: (FluxFilterDefinition | VNode)[][] = [[]];
|
|
92
79
|
const items = unref(flattenedFilters);
|
|
93
80
|
|
|
94
81
|
for (const item of items) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (name === 'FluxSeparator') {
|
|
82
|
+
if (getComponentName(item) === 'FluxSeparator') {
|
|
98
83
|
menuItems.push([]);
|
|
99
84
|
continue;
|
|
100
85
|
}
|
|
101
86
|
|
|
102
|
-
|
|
103
|
-
const type = camelCase(name.substring(10)) as FluxFilterType;
|
|
104
|
-
const props = getComponentProps<FluxFilterSpecMap[FluxFilterType]>(item);
|
|
87
|
+
const definition = resolveDefinition(item);
|
|
105
88
|
|
|
106
|
-
|
|
89
|
+
if (definition) {
|
|
90
|
+
menuItems[menuItems.length - 1].push(definition);
|
|
107
91
|
continue;
|
|
108
92
|
}
|
|
109
93
|
|
|
@@ -113,37 +97,67 @@
|
|
|
113
97
|
return menuItems;
|
|
114
98
|
});
|
|
115
99
|
|
|
100
|
+
watchEffect(() => {
|
|
101
|
+
const state = unref(modelValue);
|
|
102
|
+
|
|
103
|
+
for (const definition of Object.values(unref(buttons))) {
|
|
104
|
+
if (definition.defaultValue !== undefined && state[definition.name] === undefined) {
|
|
105
|
+
modelValue.value[definition.name] = definition.defaultValue as FluxFilterValue;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
116
110
|
function back(): void {
|
|
117
111
|
emit('back');
|
|
118
112
|
}
|
|
119
113
|
|
|
114
|
+
function clear(name: string): void {
|
|
115
|
+
const definition = unref(buttons)[name];
|
|
116
|
+
|
|
117
|
+
back();
|
|
118
|
+
modelValue.value[name] = null;
|
|
119
|
+
definition?.onClear?.();
|
|
120
|
+
emit('clear', name);
|
|
121
|
+
}
|
|
122
|
+
|
|
120
123
|
function reset(name: string): void {
|
|
124
|
+
const definition = unref(buttons)[name];
|
|
125
|
+
|
|
121
126
|
back();
|
|
127
|
+
modelValue.value[name] = (definition?.defaultValue ?? null) as FluxFilterValue;
|
|
128
|
+
definition?.onClear?.();
|
|
122
129
|
emit('reset', name);
|
|
123
130
|
}
|
|
124
131
|
|
|
125
|
-
function
|
|
132
|
+
function getDefinition(name: string): FluxFilterDefinition | undefined {
|
|
133
|
+
return unref(buttons)[name];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function getValue(name: string): FluxFilterValue | undefined {
|
|
126
137
|
if (!hasValue(name)) {
|
|
127
138
|
return undefined;
|
|
128
139
|
}
|
|
129
140
|
|
|
130
|
-
return unref(modelValue)[name] as
|
|
141
|
+
return unref(modelValue)[name] as FluxFilterValue;
|
|
131
142
|
}
|
|
132
143
|
|
|
133
144
|
function hasValue(name: string): boolean {
|
|
134
145
|
return name in unref(modelValue);
|
|
135
146
|
}
|
|
136
147
|
|
|
137
|
-
function setValue(name: string, value:
|
|
148
|
+
function setValue(name: string, value: FluxFilterValue): void {
|
|
138
149
|
modelValue.value[name] = value;
|
|
150
|
+
unref(buttons)[name]?.onChange?.(value);
|
|
139
151
|
}
|
|
140
152
|
|
|
141
153
|
provide(FluxFilterInjectionKey, {
|
|
142
154
|
state: modelValue,
|
|
143
155
|
back,
|
|
144
|
-
|
|
156
|
+
clear,
|
|
157
|
+
getDefinition,
|
|
145
158
|
getValue,
|
|
146
159
|
hasValue,
|
|
160
|
+
reset,
|
|
147
161
|
setValue
|
|
148
162
|
});
|
|
149
163
|
</script>
|
|
@@ -10,22 +10,38 @@
|
|
|
10
10
|
<script
|
|
11
11
|
lang="ts"
|
|
12
12
|
setup>
|
|
13
|
-
import type {
|
|
13
|
+
import type { FluxFilterDateSpec } from '@flux-ui/types';
|
|
14
14
|
import { DateTime } from 'luxon';
|
|
15
15
|
import { computed, unref } from 'vue';
|
|
16
16
|
import { useFilterInjection } from '~flux/components/composable';
|
|
17
|
+
import { defineFilter, pickFilterCommon } from '~flux/components/util';
|
|
17
18
|
import FluxDatePicker from './FluxDatePicker.vue';
|
|
18
19
|
import $style from '~flux/components/css/component/Filter.module.scss';
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
name
|
|
22
|
-
} = defineProps<{
|
|
23
|
-
readonly icon?: FluxIconName;
|
|
24
|
-
readonly label: string;
|
|
21
|
+
type Props = FluxFilterDateSpec & {
|
|
25
22
|
readonly max?: DateTime;
|
|
26
23
|
readonly min?: DateTime;
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
defineFilter<Props>(p => ({
|
|
27
|
+
...pickFilterCommon(p),
|
|
28
|
+
type: 'date',
|
|
29
|
+
async getValueLabel(value) {
|
|
30
|
+
if (!DateTime.isDateTime(value)) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return value.toLocaleString({
|
|
35
|
+
day: 'numeric',
|
|
36
|
+
month: 'short',
|
|
37
|
+
year: 'numeric'
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}));
|
|
41
|
+
|
|
42
|
+
const {
|
|
43
|
+
name
|
|
44
|
+
} = defineProps<Props>();
|
|
29
45
|
|
|
30
46
|
const {back, state, setValue} = useFilterInjection();
|
|
31
47
|
|
|
@@ -11,24 +11,42 @@
|
|
|
11
11
|
<script
|
|
12
12
|
lang="ts"
|
|
13
13
|
setup>
|
|
14
|
-
import type {
|
|
14
|
+
import type { FluxFilterDateRangeSpec } from '@flux-ui/types';
|
|
15
15
|
import { DateTime } from 'luxon';
|
|
16
16
|
import { computed, unref } from 'vue';
|
|
17
17
|
import { useFilterInjection } from '~flux/components/composable';
|
|
18
|
+
import { createLabelForDateRange, defineFilter, pickFilterCommon } from '~flux/components/util';
|
|
18
19
|
import FluxDatePicker from './FluxDatePicker.vue';
|
|
19
20
|
import $style from '~flux/components/css/component/Filter.module.scss';
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
name,
|
|
23
|
-
rangeMode = 'range'
|
|
24
|
-
} = defineProps<{
|
|
25
|
-
readonly icon?: FluxIconName;
|
|
26
|
-
readonly label: string;
|
|
22
|
+
type Props = FluxFilterDateRangeSpec & {
|
|
27
23
|
readonly max?: DateTime;
|
|
28
24
|
readonly min?: DateTime;
|
|
29
|
-
readonly name: string;
|
|
30
25
|
readonly rangeMode?: 'range' | 'week' | 'month';
|
|
31
|
-
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
defineFilter<Props>(p => ({
|
|
29
|
+
...pickFilterCommon(p),
|
|
30
|
+
type: 'dateRange',
|
|
31
|
+
async getValueLabel(value) {
|
|
32
|
+
if (!Array.isArray(value) || value.length !== 2) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const [start, end] = value;
|
|
37
|
+
|
|
38
|
+
if (!DateTime.isDateTime(start) || !DateTime.isDateTime(end)) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return createLabelForDateRange(start, end);
|
|
43
|
+
}
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
const {
|
|
47
|
+
name,
|
|
48
|
+
rangeMode = 'range'
|
|
49
|
+
} = defineProps<Props>();
|
|
32
50
|
|
|
33
51
|
const {back, state, setValue} = useFilterInjection();
|
|
34
52
|
|
|
@@ -11,25 +11,35 @@
|
|
|
11
11
|
<script
|
|
12
12
|
lang="ts"
|
|
13
13
|
setup>
|
|
14
|
-
import type {
|
|
14
|
+
import type { FluxFilterOptionSpec } from '@flux-ui/types';
|
|
15
15
|
import { computed, unref } from 'vue';
|
|
16
16
|
import { useFilterOptionSingle } from '~flux/components/composable/private';
|
|
17
|
-
import { isFluxFilterOptionHeader } from '~flux/components/
|
|
17
|
+
import { defineFilter, isFluxFilterOptionHeader, isFluxFilterOptionItem, pickFilterCommon } from '~flux/components/util';
|
|
18
18
|
import { FilterOptionBase } from './primitive';
|
|
19
19
|
|
|
20
|
+
type Props = FluxFilterOptionSpec & {
|
|
21
|
+
readonly isSearchable?: boolean;
|
|
22
|
+
readonly searchPlaceholder?: string;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
defineFilter<Props>(p => {
|
|
26
|
+
const items = p.options.filter(isFluxFilterOptionItem);
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
...pickFilterCommon(p),
|
|
30
|
+
type: 'option',
|
|
31
|
+
async getValueLabel(value) {
|
|
32
|
+
return items.find(o => o.value === value)?.label ?? null;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
|
+
|
|
20
37
|
const modelSearch = defineModel<string>('searchQuery', {default: ''});
|
|
21
38
|
|
|
22
39
|
const {
|
|
23
40
|
name,
|
|
24
41
|
options
|
|
25
|
-
} = defineProps<
|
|
26
|
-
readonly icon?: FluxIconName;
|
|
27
|
-
readonly isSearchable?: boolean;
|
|
28
|
-
readonly label: string;
|
|
29
|
-
readonly name: string;
|
|
30
|
-
readonly options: FluxFilterOptionRow[];
|
|
31
|
-
readonly searchPlaceholder?: string;
|
|
32
|
-
}>();
|
|
42
|
+
} = defineProps<Props>();
|
|
33
43
|
|
|
34
44
|
const {currentValue, onSelect} = useFilterOptionSingle(name);
|
|
35
45
|
|
|
@@ -12,11 +12,28 @@
|
|
|
12
12
|
<script
|
|
13
13
|
lang="ts"
|
|
14
14
|
setup>
|
|
15
|
-
import type { FluxFilterOptionRow, FluxFilterValue
|
|
15
|
+
import type { FluxFilterOptionAsyncSpec, FluxFilterOptionRow, FluxFilterValue } from '@flux-ui/types';
|
|
16
16
|
import { computed, unref } from 'vue';
|
|
17
17
|
import { useAsyncFilterOptions, useFilterOptionSingle } from '~flux/components/composable/private';
|
|
18
|
+
import { defineFilter, isFluxFilterOptionItem, pickFilterCommon } from '~flux/components/util';
|
|
18
19
|
import { FilterOptionBase } from './primitive';
|
|
19
20
|
|
|
21
|
+
type Props = FluxFilterOptionAsyncSpec & {
|
|
22
|
+
fetchRelevant(): Promise<FluxFilterOptionRow[]>;
|
|
23
|
+
fetchSearch(searchQuery: string): Promise<FluxFilterOptionRow[]>;
|
|
24
|
+
readonly searchPlaceholder?: string;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
defineFilter<Props>(p => ({
|
|
28
|
+
...pickFilterCommon(p),
|
|
29
|
+
type: 'option',
|
|
30
|
+
async getValueLabel(value) {
|
|
31
|
+
const items = (await p.fetchOptions([value])).filter(isFluxFilterOptionItem);
|
|
32
|
+
|
|
33
|
+
return items.find(o => o.value === value)?.label ?? null;
|
|
34
|
+
}
|
|
35
|
+
}));
|
|
36
|
+
|
|
20
37
|
const modelSearch = defineModel<string>('searchQuery', {
|
|
21
38
|
default: ''
|
|
22
39
|
});
|
|
@@ -26,16 +43,7 @@
|
|
|
26
43
|
fetchRelevant: fetchRelevantProp,
|
|
27
44
|
fetchSearch: fetchSearchProp,
|
|
28
45
|
name
|
|
29
|
-
} = defineProps<
|
|
30
|
-
fetchOptions(ids: FluxFilterValue[]): Promise<FluxFilterOptionRow[]>;
|
|
31
|
-
fetchRelevant(): Promise<FluxFilterOptionRow[]>;
|
|
32
|
-
fetchSearch(searchQuery: string): Promise<FluxFilterOptionRow[]>;
|
|
33
|
-
|
|
34
|
-
readonly icon?: FluxIconName;
|
|
35
|
-
readonly label: string;
|
|
36
|
-
readonly name: string;
|
|
37
|
-
readonly searchPlaceholder?: string;
|
|
38
|
-
}>();
|
|
46
|
+
} = defineProps<Props>();
|
|
39
47
|
|
|
40
48
|
const {currentValue, onSelect} = useFilterOptionSingle(name);
|
|
41
49
|
|
|
@@ -11,12 +11,34 @@
|
|
|
11
11
|
<script
|
|
12
12
|
lang="ts"
|
|
13
13
|
setup>
|
|
14
|
-
import type {
|
|
14
|
+
import type { FluxFilterOptionsSpec } from '@flux-ui/types';
|
|
15
15
|
import { computed, unref } from 'vue';
|
|
16
|
-
import { useFilterOptionMulti } from '~flux/components/composable/private';
|
|
17
|
-
import { isFluxFilterOptionHeader } from '~flux/components/
|
|
16
|
+
import { useFilterOptionMulti, useTranslate } from '~flux/components/composable/private';
|
|
17
|
+
import { defineFilter, generateMultiOptionsLabel, isFluxFilterOptionHeader, isFluxFilterOptionItem, pickFilterCommon } from '~flux/components/util';
|
|
18
18
|
import { FilterOptionBase } from './primitive';
|
|
19
19
|
|
|
20
|
+
type Props = FluxFilterOptionsSpec & {
|
|
21
|
+
readonly isSearchable?: boolean;
|
|
22
|
+
readonly searchPlaceholder?: string;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
defineFilter<Props>(p => {
|
|
26
|
+
const items = p.options.filter(isFluxFilterOptionItem);
|
|
27
|
+
const translate = useTranslate();
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
...pickFilterCommon(p),
|
|
31
|
+
type: 'options',
|
|
32
|
+
async getValueLabel(value) {
|
|
33
|
+
if (!Array.isArray(value)) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return generateMultiOptionsLabel(translate, items, value);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
});
|
|
41
|
+
|
|
20
42
|
const modelSearch = defineModel<string>('searchQuery', {
|
|
21
43
|
default: ''
|
|
22
44
|
});
|
|
@@ -24,14 +46,7 @@
|
|
|
24
46
|
const {
|
|
25
47
|
name,
|
|
26
48
|
options
|
|
27
|
-
} = defineProps<
|
|
28
|
-
readonly icon?: FluxIconName;
|
|
29
|
-
readonly isSearchable?: boolean;
|
|
30
|
-
readonly label: string;
|
|
31
|
-
readonly name: string;
|
|
32
|
-
readonly options: FluxFilterOptionRow[];
|
|
33
|
-
readonly searchPlaceholder?: string;
|
|
34
|
-
}>();
|
|
49
|
+
} = defineProps<Props>();
|
|
35
50
|
|
|
36
51
|
const {currentValue, onSelect} = useFilterOptionMulti(name);
|
|
37
52
|
|
|
@@ -12,10 +12,35 @@
|
|
|
12
12
|
<script
|
|
13
13
|
lang="ts"
|
|
14
14
|
setup>
|
|
15
|
-
import type { FluxFilterOptionRow,
|
|
16
|
-
import { useAsyncFilterOptions, useFilterOptionMulti } from '~flux/components/composable/private';
|
|
15
|
+
import type { FluxFilterOptionRow, FluxFilterOptionsAsyncSpec, FluxFilterValue } from '@flux-ui/types';
|
|
16
|
+
import { useAsyncFilterOptions, useFilterOptionMulti, useTranslate } from '~flux/components/composable/private';
|
|
17
|
+
import { defineFilter, generateMultiOptionsLabel, isFluxFilterOptionItem, pickFilterCommon } from '~flux/components/util';
|
|
17
18
|
import { FilterOptionBase } from './primitive';
|
|
18
19
|
|
|
20
|
+
type Props = FluxFilterOptionsAsyncSpec & {
|
|
21
|
+
fetchRelevant(): Promise<FluxFilterOptionRow[]>;
|
|
22
|
+
fetchSearch(searchQuery: string): Promise<FluxFilterOptionRow[]>;
|
|
23
|
+
readonly searchPlaceholder?: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
defineFilter<Props>(p => {
|
|
27
|
+
const translate = useTranslate();
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
...pickFilterCommon(p),
|
|
31
|
+
type: 'options',
|
|
32
|
+
async getValueLabel(value) {
|
|
33
|
+
if (!Array.isArray(value)) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const items = (await p.fetchOptions(value)).filter(isFluxFilterOptionItem);
|
|
38
|
+
|
|
39
|
+
return generateMultiOptionsLabel(translate, items, value);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
});
|
|
43
|
+
|
|
19
44
|
const modelSearch = defineModel<string>('searchQuery', {
|
|
20
45
|
default: ''
|
|
21
46
|
});
|
|
@@ -25,16 +50,7 @@
|
|
|
25
50
|
fetchRelevant: fetchRelevantProp,
|
|
26
51
|
fetchSearch: fetchSearchProp,
|
|
27
52
|
name
|
|
28
|
-
} = defineProps<
|
|
29
|
-
fetchOptions(ids: FluxFilterValue[]): Promise<FluxFilterOptionRow[]>;
|
|
30
|
-
fetchRelevant(): Promise<FluxFilterOptionRow[]>;
|
|
31
|
-
fetchSearch(searchQuery: string): Promise<FluxFilterOptionRow[]>;
|
|
32
|
-
|
|
33
|
-
readonly icon?: FluxIconName;
|
|
34
|
-
readonly label: string;
|
|
35
|
-
readonly name: string;
|
|
36
|
-
readonly searchPlaceholder?: string;
|
|
37
|
-
}>();
|
|
53
|
+
} = defineProps<Props>();
|
|
38
54
|
|
|
39
55
|
const {currentValue, onSelect} = useFilterOptionMulti(name);
|
|
40
56
|
|
|
@@ -38,31 +38,45 @@
|
|
|
38
38
|
lang="ts"
|
|
39
39
|
setup>
|
|
40
40
|
import { formatNumber } from '@basmilius/utils';
|
|
41
|
-
import type {
|
|
41
|
+
import type { FluxFilterRangeSpec } from '@flux-ui/types';
|
|
42
42
|
import { computed, unref } from 'vue';
|
|
43
43
|
import { useFilterInjection } from '~flux/components/composable';
|
|
44
44
|
import { useTranslate } from '~flux/components/composable/private';
|
|
45
|
+
import { defineFilter, pickFilterCommon } from '~flux/components/util';
|
|
45
46
|
import FluxFormColumn from './FluxFormColumn.vue';
|
|
46
47
|
import FluxFormField from './FluxFormField.vue';
|
|
47
48
|
import FluxFormSlider from './FluxFormSlider.vue';
|
|
48
49
|
import FluxPaneBody from './FluxPaneBody.vue';
|
|
49
50
|
|
|
51
|
+
type Props = FluxFilterRangeSpec & {
|
|
52
|
+
readonly isTicksVisible?: boolean;
|
|
53
|
+
readonly max: number;
|
|
54
|
+
readonly min: number;
|
|
55
|
+
readonly step?: number;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
defineFilter<Props>(p => ({
|
|
59
|
+
...pickFilterCommon(p),
|
|
60
|
+
type: 'range',
|
|
61
|
+
async getValueLabel(value) {
|
|
62
|
+
if (!value || !Array.isArray(value) || value.length !== 2) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const [lower, upper] = value as number[];
|
|
67
|
+
const format = p.formatter ?? formatNumber;
|
|
68
|
+
|
|
69
|
+
return `${format(lower!)} – ${format(upper!)}`;
|
|
70
|
+
}
|
|
71
|
+
}));
|
|
72
|
+
|
|
50
73
|
const {
|
|
51
74
|
formatter = formatNumber,
|
|
52
75
|
max,
|
|
53
76
|
min,
|
|
54
77
|
name,
|
|
55
78
|
step = 1
|
|
56
|
-
} = defineProps<
|
|
57
|
-
readonly icon?: FluxIconName;
|
|
58
|
-
readonly isTicksVisible?: boolean;
|
|
59
|
-
readonly label: string;
|
|
60
|
-
readonly name: string;
|
|
61
|
-
readonly max: number;
|
|
62
|
-
readonly min: number;
|
|
63
|
-
readonly step?: number;
|
|
64
|
-
readonly formatter?: (value: number) => string;
|
|
65
|
-
}>();
|
|
79
|
+
} = defineProps<Props>();
|
|
66
80
|
|
|
67
81
|
const {state, setValue} = useFilterInjection();
|
|
68
82
|
const translate = useTranslate();
|
|
@@ -25,11 +25,17 @@
|
|
|
25
25
|
@click="back()"/>
|
|
26
26
|
|
|
27
27
|
<FluxMenuItem
|
|
28
|
-
v-if="
|
|
29
|
-
:class="$style.
|
|
28
|
+
v-if="canReset(name)"
|
|
29
|
+
:class="$style.filterAction"
|
|
30
|
+
icon-leading="rotate-left"
|
|
31
|
+
@click="reset(name)"
|
|
32
|
+
style="flex-grow: 0"/>
|
|
33
|
+
|
|
34
|
+
<FluxMenuItem
|
|
35
|
+
:class="$style.filterAction"
|
|
30
36
|
icon-leading="trash"
|
|
31
37
|
is-destructive
|
|
32
|
-
@click="
|
|
38
|
+
@click="clear(name)"
|
|
33
39
|
style="flex-grow: 0"/>
|
|
34
40
|
</FluxMenuGroup>
|
|
35
41
|
|
|
@@ -44,9 +50,11 @@
|
|
|
44
50
|
lang="ts"
|
|
45
51
|
setup>
|
|
46
52
|
import { vHeightTransition } from '@flux-ui/internals';
|
|
47
|
-
import type {
|
|
53
|
+
import type { FluxFilterDefinition } from '@flux-ui/types';
|
|
48
54
|
import { unref, useTemplateRef, type VNode } from 'vue';
|
|
55
|
+
import { useFilterInjection } from '~flux/components/composable';
|
|
49
56
|
import { useTranslate } from '~flux/components/composable/private';
|
|
57
|
+
import { isResettable } from '~flux/components/util';
|
|
50
58
|
import FluxMenu from './FluxMenu.vue';
|
|
51
59
|
import FluxMenuGroup from './FluxMenuGroup.vue';
|
|
52
60
|
import FluxMenuItem from './FluxMenuItem.vue';
|
|
@@ -54,20 +62,17 @@
|
|
|
54
62
|
import { FilterMenuRenderer, VNodeRenderer } from './primitive';
|
|
55
63
|
import $style from '~flux/components/css/component/Filter.module.scss';
|
|
56
64
|
|
|
57
|
-
const emit = defineEmits<{
|
|
58
|
-
reset: [string]
|
|
59
|
-
}>();
|
|
60
|
-
|
|
61
65
|
defineProps<{
|
|
62
66
|
readonly filters: Record<string, VNode>;
|
|
63
|
-
readonly menuItems: (
|
|
64
|
-
readonly resettable?: string[];
|
|
67
|
+
readonly menuItems: (FluxFilterDefinition | VNode)[][];
|
|
65
68
|
}>();
|
|
66
69
|
|
|
67
70
|
defineSlots<{
|
|
68
71
|
default(): VNode[];
|
|
69
72
|
}>();
|
|
70
73
|
|
|
74
|
+
const {clear: clearFilter, getDefinition, getValue, reset: resetFilter} = useFilterInjection();
|
|
75
|
+
|
|
71
76
|
const translate = useTranslate();
|
|
72
77
|
|
|
73
78
|
const windowRef = useTemplateRef<{ back(to: string): void; }>('window');
|
|
@@ -76,8 +81,17 @@
|
|
|
76
81
|
unref(windowRef)?.back('default');
|
|
77
82
|
}
|
|
78
83
|
|
|
84
|
+
function clear(name: string): void {
|
|
85
|
+
back();
|
|
86
|
+
clearFilter(name);
|
|
87
|
+
}
|
|
88
|
+
|
|
79
89
|
function reset(name: string): void {
|
|
80
90
|
back();
|
|
81
|
-
|
|
91
|
+
resetFilter(name);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function canReset(name: string): boolean {
|
|
95
|
+
return isResettable(getDefinition(name), getValue(name));
|
|
82
96
|
}
|
|
83
97
|
</script>
|