@teammodlr/ui-kit 3.0.0-beta.33 → 3.0.0-beta.36
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/lib/components/clearbutton/ClearButton.vue.d.ts +7 -0
- package/lib/components/clearbutton/style/ClearButton.css.d.ts +2 -0
- package/lib/components/clearbutton/style/index.d.ts +1 -0
- package/lib/components/hovercard/HoverCard.vue.d.ts +122 -0
- package/lib/components/hovercard/style/HoverCard.css.d.ts +3 -0
- package/lib/components/hovercard/style/index.d.ts +1 -0
- package/lib/components/index.d.ts +1 -0
- package/lib/components/input/Input.vue.d.ts +15 -0
- package/lib/components/input/style/Input.css.d.ts +4 -0
- package/lib/components/select/Select.vue.d.ts +12 -0
- package/lib/components/select/style/Select.css.d.ts +2 -1
- package/lib/components/textarea/Textarea.vue.d.ts +11 -0
- package/lib/components/textarea/style/Textarea.css.d.ts +2 -0
- package/lib/index.cjs.js +3 -3
- package/lib/index.es.js +4853 -4079
- package/lib/style.css +1 -1
- package/package.json +1 -1
- package/src/components/clearbutton/ClearButton.vue +39 -0
- package/src/components/clearbutton/style/ClearButton.css.ts +37 -0
- package/src/components/clearbutton/style/index.ts +1 -0
- package/src/components/hovercard/HoverCard.vue +163 -0
- package/src/components/hovercard/style/HoverCard.css.ts +175 -0
- package/src/components/hovercard/style/index.ts +1 -0
- package/src/components/index.ts +1 -1
- package/src/components/input/Input.vue +51 -3
- package/src/components/input/examples/clearable.vue +18 -0
- package/src/components/input/examples/index.stories.ts +3 -1
- package/src/components/input/examples/index.ts +1 -0
- package/src/components/input/style/Input.css.ts +20 -0
- package/src/components/select/Select.vue +31 -1
- package/src/components/select/examples/clearable.vue +18 -0
- package/src/components/select/examples/index.stories.ts +2 -1
- package/src/components/select/examples/index.ts +1 -0
- package/src/components/select/style/Select.css.ts +9 -6
- package/src/components/textarea/Textarea.vue +39 -3
- package/src/components/textarea/examples/clearable.vue +16 -0
- package/src/components/textarea/examples/index.stories.ts +2 -1
- package/src/components/textarea/examples/index.ts +1 -0
- package/src/components/textarea/style/Textarea.css.ts +11 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<HoverCardRoot
|
|
3
|
+
v-model:open="isOpen"
|
|
4
|
+
:open-delay="openDelay"
|
|
5
|
+
:close-delay="closeDelay"
|
|
6
|
+
>
|
|
7
|
+
<HoverCardTrigger as-child>
|
|
8
|
+
<slot />
|
|
9
|
+
</HoverCardTrigger>
|
|
10
|
+
<HoverCardPortal :disabled="!transfer">
|
|
11
|
+
<HoverCardContent
|
|
12
|
+
:class="contentClasses"
|
|
13
|
+
:side="resolvedSide"
|
|
14
|
+
:align="resolvedAlign"
|
|
15
|
+
:side-offset="sideOffset"
|
|
16
|
+
:collision-padding="collisionPadding"
|
|
17
|
+
:style="{ zIndex: zIndex }"
|
|
18
|
+
>
|
|
19
|
+
<slot name="content" />
|
|
20
|
+
<HoverCardArrow v-if="showArrow" :class="style.arrow" :width="12" :height="6" />
|
|
21
|
+
</HoverCardContent>
|
|
22
|
+
</HoverCardPortal>
|
|
23
|
+
</HoverCardRoot>
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<script lang="ts">
|
|
27
|
+
import {
|
|
28
|
+
HoverCardArrow,
|
|
29
|
+
HoverCardContent,
|
|
30
|
+
HoverCardPortal,
|
|
31
|
+
HoverCardRoot,
|
|
32
|
+
HoverCardTrigger,
|
|
33
|
+
type HoverCardContentProps
|
|
34
|
+
} from 'reka-ui';
|
|
35
|
+
import {
|
|
36
|
+
computed,
|
|
37
|
+
defineComponent,
|
|
38
|
+
ref,
|
|
39
|
+
useAttrs,
|
|
40
|
+
watch,
|
|
41
|
+
type PropType
|
|
42
|
+
} from 'vue';
|
|
43
|
+
import {
|
|
44
|
+
type Placement,
|
|
45
|
+
placementWhileList
|
|
46
|
+
} from '../../composables/usePopper';
|
|
47
|
+
import { useZIndex } from '../../composables/useZIndex';
|
|
48
|
+
import * as style from './style';
|
|
49
|
+
|
|
50
|
+
export default defineComponent({
|
|
51
|
+
name: 'MHoverCard',
|
|
52
|
+
inheritAttrs: false,
|
|
53
|
+
components: {
|
|
54
|
+
HoverCardArrow,
|
|
55
|
+
HoverCardContent,
|
|
56
|
+
HoverCardPortal,
|
|
57
|
+
HoverCardRoot,
|
|
58
|
+
HoverCardTrigger
|
|
59
|
+
},
|
|
60
|
+
props: {
|
|
61
|
+
open: {
|
|
62
|
+
type: Boolean,
|
|
63
|
+
default: false
|
|
64
|
+
},
|
|
65
|
+
placement: {
|
|
66
|
+
type: String as PropType<Placement>,
|
|
67
|
+
default: 'bottom',
|
|
68
|
+
validator: (value: Placement) => placementWhileList.includes(value)
|
|
69
|
+
},
|
|
70
|
+
transfer: {
|
|
71
|
+
type: Boolean,
|
|
72
|
+
default: true
|
|
73
|
+
},
|
|
74
|
+
sideOffset: {
|
|
75
|
+
type: Number,
|
|
76
|
+
default: 5
|
|
77
|
+
},
|
|
78
|
+
collisionPadding: {
|
|
79
|
+
type: [Number, Object] as PropType<number | { top?: number; right?: number; bottom?: number; left?: number }>,
|
|
80
|
+
default: 10
|
|
81
|
+
},
|
|
82
|
+
arrow: {
|
|
83
|
+
type: Boolean,
|
|
84
|
+
default: false
|
|
85
|
+
},
|
|
86
|
+
variant: {
|
|
87
|
+
type: String as PropType<'light' | 'dark'>,
|
|
88
|
+
default: 'light'
|
|
89
|
+
},
|
|
90
|
+
openDelay: {
|
|
91
|
+
type: Number,
|
|
92
|
+
default: 700
|
|
93
|
+
},
|
|
94
|
+
closeDelay: {
|
|
95
|
+
type: Number,
|
|
96
|
+
default: 300
|
|
97
|
+
},
|
|
98
|
+
zIndex: {
|
|
99
|
+
type: Number
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
emits: ['update:open'],
|
|
103
|
+
setup(props, { emit }) {
|
|
104
|
+
const attrs = useAttrs();
|
|
105
|
+
const { nextZIndex } = useZIndex();
|
|
106
|
+
const isOpen = ref(props.open);
|
|
107
|
+
const zIndex = ref(props.zIndex ?? nextZIndex());
|
|
108
|
+
|
|
109
|
+
const resolvedPlacement = computed(() => {
|
|
110
|
+
const [side, align] = props.placement.split('-');
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
side: side === 'top' || side === 'right' || side === 'bottom' || side === 'left'
|
|
114
|
+
? side
|
|
115
|
+
: undefined,
|
|
116
|
+
align: align === 'start' || align === 'center' || align === 'end'
|
|
117
|
+
? align
|
|
118
|
+
: undefined
|
|
119
|
+
} as {
|
|
120
|
+
side?: HoverCardContentProps['side'];
|
|
121
|
+
align?: HoverCardContentProps['align'];
|
|
122
|
+
};
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const showArrow = computed(() => {
|
|
126
|
+
const { align } = resolvedPlacement.value;
|
|
127
|
+
return props.arrow && (!align || align === 'center');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const contentClasses = computed(() => {
|
|
131
|
+
return [
|
|
132
|
+
'm-hover-card-content',
|
|
133
|
+
style.content,
|
|
134
|
+
{
|
|
135
|
+
[style.dark]: props.variant === 'dark'
|
|
136
|
+
},
|
|
137
|
+
attrs.class
|
|
138
|
+
];
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
watch(
|
|
142
|
+
() => props.open,
|
|
143
|
+
value => {
|
|
144
|
+
isOpen.value = value;
|
|
145
|
+
}
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
watch(isOpen, value => {
|
|
149
|
+
emit('update:open', value);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
isOpen,
|
|
154
|
+
zIndex,
|
|
155
|
+
showArrow,
|
|
156
|
+
contentClasses,
|
|
157
|
+
resolvedSide: computed(() => resolvedPlacement.value.side),
|
|
158
|
+
resolvedAlign: computed(() => resolvedPlacement.value.align),
|
|
159
|
+
style
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
</script>
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { globalKeyframes, globalStyle, style } from '@vanilla-extract/css';
|
|
2
|
+
import { themeVars } from '../../../styles/theme.css';
|
|
3
|
+
|
|
4
|
+
const slideFadeOutTopAnimation = 'hover-card-slide-fade-out-top';
|
|
5
|
+
globalKeyframes(slideFadeOutTopAnimation, {
|
|
6
|
+
from: {
|
|
7
|
+
opacity: 1,
|
|
8
|
+
transform: 'translateY(0) scale(1)'
|
|
9
|
+
},
|
|
10
|
+
to: {
|
|
11
|
+
opacity: 0,
|
|
12
|
+
transform: 'translateY(6px) scale(0.98)'
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const slideFadeOutBottomAnimation = 'hover-card-slide-fade-out-bottom';
|
|
17
|
+
globalKeyframes(slideFadeOutBottomAnimation, {
|
|
18
|
+
from: {
|
|
19
|
+
opacity: 1,
|
|
20
|
+
transform: 'translateY(0) scale(1)'
|
|
21
|
+
},
|
|
22
|
+
to: {
|
|
23
|
+
opacity: 0,
|
|
24
|
+
transform: 'translateY(-6px) scale(0.98)'
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const slideFadeOutLeftAnimation = 'hover-card-slide-fade-out-left';
|
|
29
|
+
globalKeyframes(slideFadeOutLeftAnimation, {
|
|
30
|
+
from: {
|
|
31
|
+
opacity: 1,
|
|
32
|
+
transform: 'translateX(0) scale(1)'
|
|
33
|
+
},
|
|
34
|
+
to: {
|
|
35
|
+
opacity: 0,
|
|
36
|
+
transform: 'translateX(6px) scale(0.98)'
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const slideFadeOutRightAnimation = 'hover-card-slide-fade-out-right';
|
|
41
|
+
globalKeyframes(slideFadeOutRightAnimation, {
|
|
42
|
+
from: {
|
|
43
|
+
opacity: 1,
|
|
44
|
+
transform: 'translateX(0) scale(1)'
|
|
45
|
+
},
|
|
46
|
+
to: {
|
|
47
|
+
opacity: 0,
|
|
48
|
+
transform: 'translateX(-6px) scale(0.98)'
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const slideFadeTopAnimation = 'hover-card-slide-fade-top';
|
|
53
|
+
globalKeyframes(slideFadeTopAnimation, {
|
|
54
|
+
from: {
|
|
55
|
+
opacity: 0,
|
|
56
|
+
transform: 'translateY(6px) scale(0.98)'
|
|
57
|
+
},
|
|
58
|
+
to: {
|
|
59
|
+
opacity: 1,
|
|
60
|
+
transform: 'translateY(0) scale(1)'
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const slideFadeBottomAnimation = 'hover-card-slide-fade-bottom';
|
|
65
|
+
globalKeyframes(slideFadeBottomAnimation, {
|
|
66
|
+
from: {
|
|
67
|
+
opacity: 0,
|
|
68
|
+
transform: 'translateY(-6px) scale(0.98)'
|
|
69
|
+
},
|
|
70
|
+
to: {
|
|
71
|
+
opacity: 1,
|
|
72
|
+
transform: 'translateY(0) scale(1)'
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const slideFadeLeftAnimation = 'hover-card-slide-fade-left';
|
|
77
|
+
globalKeyframes(slideFadeLeftAnimation, {
|
|
78
|
+
from: {
|
|
79
|
+
opacity: 0,
|
|
80
|
+
transform: 'translateX(6px) scale(0.98)'
|
|
81
|
+
},
|
|
82
|
+
to: {
|
|
83
|
+
opacity: 1,
|
|
84
|
+
transform: 'translateX(0) scale(1)'
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const slideFadeRightAnimation = 'hover-card-slide-fade-right';
|
|
89
|
+
globalKeyframes(slideFadeRightAnimation, {
|
|
90
|
+
from: {
|
|
91
|
+
opacity: 0,
|
|
92
|
+
transform: 'translateX(-6px) scale(0.98)'
|
|
93
|
+
},
|
|
94
|
+
to: {
|
|
95
|
+
opacity: 1,
|
|
96
|
+
transform: 'translateX(0) scale(1)'
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
export const content = style({
|
|
101
|
+
backgroundColor: '#ffffff',
|
|
102
|
+
borderRadius: '5px',
|
|
103
|
+
padding: '5px',
|
|
104
|
+
zIndex: 4000,
|
|
105
|
+
boxSizing: 'border-box',
|
|
106
|
+
position: 'relative',
|
|
107
|
+
minWidth: '130px',
|
|
108
|
+
maxWidth: '350px',
|
|
109
|
+
border: `1px solid ${themeVars.color.gray[300]}`,
|
|
110
|
+
boxShadow: themeVars.shadow.md,
|
|
111
|
+
transformOrigin: 'var(--reka-hover-card-content-transform-origin)',
|
|
112
|
+
willChange: 'transform, opacity',
|
|
113
|
+
outline: 'none',
|
|
114
|
+
selectors: {
|
|
115
|
+
'&[data-state="open"]': {
|
|
116
|
+
animationDuration: '0.16s',
|
|
117
|
+
animationTimingFunction: 'cubic-bezier(0.16, 1, 0.3, 1)',
|
|
118
|
+
animationFillMode: 'both'
|
|
119
|
+
},
|
|
120
|
+
'&[data-state="closed"]': {
|
|
121
|
+
animationDuration: '0.12s',
|
|
122
|
+
animationTimingFunction: 'ease-out',
|
|
123
|
+
animationFillMode: 'both'
|
|
124
|
+
},
|
|
125
|
+
'&[data-state="closed"][data-side="top"]': {
|
|
126
|
+
animationName: slideFadeOutTopAnimation
|
|
127
|
+
},
|
|
128
|
+
'&[data-state="closed"][data-side="bottom"]': {
|
|
129
|
+
animationName: slideFadeOutBottomAnimation
|
|
130
|
+
},
|
|
131
|
+
'&[data-state="closed"][data-side="left"]': {
|
|
132
|
+
animationName: slideFadeOutLeftAnimation
|
|
133
|
+
},
|
|
134
|
+
'&[data-state="closed"][data-side="right"]': {
|
|
135
|
+
animationName: slideFadeOutRightAnimation
|
|
136
|
+
},
|
|
137
|
+
'&[data-state="open"][data-side="top"]': {
|
|
138
|
+
animationName: slideFadeTopAnimation
|
|
139
|
+
},
|
|
140
|
+
'&[data-state="open"][data-side="bottom"]': {
|
|
141
|
+
animationName: slideFadeBottomAnimation
|
|
142
|
+
},
|
|
143
|
+
'&[data-state="open"][data-side="left"]': {
|
|
144
|
+
animationName: slideFadeLeftAnimation
|
|
145
|
+
},
|
|
146
|
+
'&[data-state="open"][data-side="right"]': {
|
|
147
|
+
animationName: slideFadeRightAnimation
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
export const arrow = style({
|
|
153
|
+
fill: '#ffffff'
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
globalStyle(`${arrow} path`, {
|
|
157
|
+
fill: '#ffffff',
|
|
158
|
+
stroke: themeVars.color.gray[300],
|
|
159
|
+
strokeWidth: '1px'
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
export const dark = style({
|
|
163
|
+
backgroundColor: themeVars.color.gray[900],
|
|
164
|
+
border: `1px solid ${themeVars.color.gray[800]}`,
|
|
165
|
+
color: themeVars.color.gray[100]
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
globalStyle(`${dark} ${arrow}`, {
|
|
169
|
+
fill: themeVars.color.gray[900]
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
globalStyle(`${dark} ${arrow} path`, {
|
|
173
|
+
fill: themeVars.color.gray[900],
|
|
174
|
+
stroke: themeVars.color.gray[800]
|
|
175
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './HoverCard.css';
|
package/src/components/index.ts
CHANGED
|
@@ -59,4 +59,4 @@ export { default as MDialogProvider } from './dialogprovider/DialogProvider.vue'
|
|
|
59
59
|
export { default as MDrawer } from './drawer/Drawer.vue';
|
|
60
60
|
export { default as MTree } from './tree/Tree.vue';
|
|
61
61
|
export { default as MPopover } from './popover/Popover.vue';
|
|
62
|
-
|
|
62
|
+
export { default as MHoverCard } from './hovercard/HoverCard.vue';
|
|
@@ -20,9 +20,10 @@
|
|
|
20
20
|
:type="inputType"
|
|
21
21
|
:placeholder="placeholder"
|
|
22
22
|
:name="name"
|
|
23
|
-
:value="
|
|
23
|
+
:value="currentValue"
|
|
24
24
|
:readonly="readonly"
|
|
25
25
|
v-on="inputListeners"
|
|
26
|
+
@input="onNativeInput"
|
|
26
27
|
:class="inputComputedClasses"
|
|
27
28
|
v-bind="$attrs"
|
|
28
29
|
:disabled="disabled || loading"
|
|
@@ -32,6 +33,7 @@
|
|
|
32
33
|
<div v-if="hasSuffix" :class="style.suffix">
|
|
33
34
|
<slot name="suffix" />
|
|
34
35
|
</div>
|
|
36
|
+
<MClearButton v-if="showClear" :class="clearClasses" @clear="onClear" />
|
|
35
37
|
<div v-if="hasRightIcon" :class="style.rightIcon">
|
|
36
38
|
<slot name="icon-right" />
|
|
37
39
|
</div>
|
|
@@ -52,14 +54,17 @@ import {
|
|
|
52
54
|
defineComponent,
|
|
53
55
|
ref,
|
|
54
56
|
computed,
|
|
55
|
-
toRef
|
|
57
|
+
toRef,
|
|
58
|
+
watch
|
|
56
59
|
} from "vue";
|
|
57
60
|
import { useInput } from "../../composables/useInput";
|
|
58
61
|
import { useInteractive } from "../../composables/useInteractive";
|
|
62
|
+
import MClearButton from '../clearbutton/ClearButton.vue';
|
|
59
63
|
import * as style from './style';
|
|
60
64
|
|
|
61
65
|
export default defineComponent({
|
|
62
66
|
name: 'MInput',
|
|
67
|
+
components: { MClearButton },
|
|
63
68
|
props: {
|
|
64
69
|
...useInput.props(),
|
|
65
70
|
...useInteractive.props(),
|
|
@@ -103,8 +108,12 @@ export default defineComponent({
|
|
|
103
108
|
type: String,
|
|
104
109
|
default: ''
|
|
105
110
|
},
|
|
111
|
+
clearable: {
|
|
112
|
+
type: Boolean,
|
|
113
|
+
default: false
|
|
114
|
+
},
|
|
106
115
|
},
|
|
107
|
-
emits: useInput.emits(),
|
|
116
|
+
emits: [...useInput.emits(), 'clear'],
|
|
108
117
|
setup(props, { slots, emit, expose }) {
|
|
109
118
|
const element = ref<HTMLInputElement|null>(null);
|
|
110
119
|
const { focus, blur } = useInteractive(element);
|
|
@@ -170,6 +179,8 @@ export default defineComponent({
|
|
|
170
179
|
[style.disabled]: disabled.value || loading.value,
|
|
171
180
|
[style.hasLeftIcon]: hasLeftIcon.value,
|
|
172
181
|
[style.hasRightIcon]: hasRightIcon.value,
|
|
182
|
+
[style.hasClear]: props.clearable,
|
|
183
|
+
[style.hasClearAndRightIcon]: props.clearable && hasRightIcon.value,
|
|
173
184
|
}
|
|
174
185
|
];
|
|
175
186
|
|
|
@@ -193,8 +204,41 @@ export default defineComponent({
|
|
|
193
204
|
return formError.value || props.error;
|
|
194
205
|
});
|
|
195
206
|
|
|
207
|
+
// Tracks typed text so clearable works without v-model
|
|
208
|
+
const internalValue = ref<any>(props.modelValue ?? '');
|
|
209
|
+
watch(() => props.modelValue, value => {
|
|
210
|
+
internalValue.value = value ?? '';
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
const currentValue = computed(() => {
|
|
214
|
+
const value = props.modelValue !== undefined ? props.modelValue : internalValue.value;
|
|
215
|
+
return value || '';
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
const onNativeInput = (event: Event) => {
|
|
219
|
+
internalValue.value = (event.target as HTMLInputElement).value;
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const showClear = computed(() => {
|
|
223
|
+
return props.clearable
|
|
224
|
+
&& !disabled.value
|
|
225
|
+
&& !loading.value
|
|
226
|
+
&& !props.readonly
|
|
227
|
+
&& currentValue.value !== '';
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
const clearClasses = computed(() => {
|
|
231
|
+
return [
|
|
232
|
+
style.clear,
|
|
233
|
+
{ [style.clearWithRightIcon]: hasRightIcon.value }
|
|
234
|
+
];
|
|
235
|
+
});
|
|
236
|
+
|
|
196
237
|
const onClear = () => {
|
|
238
|
+
internalValue.value = '';
|
|
197
239
|
emit('update:modelValue', '');
|
|
240
|
+
emit('clear');
|
|
241
|
+
focus();
|
|
198
242
|
};
|
|
199
243
|
|
|
200
244
|
expose({
|
|
@@ -220,6 +264,10 @@ export default defineComponent({
|
|
|
220
264
|
hasHelpMessage,
|
|
221
265
|
inputError,
|
|
222
266
|
style,
|
|
267
|
+
showClear,
|
|
268
|
+
clearClasses,
|
|
269
|
+
currentValue,
|
|
270
|
+
onNativeInput,
|
|
223
271
|
|
|
224
272
|
onClear,
|
|
225
273
|
validate,
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div style="display: flex; flex-direction: column; gap: 12px; max-width: 320px;">
|
|
3
|
+
<MInput v-model="value" placeholder="Type something..." clearable />
|
|
4
|
+
<MInput v-model="value" placeholder="With a right icon" clearable>
|
|
5
|
+
<template #icon-right>
|
|
6
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" style="width: 20px; height: 20px;">
|
|
7
|
+
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z" clip-rule="evenodd" />
|
|
8
|
+
</svg>
|
|
9
|
+
</template>
|
|
10
|
+
</MInput>
|
|
11
|
+
</div>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<script lang="ts" setup>
|
|
15
|
+
import { ref } from 'vue';
|
|
16
|
+
|
|
17
|
+
const value = ref('Clear me');
|
|
18
|
+
</script>
|
|
@@ -5,7 +5,8 @@ import {
|
|
|
5
5
|
MInputPrefix,
|
|
6
6
|
MInputLeftIcon,
|
|
7
7
|
MInputRightIcon,
|
|
8
|
-
MInputSizes
|
|
8
|
+
MInputSizes,
|
|
9
|
+
MInputClearable
|
|
9
10
|
} from './index';
|
|
10
11
|
|
|
11
12
|
import { createStory, sizeArgType } from '../../../__storybook__';
|
|
@@ -24,3 +25,4 @@ export const Prefix = createStory(MInputPrefix);
|
|
|
24
25
|
export const Sizes = createStory(MInputSizes);
|
|
25
26
|
export const LeftIcon = createStory(MInputLeftIcon);
|
|
26
27
|
export const RightIcon = createStory(MInputRightIcon);
|
|
28
|
+
export const Clearable = createStory(MInputClearable);
|
|
@@ -4,3 +4,4 @@ export { default as MInputPrefix } from './prefix.vue';
|
|
|
4
4
|
export { default as MInputSizes } from './sizes.vue';
|
|
5
5
|
export { default as MInputLeftIcon } from './left-icon.vue';
|
|
6
6
|
export { default as MInputRightIcon } from './right-icon.vue';
|
|
7
|
+
export { default as MInputClearable } from './clearable.vue';
|
|
@@ -251,6 +251,26 @@ export const hasRightIcon = style({
|
|
|
251
251
|
paddingRight: '2.25rem'
|
|
252
252
|
});
|
|
253
253
|
|
|
254
|
+
export const hasClear = style({
|
|
255
|
+
paddingRight: '1.75rem'
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
export const hasClearAndRightIcon = style({
|
|
259
|
+
paddingRight: '4rem'
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
export const clear = style({
|
|
263
|
+
position: 'absolute',
|
|
264
|
+
zIndex: 2,
|
|
265
|
+
top: '50%',
|
|
266
|
+
right: themeVars.space[1],
|
|
267
|
+
transform: 'translateY(-50%)'
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
export const clearWithRightIcon = style({
|
|
271
|
+
right: '2.25rem'
|
|
272
|
+
});
|
|
273
|
+
|
|
254
274
|
export const prefix = style({
|
|
255
275
|
pointerEvents: 'none',
|
|
256
276
|
marginRight: themeVars.space[3],
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down-icon lucide-chevron-down"><path d="m6 9 6 6 6-6"/></svg>
|
|
36
36
|
</span>
|
|
37
37
|
</div>
|
|
38
|
+
<MClearButton v-if="showClear" :class="style.clear" @clear="clearSelected" />
|
|
38
39
|
</div>
|
|
39
40
|
</PopoverTrigger>
|
|
40
41
|
|
|
@@ -121,6 +122,7 @@ import {
|
|
|
121
122
|
type PropType
|
|
122
123
|
} from 'vue';
|
|
123
124
|
import MTag from '../tag/Tag.vue';
|
|
125
|
+
import MClearButton from '../clearbutton/ClearButton.vue';
|
|
124
126
|
import { VList } from 'virtua/vue';
|
|
125
127
|
import * as style from './style';
|
|
126
128
|
|
|
@@ -140,7 +142,8 @@ export default defineComponent({
|
|
|
140
142
|
emits: {
|
|
141
143
|
...useInput.emits(),
|
|
142
144
|
'on-open': () => true,
|
|
143
|
-
'on-close': () => true
|
|
145
|
+
'on-close': () => true,
|
|
146
|
+
clear: () => true
|
|
144
147
|
},
|
|
145
148
|
props: {
|
|
146
149
|
...useInput.props(),
|
|
@@ -226,6 +229,10 @@ export default defineComponent({
|
|
|
226
229
|
zIndex: {
|
|
227
230
|
type: Number
|
|
228
231
|
},
|
|
232
|
+
clearable: {
|
|
233
|
+
type: Boolean,
|
|
234
|
+
default: false
|
|
235
|
+
},
|
|
229
236
|
showDataOptions: {
|
|
230
237
|
type: Boolean,
|
|
231
238
|
default: true
|
|
@@ -235,6 +242,7 @@ export default defineComponent({
|
|
|
235
242
|
},
|
|
236
243
|
components: {
|
|
237
244
|
MTag,
|
|
245
|
+
MClearButton,
|
|
238
246
|
VList,
|
|
239
247
|
PopoverContent,
|
|
240
248
|
PopoverPortal,
|
|
@@ -615,6 +623,25 @@ export default defineComponent({
|
|
|
615
623
|
}
|
|
616
624
|
}
|
|
617
625
|
|
|
626
|
+
const hasValue = computed(() => {
|
|
627
|
+
return props.multiple
|
|
628
|
+
? Array.isArray(selected.value) && selected.value.length > 0
|
|
629
|
+
: !!selected.value;
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
const showClear = computed(() => {
|
|
633
|
+
return props.clearable
|
|
634
|
+
&& hasValue.value
|
|
635
|
+
&& !disabled.value
|
|
636
|
+
&& !loading.value;
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
function clearSelected() {
|
|
640
|
+
selected.value = props.multiple ? [] : '';
|
|
641
|
+
emit('clear');
|
|
642
|
+
container.value?.focus();
|
|
643
|
+
}
|
|
644
|
+
|
|
618
645
|
function removeSelected(value: string | number) {
|
|
619
646
|
if (!props.multiple) {
|
|
620
647
|
return;
|
|
@@ -783,6 +810,7 @@ export default defineComponent({
|
|
|
783
810
|
style.base,
|
|
784
811
|
style.sizes[props.size],
|
|
785
812
|
{
|
|
813
|
+
[style.hasClear]: showClear.value,
|
|
786
814
|
[style.selectedTags]:
|
|
787
815
|
props.multiple &&
|
|
788
816
|
Array.isArray(selected.value) &&
|
|
@@ -954,6 +982,8 @@ export default defineComponent({
|
|
|
954
982
|
getSelectedLabel,
|
|
955
983
|
openSelect,
|
|
956
984
|
removeSelected,
|
|
985
|
+
showClear,
|
|
986
|
+
clearSelected,
|
|
957
987
|
inputError,
|
|
958
988
|
transitionName,
|
|
959
989
|
inputListeners,
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div style="display: flex; flex-direction: column; gap: 12px; max-width: 320px;">
|
|
3
|
+
<MSelect v-model="single" :options="options" clearable />
|
|
4
|
+
<MSelect v-model="multiple" :options="options" multiple clearable />
|
|
5
|
+
</div>
|
|
6
|
+
</template>
|
|
7
|
+
|
|
8
|
+
<script lang="ts" setup>
|
|
9
|
+
import { ref } from 'vue';
|
|
10
|
+
|
|
11
|
+
const options = Array.from({ length: 10 }, (_, i) => ({
|
|
12
|
+
value: i + 1,
|
|
13
|
+
label: `Option ${i + 1}`
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
const single = ref(2);
|
|
17
|
+
const multiple = ref([1, 2]);
|
|
18
|
+
</script>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import MSelect from '../Select.vue';
|
|
2
|
-
import { MSelectBasic, MSelectSearchable, MSelectMultiple, MSelectFill } from './index';
|
|
2
|
+
import { MSelectBasic, MSelectSearchable, MSelectMultiple, MSelectFill, MSelectClearable } from './index';
|
|
3
3
|
|
|
4
4
|
import { createStory, sizeArgType } from '../../../__storybook__';
|
|
5
5
|
|
|
@@ -15,3 +15,4 @@ export const Basic = createStory(MSelectBasic);
|
|
|
15
15
|
export const Searchable = createStory(MSelectSearchable);
|
|
16
16
|
export const Multiple = createStory(MSelectMultiple);
|
|
17
17
|
export const Fill = createStory(MSelectFill);
|
|
18
|
+
export const Clearable = createStory(MSelectClearable);
|
|
@@ -2,3 +2,4 @@ export { default as MSelectBasic } from './basic.vue';
|
|
|
2
2
|
export { default as MSelectSearchable } from './searchable.vue';
|
|
3
3
|
export { default as MSelectMultiple } from './multiple.vue';
|
|
4
4
|
export { default as MSelectFill } from './fill.vue';
|
|
5
|
+
export { default as MSelectClearable } from './clearable.vue';
|
|
@@ -379,13 +379,16 @@ export const noResults = style({
|
|
|
379
379
|
fontSize: themeVars.fontSize.small
|
|
380
380
|
});
|
|
381
381
|
|
|
382
|
-
export const
|
|
383
|
-
width: themeVars.space[4],
|
|
384
|
-
height: themeVars.space[4],
|
|
382
|
+
export const clear = style({
|
|
385
383
|
position: 'absolute',
|
|
386
|
-
|
|
387
|
-
top: '
|
|
388
|
-
|
|
384
|
+
zIndex: 1,
|
|
385
|
+
top: '50%',
|
|
386
|
+
right: '2.25rem',
|
|
387
|
+
transform: 'translateY(-50%)'
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
export const hasClear = style({
|
|
391
|
+
paddingRight: '3.75rem'
|
|
389
392
|
});
|
|
390
393
|
|
|
391
394
|
export const arrow = style({
|