@fastkit/vui 1.1.1 → 1.3.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fastkit/vui",
3
- "version": "1.1.1",
3
+ "version": "1.3.0",
4
4
  "description": "A simple, extensible UI kit for Vue applications.",
5
5
  "keywords": [
6
6
  "fastkit",
@@ -44,39 +44,39 @@
44
44
  "src"
45
45
  ],
46
46
  "dependencies": {
47
- "@fastkit/color-scheme": "^2.1.0",
48
- "@fastkit/helpers": "^0.15.0",
49
47
  "@fastkit/color-scheme-gen": "^1.1.0",
48
+ "@fastkit/color-scheme": "^2.1.0",
50
49
  "@fastkit/icon-font": "^2.1.0",
51
- "@fastkit/dom": "^0.3.0",
52
50
  "@fastkit/media-match": "^2.1.0",
51
+ "@fastkit/helpers": "^0.15.0",
52
+ "@fastkit/dom": "^0.3.0",
53
+ "@fastkit/rules": "^0.15.0",
54
+ "@fastkit/stylebase": "^0.13.1",
53
55
  "@fastkit/tiny-logger": "^0.15.0",
56
+ "@fastkit/vue-action": "^0.5.0",
54
57
  "@fastkit/media-match-gen": "^1.1.0",
55
- "@fastkit/stylebase": "^0.13.1",
56
- "@fastkit/vue-action": "^0.4.0",
57
- "@fastkit/rules": "^0.15.0",
58
- "@fastkit/vue-click-outside": "^0.3.0",
59
- "@fastkit/vue-disabled-reason": "^0.1.0",
60
- "@fastkit/vue-form-control": "^0.22.1",
61
- "@fastkit/vue-body-scroll-lock": "^0.3.0",
62
- "@fastkit/vue-keyboard": "^0.3.0",
63
- "@fastkit/vue-loading": "^0.16.0",
64
- "@fastkit/vue-app-layout": "^0.17.0",
65
- "@fastkit/vue-location": "^0.5.0",
66
- "@fastkit/vue-color-scheme": "^0.16.0",
67
- "@fastkit/vue-media-match": "^0.15.0",
68
- "@fastkit/vue-scroller": "^0.16.0",
69
- "@fastkit/vue-resize": "^0.3.0",
70
- "@fastkit/vue-stack": "^0.18.0",
71
- "@fastkit/vue-utils": "^0.16.0",
72
- "@fastkit/vue-transitions": "^0.3.0"
58
+ "@fastkit/vue-body-scroll-lock": "^0.4.0",
59
+ "@fastkit/vue-app-layout": "^0.18.0",
60
+ "@fastkit/vue-disabled-reason": "^0.2.0",
61
+ "@fastkit/vue-color-scheme": "^0.17.0",
62
+ "@fastkit/vue-form-control": "^0.24.0",
63
+ "@fastkit/vue-click-outside": "^0.4.0",
64
+ "@fastkit/vue-keyboard": "^0.4.0",
65
+ "@fastkit/vue-location": "^0.6.0",
66
+ "@fastkit/vue-loading": "^0.17.0",
67
+ "@fastkit/vue-resize": "^0.4.0",
68
+ "@fastkit/vue-media-match": "^0.16.0",
69
+ "@fastkit/vue-utils": "^0.17.0",
70
+ "@fastkit/vue-scroller": "^0.17.0",
71
+ "@fastkit/vue-transitions": "^0.4.0",
72
+ "@fastkit/vue-stack": "^0.19.2"
73
73
  },
74
74
  "devDependencies": {
75
- "vue": "^3.5.21",
75
+ "vue": "^3.5.22",
76
76
  "vue-router": "^4.5.1"
77
77
  },
78
78
  "peerDependencies": {
79
- "vue": "^3.4.0",
79
+ "vue": "^3.5.0",
80
80
  "vue-router": "^4.4.0"
81
81
  },
82
82
  "buildOptions": {
@@ -6,6 +6,7 @@
6
6
  width: 100%;
7
7
  min-width: 0;
8
8
  height: var(--control-field-height);
9
+ min-height: var(--control-field-height);
9
10
  padding: 0 8px;
10
11
  font-size: var(--control-field-font-size);
11
12
  line-height: var(--control-field-line-height);
@@ -64,7 +64,7 @@ export const VControlField = defineComponent({
64
64
  error: Boolean,
65
65
  },
66
66
  emits: {
67
- click: (ev: MouseEvent) => true,
67
+ click: (ev: PointerEvent) => true,
68
68
  },
69
69
  slots,
70
70
  setup(props, ctx) {
@@ -139,7 +139,7 @@ export const VControlField = defineComponent({
139
139
  );
140
140
  };
141
141
 
142
- const handleClick = (ev: MouseEvent) => ctx.emit('click', ev);
142
+ const handleClick = (ev: PointerEvent) => ctx.emit('click', ev);
143
143
 
144
144
  return () => (
145
145
  <div
@@ -493,7 +493,7 @@ export const VDataTable = defineComponent({
493
493
  }
494
494
  }
495
495
 
496
- function handleClickSortHeader(header: DataTableHeader, ev: MouseEvent) {
496
+ function handleClickSortHeader(header: DataTableHeader, ev: PointerEvent) {
497
497
  const { sortQuery } = header;
498
498
  if (!sortQuery) return;
499
499
  if (sortByRef.value === sortQuery) {
@@ -0,0 +1,42 @@
1
+ @use '../../styles/core';
2
+
3
+ @layer vui {
4
+ .v-file-input {
5
+ &__inner {
6
+ display: flex;
7
+ align-items: stretch;
8
+ }
9
+
10
+ &__button {
11
+ flex: 0 0 auto;
12
+ height: auto;
13
+ margin: 0;
14
+ margin-bottom: 1px;
15
+ border-top-right-radius: 0;
16
+ border-bottom-right-radius: 0;
17
+ }
18
+
19
+ &__selections {
20
+ display: flex;
21
+ flex-wrap: wrap;
22
+ align-items: center;
23
+ align-self: stretch;
24
+ padding: 4px 6px;
25
+ }
26
+
27
+ &__placeholder {
28
+ color: var(--control-disabled-color);
29
+ }
30
+
31
+ &__input {
32
+ flex: 1 1 100%;
33
+ cursor: text;
34
+ border-top-left-radius: 0;
35
+ border-bottom-left-radius: 0;
36
+
37
+ &__element {
38
+ @include core.hidden-input-element;
39
+ }
40
+ }
41
+ }
42
+ }
@@ -0,0 +1,178 @@
1
+ import './VFileInput.scss';
2
+ import { defineComponent, computed } from 'vue';
3
+ import {
4
+ createFileInputNodeSettings,
5
+ useFileInputNodeControl,
6
+ createFormNodeWrapperProps,
7
+ FormNodeWrapperSlots,
8
+ FileInputNodeEmits,
9
+ type FileInputNodeControl,
10
+ } from '@fastkit/vue-form-control';
11
+ import { defineSlots, ExtractPropInput, withCtx } from '@fastkit/vue-utils';
12
+ import { VFormControl } from '../VFormControl';
13
+ import {
14
+ VControlField,
15
+ createControlFieldProps,
16
+ InputBoxSlots,
17
+ } from '../VControlField';
18
+ import {
19
+ createControlProps,
20
+ useControl,
21
+ createControlFieldProviderProps,
22
+ useControlField,
23
+ } from '../../composables';
24
+ import { colorSchemeProps } from '@fastkit/vue-color-scheme';
25
+ import { VUI_FILE_INPUT_SYMBOL, useVui } from '../../injections';
26
+ import { VButton } from '../VButton';
27
+
28
+ const { props, emits } = createFileInputNodeSettings();
29
+
30
+ const slots = defineSlots<
31
+ FormNodeWrapperSlots &
32
+ InputBoxSlots & {
33
+ selections?: (control: FileInputNodeControl) => any;
34
+ }
35
+ >();
36
+
37
+ function createFileInputProps() {
38
+ return {
39
+ ...props,
40
+ ...colorSchemeProps(),
41
+ ...createFormNodeWrapperProps(),
42
+ ...createControlFieldProps(),
43
+ ...createControlFieldProviderProps(),
44
+ ...createControlProps(),
45
+ ...slots(),
46
+ buttonText: {
47
+ type: String,
48
+ default: 'Choose File',
49
+ },
50
+ truncateLength: {
51
+ type: [Number, String],
52
+ default: 22,
53
+ },
54
+ };
55
+ }
56
+
57
+ export type FileInputPropsOptions = ReturnType<typeof createFileInputProps>;
58
+
59
+ export type FileInputEmits = FileInputNodeEmits;
60
+
61
+ export type FileInputInput = ExtractPropInput<
62
+ ReturnType<typeof createFileInputProps>
63
+ >;
64
+
65
+ export const VFileInput = defineComponent({
66
+ name: 'VFileInput',
67
+ props: createFileInputProps(),
68
+ emits,
69
+ slots,
70
+ setup(props, ctx) {
71
+ const vui = useVui();
72
+ const inputControl = useFileInputNodeControl(props, ctx, {
73
+ nodeType: VUI_FILE_INPUT_SYMBOL,
74
+ });
75
+ const control = useControl(props);
76
+ useControlField(props);
77
+
78
+ const classes = computed(() => ['v-file-input', control.classes.value]);
79
+
80
+ const handleClickLabel = (ev: PointerEvent) => {
81
+ inputControl.focus();
82
+ };
83
+
84
+ const endAdornmentSlot = () => {
85
+ // }
86
+ if (props.clearable && inputControl.value.length) {
87
+ return (
88
+ <VButton
89
+ key="clear"
90
+ icon={vui.icon('clear')}
91
+ rounded
92
+ onClick={(ev) => {
93
+ ev.stopPropagation();
94
+ inputControl.clear();
95
+ }}
96
+ />
97
+ );
98
+ }
99
+ };
100
+
101
+ const showDialog = () => {
102
+ if (inputControl.canOperation) {
103
+ inputControl.inputElement?.click();
104
+ }
105
+ };
106
+
107
+ const renderSelections = () => {
108
+ if (ctx.slots.selections) {
109
+ return ctx.slots.selections(inputControl);
110
+ }
111
+
112
+ const { files } = inputControl.selectionContext;
113
+ if (!files.length)
114
+ return (
115
+ <span class="v-file-input__placeholder">
116
+ {inputControl.placeholder || 'No file chosen'}
117
+ </span>
118
+ );
119
+ return files.map((file) => file.truncatedName).join(', ');
120
+ };
121
+
122
+ const defaultSlot = () => (
123
+ <div class="v-file-input__inner">
124
+ <VButton
125
+ key="open"
126
+ class="v-file-input__button"
127
+ color={'base' as any}
128
+ size={props.size}
129
+ startIcon={vui.icon('fileUpload')}
130
+ variant={vui.setting('containedVariant')}
131
+ onClick={showDialog}>
132
+ {props.buttonText}
133
+ </VButton>
134
+ <VControlField
135
+ class="v-file-input__input"
136
+ startAdornment={props.startAdornment}
137
+ endAdornment={props.endAdornment}
138
+ size={control.size.value}
139
+ onClick={showDialog}
140
+ autoHeight
141
+ v-slots={{
142
+ ...ctx.slots,
143
+ default: withCtx(() => [
144
+ inputControl.createInputElement({
145
+ class: 'v-file-input__input__element',
146
+ }),
147
+ <div class="v-file-input__selections">{renderSelections()}</div>,
148
+ ]),
149
+ endAdornment: withCtx(endAdornmentSlot),
150
+ }}
151
+ />
152
+ </div>
153
+ );
154
+
155
+ ctx.expose({
156
+ control: inputControl,
157
+ });
158
+
159
+ return () => (
160
+ <VFormControl
161
+ nodeControl={inputControl}
162
+ // focused={inputControl.focused}
163
+ hiddenInfo={props.hiddenInfo}
164
+ class={classes.value}
165
+ label={props.label}
166
+ hint={props.hint}
167
+ hinttip={props.hinttip}
168
+ requiredChip={props.requiredChip}
169
+ onClickLabel={handleClickLabel}
170
+ v-slots={{
171
+ ...ctx.slots,
172
+ default: withCtx(defaultSlot),
173
+ // infoAppends: withCtx(infoAppendsSlot),
174
+ }}
175
+ />
176
+ );
177
+ },
178
+ });
@@ -0,0 +1 @@
1
+ export * from './VFileInput';
@@ -115,7 +115,8 @@ export const VFormControl = defineComponent({
115
115
  vui,
116
116
  );
117
117
 
118
- const handleClick = (ev: MouseEvent) => ctx.emit('clickLabel', ev, control);
118
+ const handleClick = (ev: PointerEvent) =>
119
+ ctx.emit('clickLabel', ev, control);
119
120
 
120
121
  return () => {
121
122
  const label = control.renderLabel();
@@ -77,7 +77,7 @@ export const VIcon = defineComponent({
77
77
  ...iconProps,
78
78
  },
79
79
  emits: {} as {
80
- click: (ev: MouseEvent) => true;
80
+ click: (ev: PointerEvent) => true;
81
81
  },
82
82
  setup(props, ctx) {
83
83
  const iconName = computed(() => props.name);
@@ -177,7 +177,7 @@ export const VNavigationItem = defineComponent({
177
177
  return opened.value ? close() : open();
178
178
  }
179
179
 
180
- const onClick = (ev: MouseEvent) => {
180
+ const onClick = (ev: PointerEvent) => {
181
181
  if (!to.value) {
182
182
  return toggle();
183
183
  }
@@ -267,7 +267,7 @@ export const VPagination = defineComponent({
267
267
  });
268
268
  })();
269
269
 
270
- const onClick = (ev: MouseEvent) => {
270
+ const onClick = (ev: PointerEvent) => {
271
271
  ev.preventDefault();
272
272
  if (page === undefined || active || disabled) return;
273
273
  setPage(page);
@@ -248,10 +248,10 @@ export const VSelect = defineComponent({
248
248
  );
249
249
 
250
250
  if (currentEl) {
251
- const ev = new MouseEvent('click', {
252
- view: window,
251
+ const ev = new PointerEvent('click', {
253
252
  bubbles: true,
254
253
  cancelable: true,
254
+ view: window,
255
255
  });
256
256
  currentEl.dispatchEvent(ev);
257
257
  ev.preventDefault();
@@ -283,7 +283,7 @@ export const VSelect = defineComponent({
283
283
  closeMenu,
284
284
  });
285
285
 
286
- const handleClickLabel = (ev: MouseEvent) => {
286
+ const handleClickLabel = (ev: PointerEvent) => {
287
287
  focus();
288
288
  };
289
289
 
@@ -28,7 +28,7 @@ export const VTab = defineComponent({
28
28
  icon: [String, Function] as PropType<RawIconProp>,
29
29
  },
30
30
  emits: {
31
- click: (ev: MouseEvent) => true,
31
+ click: (ev: PointerEvent) => true,
32
32
  },
33
33
  setup(props, ctx) {
34
34
  const classesRef = computed(() => ({
@@ -60,7 +60,7 @@ export const VTextField = defineComponent({
60
60
 
61
61
  const classes = computed(() => ['v-text-field', control.classes.value]);
62
62
 
63
- const handleClickLabel = (ev: MouseEvent) => {
63
+ const handleClickLabel = (ev: PointerEvent) => {
64
64
  inputControl.focus();
65
65
  };
66
66
 
@@ -35,6 +35,7 @@ export * from './VSelect';
35
35
  export * from './VTextField';
36
36
  export * from './VNumberField';
37
37
  export * from './VTextarea';
38
+ export * from './VFileInput';
38
39
  // export * from './VWysiwygEditor';
39
40
  export * from './VFormGroup';
40
41
  export * from './VForm';
package/src/injections.ts CHANGED
@@ -27,6 +27,7 @@ export const VUI_RADIO_GROUP_SYMBOL = 'vui-radio-group';
27
27
  export const VUI_RADIO_SYMBOL = 'vui-radio';
28
28
  export const VUI_SWITCH_GROUP_SYMBOL = 'vui-switch-group';
29
29
  export const VUI_SWITCH_SYMBOL = 'vui-switch';
30
+ export const VUI_FILE_INPUT_SYMBOL = 'vui-file-input';
30
31
  export const VUI_FORM_GROUP_SYMBOL = 'vui-form-group';
31
32
  export const VUI_FORM_SYMBOL = 'vui-form';
32
33
 
package/src/service.tsx CHANGED
@@ -132,6 +132,7 @@ export interface VuiServiceIconSettings {
132
132
  hinttip: IconName;
133
133
  clear: IconName;
134
134
  reload: IconName;
135
+ fileUpload: IconName;
135
136
  }
136
137
 
137
138
  export type RawVuiServiceIconSettings = Partial<VuiServiceIconSettings>;
@@ -439,7 +440,7 @@ export class VuiService {
439
440
  const action: VStackAction = {
440
441
  key,
441
442
  content: ({ control, key }) => {
442
- const onClick = (ev: MouseEvent) => _onClick(control, ev);
443
+ const onClick = (ev: PointerEvent) => _onClick(control, ev);
443
444
 
444
445
  const _attrs: any = {
445
446
  disabled: control.guardInProgress,