@flux-ui/components 3.0.0-next.28 → 3.0.0-next.29
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/primitive/FilterBadge.vue.d.ts +12 -0
- package/dist/component/primitive/index.d.ts +2 -0
- package/dist/index.css +151 -109
- package/dist/index.js +263 -167
- package/dist/index.js.map +1 -1
- package/package.json +10 -10
- package/src/component/FluxFilterBar.vue +32 -8
- package/src/component/FluxOverflowBar.vue +11 -1
- package/src/component/primitive/FilterBadge.vue +43 -0
- package/src/component/primitive/index.ts +2 -0
- package/src/css/component/Badge.module.scss +1 -0
- package/src/css/component/Filter.module.scss +31 -2
- package/src/css/component/Icon.module.scss +25 -23
- package/src/css/component/Item.module.scss +2 -1
- package/src/css/component/Menu.module.scss +3 -0
- package/src/css/component/Timeline.module.scss +6 -2
- package/src/css/component/base/Button.module.scss +10 -0
- package/src/css/typography.scss +4 -2
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flux-ui/components",
|
|
3
3
|
"description": "A set of opiniated UI components.",
|
|
4
|
-
"version": "3.0.0-next.
|
|
4
|
+
"version": "3.0.0-next.29",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"funding": "https://github.com/sponsors/basmilius",
|
|
@@ -51,28 +51,28 @@
|
|
|
51
51
|
"typings": "./dist/index.d.ts",
|
|
52
52
|
"sideEffects": false,
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@basmilius/common": "^3.
|
|
55
|
-
"@basmilius/utils": "^3.
|
|
56
|
-
"@flux-ui/internals": "3.0.0-next.
|
|
57
|
-
"@flux-ui/types": "3.0.0-next.
|
|
54
|
+
"@basmilius/common": "^3.11.0",
|
|
55
|
+
"@basmilius/utils": "^3.11.0",
|
|
56
|
+
"@flux-ui/internals": "3.0.0-next.29",
|
|
57
|
+
"@flux-ui/types": "3.0.0-next.29",
|
|
58
58
|
"@fortawesome/fontawesome-common-types": "^7.2.0",
|
|
59
59
|
"clsx": "^2.1.1",
|
|
60
60
|
"imask": "^7.6.1",
|
|
61
61
|
"lodash-es": "^4.17.23",
|
|
62
62
|
"luxon": "^3.7.2",
|
|
63
|
-
"vue": "^3.6.0-beta.
|
|
63
|
+
"vue": "^3.6.0-beta.7"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
|
-
"@basmilius/vite-preset": "^3.
|
|
66
|
+
"@basmilius/vite-preset": "^3.11.0",
|
|
67
67
|
"@types/lodash-es": "^4.17.12",
|
|
68
68
|
"@types/luxon": "^3.7.1",
|
|
69
|
-
"@types/node": "^25.3.
|
|
69
|
+
"@types/node": "^25.3.3",
|
|
70
70
|
"@vitejs/plugin-vue": "^6.0.4",
|
|
71
|
-
"@vue/tsconfig": "^0.
|
|
71
|
+
"@vue/tsconfig": "^0.9.0",
|
|
72
72
|
"pinia": "^3.0.4",
|
|
73
73
|
"sass-embedded": "^1.97.3",
|
|
74
74
|
"typescript": "^5.9.3",
|
|
75
|
-
"vite": "^8.0.0-beta.
|
|
75
|
+
"vite": "^8.0.0-beta.16",
|
|
76
76
|
"vue-tsc": "^3.2.5"
|
|
77
77
|
}
|
|
78
78
|
}
|
|
@@ -20,16 +20,35 @@
|
|
|
20
20
|
<template
|
|
21
21
|
v-for="button of buttons"
|
|
22
22
|
:key="button.name">
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
<FluxFlyout>
|
|
24
|
+
<template #opener="{open}">
|
|
25
|
+
<FluxSecondaryButton
|
|
26
|
+
v-if="modelValue[button.name]"
|
|
27
|
+
:class="$style.filterButton"
|
|
28
|
+
:icon-leading="button.icon"
|
|
29
|
+
:label="button.label"
|
|
30
|
+
@click="open()">
|
|
31
|
+
<template #after>
|
|
32
|
+
<FilterBadge
|
|
33
|
+
:item="button"
|
|
34
|
+
:value="modelValue[button.name]"/>
|
|
35
|
+
</template>
|
|
36
|
+
</FluxSecondaryButton>
|
|
37
|
+
</template>
|
|
38
|
+
|
|
39
|
+
<div :class="$style.filter">
|
|
40
|
+
<FluxMenu>
|
|
41
|
+
<VNodeRenderer :vnode="filters[button.name]"/>
|
|
42
|
+
</FluxMenu>
|
|
43
|
+
</div>
|
|
44
|
+
</FluxFlyout>
|
|
27
45
|
</template>
|
|
28
46
|
|
|
29
47
|
<template #overflow>
|
|
30
|
-
<
|
|
31
|
-
v-if="
|
|
32
|
-
|
|
48
|
+
<FluxSeparator
|
|
49
|
+
v-if="isFiltered"
|
|
50
|
+
direction="vertical"
|
|
51
|
+
style="margin-top: 9px; margin-bottom: 9px"/>
|
|
33
52
|
|
|
34
53
|
<FluxFlyout>
|
|
35
54
|
<template #opener="{open}">
|
|
@@ -56,14 +75,17 @@
|
|
|
56
75
|
lang="ts"
|
|
57
76
|
setup>
|
|
58
77
|
import type { FluxFilterState } from '@flux-ui/types';
|
|
78
|
+
import { computed, unref } from "vue";
|
|
79
|
+
import { FilterBadge, VNodeRenderer } from '$flux/component/primitive';
|
|
59
80
|
import FluxFilterBase from './FluxFilterBase.vue';
|
|
60
81
|
import FluxFilterWindow from './FluxFilterWindow.vue';
|
|
61
82
|
import FluxFlyout from './FluxFlyout.vue';
|
|
62
83
|
import FluxFormInput from './FluxFormInput.vue';
|
|
84
|
+
import FluxMenu from '$flux/component/FluxMenu.vue';
|
|
63
85
|
import FluxOverflowBar from './FluxOverflowBar.vue';
|
|
64
86
|
import FluxSecondaryButton from './FluxSecondaryButton.vue';
|
|
87
|
+
import FluxSeparator from '$flux/component/FluxSeparator.vue';
|
|
65
88
|
import $style from '$flux/css/component/Filter.module.scss';
|
|
66
|
-
import FluxSpacing from '$flux/component/FluxSpacing.vue';
|
|
67
89
|
|
|
68
90
|
const emit = defineEmits<{
|
|
69
91
|
reset: [string];
|
|
@@ -87,6 +109,8 @@
|
|
|
87
109
|
default?(): any;
|
|
88
110
|
}>();
|
|
89
111
|
|
|
112
|
+
const isFiltered = computed(() => Object.entries(unref(modelValue)).filter(([, val]) => Boolean(val)).length > 0);
|
|
113
|
+
|
|
90
114
|
function reset(name: string): void {
|
|
91
115
|
emit('reset', name);
|
|
92
116
|
}
|
|
@@ -76,7 +76,17 @@
|
|
|
76
76
|
availableSize.value = direction === 'horizontal' ? bar.offsetWidth : bar.offsetHeight;
|
|
77
77
|
itemSizes.value = Array.from(measurer.children)
|
|
78
78
|
.filter(item => item instanceof HTMLElement)
|
|
79
|
-
.map(item =>
|
|
79
|
+
.map(item => {
|
|
80
|
+
const {display} = getComputedStyle(item);
|
|
81
|
+
|
|
82
|
+
if (display === 'contents') {
|
|
83
|
+
item = item.children[0] as HTMLElement;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return direction === 'horizontal'
|
|
87
|
+
? item.offsetWidth
|
|
88
|
+
: item.offsetHeight;
|
|
89
|
+
});
|
|
80
90
|
|
|
81
91
|
let size = 0;
|
|
82
92
|
let visible = 0;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<FluxBadge
|
|
3
|
+
v-if="valueLabel"
|
|
4
|
+
:class="$style.filterBadge"
|
|
5
|
+
:is-loading="isLoading"
|
|
6
|
+
:label="valueLabel"
|
|
7
|
+
@click="onClick"/>
|
|
8
|
+
</template>
|
|
9
|
+
|
|
10
|
+
<script
|
|
11
|
+
lang="ts"
|
|
12
|
+
setup>
|
|
13
|
+
import { useLoaded } from '@basmilius/common';
|
|
14
|
+
import type { FluxFilterItem, FluxFilterValue } from '@flux-ui/types';
|
|
15
|
+
import { computed, ref, unref, watch } from 'vue';
|
|
16
|
+
import { FluxBadge } from '$flux/component';
|
|
17
|
+
import $style from '$flux/css/component/Filter.module.scss';
|
|
18
|
+
|
|
19
|
+
const emit = defineEmits<{
|
|
20
|
+
click: [MouseEvent];
|
|
21
|
+
}>();
|
|
22
|
+
|
|
23
|
+
const {
|
|
24
|
+
item,
|
|
25
|
+
value
|
|
26
|
+
} = defineProps<{
|
|
27
|
+
readonly item: FluxFilterItem;
|
|
28
|
+
readonly value: FluxFilterValue;
|
|
29
|
+
}>();
|
|
30
|
+
|
|
31
|
+
const {isLoading, loaded} = useLoaded();
|
|
32
|
+
const getValueLabel = computed(() => loaded(item.getValueLabel));
|
|
33
|
+
|
|
34
|
+
const valueLabel = ref<string>();
|
|
35
|
+
|
|
36
|
+
function onClick(evt: MouseEvent): void {
|
|
37
|
+
emit('click', evt);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
watch([() => item, () => value], async () => {
|
|
41
|
+
valueLabel.value = await unref(getValueLabel)(value) ?? undefined;
|
|
42
|
+
}, {deep: true, immediate: true});
|
|
43
|
+
</script>
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { default as Anchor } from './Anchor.vue';
|
|
2
2
|
export { default as AnchorPopup } from './AnchorPopup.vue';
|
|
3
|
+
export { default as FilterBadge } from './FilterBadge.vue';
|
|
4
|
+
export { default as FilterItem } from './FilterItem.vue';
|
|
3
5
|
export { default as FilterOptionBase } from './FilterOptionBase.vue';
|
|
4
6
|
export { default as SelectBase } from './SelectBase.vue';
|
|
5
7
|
export { default as SliderBase } from './SliderBase.vue';
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
max-height: 50dvh;
|
|
3
3
|
max-width: 100%;
|
|
4
4
|
padding: 9px;
|
|
5
|
-
width:
|
|
5
|
+
width: 270px;
|
|
6
6
|
overflow: auto;
|
|
7
7
|
scrollbar-width: none;
|
|
8
8
|
transition: height 150ms var(--deceleration-curve);
|
|
@@ -40,6 +40,32 @@
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
.filterBadge {
|
|
44
|
+
padding-left: 6px;
|
|
45
|
+
padding-right: 6px;
|
|
46
|
+
background: var(--primary-50);
|
|
47
|
+
border: 1px dashed var(--primary-300);
|
|
48
|
+
font-weight: 500;
|
|
49
|
+
|
|
50
|
+
:local(.badgeLabel) {
|
|
51
|
+
color: var(--primary-900);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.filterButton {
|
|
56
|
+
border: 1px solid var(--surface-stroke);
|
|
57
|
+
box-shadow: none;
|
|
58
|
+
|
|
59
|
+
:local(.badge) {
|
|
60
|
+
border-radius: var(--radius-half);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
:local(.buttonIcon) {
|
|
64
|
+
color: var(--primary-700);
|
|
65
|
+
font-size: 16px;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
43
69
|
.filterHeader {
|
|
44
70
|
position: sticky;
|
|
45
71
|
margin: -9px -9px 3px;
|
|
@@ -70,13 +96,16 @@
|
|
|
70
96
|
|
|
71
97
|
.filterSearch {
|
|
72
98
|
position: sticky;
|
|
73
|
-
top: 52px;
|
|
74
99
|
margin: -9px -9px 0;
|
|
75
100
|
padding: 9px;
|
|
76
101
|
background: linear-gradient(to bottom, var(--surface) 75%, transparent);
|
|
77
102
|
z-index: 1;
|
|
78
103
|
}
|
|
79
104
|
|
|
105
|
+
.filterHeader + .menuGroup .filterSearch {
|
|
106
|
+
top: 52px;
|
|
107
|
+
}
|
|
108
|
+
|
|
80
109
|
.filterBar {
|
|
81
110
|
display: flex;
|
|
82
111
|
flex-flow: row nowrap;
|
|
@@ -1,31 +1,33 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
@layer flux-base {
|
|
2
|
+
.icon {
|
|
3
|
+
height: 1em;
|
|
4
|
+
width: 1em;
|
|
5
|
+
flex-shrink: 0;
|
|
6
|
+
font-size: 20px;
|
|
7
|
+
line-height: 1;
|
|
8
|
+
overflow: visible;
|
|
9
|
+
}
|
|
9
10
|
|
|
10
|
-
.fontAwesomeIcon {
|
|
11
|
-
|
|
11
|
+
.fontAwesomeIcon {
|
|
12
|
+
composes: icon;
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
}
|
|
14
|
+
display: inline-block;
|
|
15
|
+
}
|
|
15
16
|
|
|
16
|
-
.iconBoxed {
|
|
17
|
-
|
|
17
|
+
.iconBoxed {
|
|
18
|
+
composes: basePane from './base/Pane.module.scss';
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
display: flex;
|
|
21
|
+
height: 1em;
|
|
22
|
+
width: 1em;
|
|
23
|
+
align-items: center;
|
|
24
|
+
flex-shrink: 0;
|
|
25
|
+
justify-content: center;
|
|
26
|
+
font-size: 42px;
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
.icon {
|
|
29
|
+
font-size: .45em;
|
|
30
|
+
}
|
|
29
31
|
}
|
|
30
32
|
}
|
|
31
33
|
|
|
@@ -57,14 +57,18 @@
|
|
|
57
57
|
|
|
58
58
|
.timelineItemBody {
|
|
59
59
|
position: relative;
|
|
60
|
-
|
|
60
|
+
display: flex;
|
|
61
61
|
padding-top: 9px;
|
|
62
62
|
padding-bottom: 9px;
|
|
63
|
+
align-items: flex-start;
|
|
64
|
+
align-self: center;
|
|
65
|
+
flex-flow: column;
|
|
66
|
+
gap: 15px;
|
|
63
67
|
}
|
|
64
68
|
|
|
65
69
|
.timelineItemHeader {
|
|
66
70
|
display: flex;
|
|
67
|
-
margin-bottom: 9px;
|
|
71
|
+
margin-bottom: -9px;
|
|
68
72
|
flex-flow: column;
|
|
69
73
|
|
|
70
74
|
:is(strong) {
|
package/src/css/typography.scss
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
h1 {
|
|
9
9
|
color: var(--foreground-prominent);
|
|
10
|
-
font-size:
|
|
10
|
+
font-size: 27px;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
h2 {
|
|
@@ -35,8 +35,10 @@
|
|
|
35
35
|
a:not([class]), a[class=""] {
|
|
36
36
|
color: var(--primary-600);
|
|
37
37
|
text-decoration: underline;
|
|
38
|
+
text-decoration-color: var(--primary-400);
|
|
39
|
+
text-decoration-skip: auto;
|
|
38
40
|
text-decoration-thickness: 1px;
|
|
39
|
-
text-underline-offset:
|
|
41
|
+
text-underline-offset: 2px;
|
|
40
42
|
|
|
41
43
|
&:hover {
|
|
42
44
|
text-decoration: none;
|