@vuetkit/components 0.0.8 → 0.0.10

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/README.md CHANGED
@@ -1,12 +1,20 @@
1
- # Components
1
+ # Composable Components
2
2
 
3
3
  ## Description
4
4
 
5
- Collection of business development components for Vue3 + ElementPlus projects.
5
+ Collection of business development Composable Components for Vue3 + ElementPlus projects.
6
6
 
7
- ## All Components / Component Hooks
7
+ ## All Composable Components
8
8
 
9
9
  ### Feedback
10
10
 
11
11
  - [useMessage](/components/src/feedback/useMessage/)
12
12
  - [useAsyncConfirm](/components/src/feedback/useAsyncConfirm/)
13
+
14
+ ### Form
15
+
16
+ - [useForm](/components/src/form/useForm/)
17
+
18
+ ### Table
19
+
20
+ - [useTable](/components/src/table/useTable/)
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { RequestService } from "@vuetkit/core";
2
- import { MessageProps, MessageType } from "element-plus";
2
+ import { ComponentSize, FormRules, MessageProps, MessageType, TableColumnCtx, TableProps } from "element-plus";
3
+ import { Component, VNode } from "vue";
3
4
 
4
5
  //#region src/feedback/useAsyncConfirm/index.d.ts
5
6
  interface AsyncConfirmOptions {
@@ -27,4 +28,88 @@ declare function useMessage(options?: MessageProps): {
27
28
  closeAll: () => void;
28
29
  };
29
30
  //#endregion
30
- export { AsyncConfirmOptions, useAsyncConfirm, useMessage };
31
+ //#region src/form/useForm/index.d.ts
32
+ type CustomRender = (...args: unknown[]) => unknown;
33
+ type Recordable = Record<string, unknown>;
34
+ type DeepKeys<T> = T extends null | undefined ? never : T extends object ? { [K in keyof T]: K extends string ? T[K] extends null | undefined ? `${K}` : T[K] extends object ? `${K}` | `${K}.${DeepKeys<T[K]>}` : `${K}` : never }[keyof T] : never;
35
+ type DefaultComponentKey = 'input' | 'select' | 'date-picker' | 'time-picker' | 'checkbox' | 'radio' | 'upload' | 'input-number' | 'switch' | 'rate' | 'tree-select' | 'cascader' | 'color-picker' | 'input-otp' | 'mention' | 'select-v2' | 'slider' | 'time-select' | 'transfer';
36
+ type FormItemType = DefaultComponentKey | string | CustomRender;
37
+ interface FormSchema<T> {
38
+ label: string;
39
+ prop: DeepKeys<T>;
40
+ type: FormItemType;
41
+ span?: number;
42
+ componentProps?: Recordable;
43
+ }
44
+ type FormRuleFn<T> = (data: T) => FormRules<Recordable>;
45
+ interface FormOptions<T> {
46
+ 'schemas': FormSchema<T>[];
47
+ 'rules'?: FormRules<Recordable> | FormRuleFn<T>;
48
+ 'defaultData'?: T;
49
+ 'inline'?: boolean;
50
+ 'colSpan'?: number;
51
+ 'size'?: ComponentSize;
52
+ 'rowGutter'?: number;
53
+ 'labelWidth'?: string;
54
+ 'labelPosition'?: 'left' | 'top' | 'right';
55
+ 'labelSuffix'?: string;
56
+ 'validate-on-rule-change'?: boolean;
57
+ 'disabled'?: boolean;
58
+ 'require-asterisk-position'?: 'left' | 'right';
59
+ 'scrollTo-error'?: boolean;
60
+ 'scroll-into-view-options'?: boolean | ScrollIntoViewOptions;
61
+ 'status-icon'?: boolean;
62
+ 'show-message'?: boolean;
63
+ 'inline-message'?: boolean;
64
+ 'enterCallback'?: () => void;
65
+ /**
66
+ * Custom component merged with default component. Only affects this useForm instance.
67
+ * eg: { 'c-input': CInput, 'c-select': CSelect }
68
+ * If the same key will retain the custom component.
69
+ */
70
+ 'customComponent'?: Record<string, Component>;
71
+ }
72
+ type FormReturnType<T extends object> = [Component, {
73
+ validate: (callback: (data: T) => void) => void;
74
+ reset: () => void;
75
+ getData: () => T;
76
+ setData: (newData: T) => void;
77
+ }];
78
+ declare function setDeepProperty(data: Recordable, propArr: string[], value: unknown): void;
79
+ declare function useForm<T extends object>(options: FormOptions<T>): FormReturnType<T>;
80
+ //#endregion
81
+ //#region src/table/useTable/index.d.ts
82
+ type DefaultRow = Record<PropertyKey, any>;
83
+ interface TableColumn<T extends DefaultRow> {
84
+ render?: (row: T) => VNode | string;
85
+ children?: TableColumn<T>[];
86
+ prop?: string;
87
+ label?: string;
88
+ width?: string | number;
89
+ minWidth?: string | number;
90
+ fixed?: boolean | 'left' | 'right';
91
+ align?: 'left' | 'center' | 'right';
92
+ headerAlign?: 'left' | 'center' | 'right';
93
+ className?: string;
94
+ labelClassName?: string;
95
+ showOverflowTooltip?: boolean;
96
+ sortable?: boolean | string;
97
+ sortMethod?: (a: T, b: T) => number;
98
+ sortBy?: string | string[];
99
+ sortOrders?: ('ascending' | 'descending')[];
100
+ resizable?: boolean;
101
+ formatter?: (row: T, column: TableColumnCtx<T>, cellValue: any, index: number) => any;
102
+ type?: 'selection' | 'index' | 'expand';
103
+ selectable?: (row: T, index: number) => boolean;
104
+ reserveSelection?: boolean;
105
+ index?: number | ((index: number) => number);
106
+ }
107
+ interface TableOptions<T extends DefaultRow> extends TableProps<T> {
108
+ columns: TableColumn<T>[];
109
+ align?: 'left' | 'center' | 'right';
110
+ headerAlign?: 'left' | 'center' | 'right';
111
+ }
112
+ type TableReturn = [Component];
113
+ declare function useTable<T extends DefaultRow>(options: TableOptions<T>): TableReturn;
114
+ //#endregion
115
+ export { AsyncConfirmOptions, CustomRender, DefaultComponentKey, FormItemType, FormOptions, FormReturnType, FormRuleFn, FormSchema, Recordable, TableColumn, TableOptions, TableReturn, setDeepProperty, useAsyncConfirm, useForm, useMessage, useTable };
package/dist/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { useRequest } from "@vuetkit/core";
2
- import { ElMessage, ElMessageBox } from "element-plus";
2
+ import { ElCascader, ElCheckbox, ElCol, ElColorPicker, ElDatePicker, ElForm, ElFormItem, ElInput, ElInputNumber, ElInputOtp, ElMention, ElMessage, ElMessageBox, ElRadio, ElRate, ElRow, ElSelect, ElSelectV2, ElSlider, ElSwitch, ElTable, ElTableColumn, ElTimePicker, ElTimeSelect, ElTransfer, ElTreeSelect, ElUpload } from "element-plus";
3
+ import { isFunc } from "@vuetkit/shared";
4
+ import { computed, defineComponent, h, nextTick, onMounted, ref, useTemplateRef } from "vue";
3
5
  //#region src/feedback/useMessage/index.ts
4
6
  const DEFAULT_DURATION = 3e3;
5
7
  const DEFAULT_SHOW_CLOSE = true;
@@ -78,4 +80,238 @@ function useAsyncConfirm(confirmService, options) {
78
80
  };
79
81
  }
80
82
  //#endregion
81
- export { useAsyncConfirm, useMessage };
83
+ //#region src/form/useForm/index.ts
84
+ const defaultComponent = {
85
+ "input": ElInput,
86
+ "select": ElSelect,
87
+ "date-picker": ElDatePicker,
88
+ "time-picker": ElTimePicker,
89
+ "checkbox": ElCheckbox,
90
+ "radio": ElRadio,
91
+ "upload": ElUpload,
92
+ "input-number": ElInputNumber,
93
+ "switch": ElSwitch,
94
+ "rate": ElRate,
95
+ "tree-select": ElTreeSelect,
96
+ "cascader": ElCascader,
97
+ "color-picker": ElColorPicker,
98
+ "input-otp": ElInputOtp,
99
+ "mention": ElMention,
100
+ "select-v2": ElSelectV2,
101
+ "slider": ElSlider,
102
+ "time-select": ElTimeSelect,
103
+ "transfer": ElTransfer
104
+ };
105
+ function setDeepProperty(data, propArr, value) {
106
+ let temp = data;
107
+ for (let i = 0; i < propArr.length - 1; i++) {
108
+ const key = propArr[i];
109
+ if (temp[key] === void 0) temp[key] = {};
110
+ temp = temp[key];
111
+ }
112
+ temp[propArr[propArr.length - 1]] = value;
113
+ }
114
+ function useForm(options) {
115
+ const { schemas = [], rules = {}, colSpan = 24, defaultData, disabled = false, inline = false, size, rowGutter = 20, labelWidth, labelSuffix = "", labelPosition = "left", "validate-on-rule-change": validateOnRuleChange = true, enterCallback, customComponent, "scrollTo-error": scrollToError = false, "scroll-into-view-options": scrollIntoViewOptions = true, "status-icon": statusIcon = false, "require-asterisk-position": requireAsteriskPosition = "left", "show-message": showMessage = true, "inline-message": inlineMessage = false } = options || {};
116
+ const components = {};
117
+ if (customComponent) Object.assign(components, defaultComponent, customComponent);
118
+ else Object.assign(components, defaultComponent);
119
+ const initFormData = schemas.reduce((prev, cur) => {
120
+ const prop = cur.prop;
121
+ const propArr = prop.split(".");
122
+ if ((propArr === null || propArr === void 0 ? void 0 : propArr.length) < 2) prev[prop] = void 0;
123
+ else setDeepProperty(prev, propArr, void 0);
124
+ return prev;
125
+ }, {});
126
+ const defaultFormData = defaultData || initFormData;
127
+ const copyInitFormData = JSON.parse(JSON.stringify(defaultFormData));
128
+ const formData = ref(defaultFormData);
129
+ const formInstanceRef = ref();
130
+ function getComponent(type) {
131
+ if (type && typeof type !== "string") return type;
132
+ return components[type];
133
+ }
134
+ const FormComp = defineComponent((props, { slots }) => {
135
+ const formRef = useTemplateRef("formRef");
136
+ /**
137
+ * get data value
138
+ * @data data object
139
+ * @prop bind prop
140
+ * eg:
141
+ * prop: 'user.name'
142
+ * data: {
143
+ * user: {
144
+ * usename: 'vuetkit',
145
+ * }
146
+ * }
147
+ */
148
+ function getFormData(data, prop) {
149
+ var _prop;
150
+ const propArr = prop.split(".");
151
+ if ((propArr === null || propArr === void 0 ? void 0 : propArr.length) < 2) return (_prop = data[prop]) !== null && _prop !== void 0 ? _prop : void 0;
152
+ let value = data;
153
+ for (const propItem of propArr) {
154
+ if (value === void 0 || value === null) return;
155
+ if (typeof value !== "object") return;
156
+ value = value[propItem];
157
+ }
158
+ return value;
159
+ }
160
+ function setFormData(data, prop, value) {
161
+ const propArr = prop.split(".");
162
+ if ((propArr === null || propArr === void 0 ? void 0 : propArr.length) < 2) {
163
+ data[prop] = value;
164
+ return;
165
+ }
166
+ setDeepProperty(data, propArr, value);
167
+ }
168
+ function handleEnter() {
169
+ enterCallback === null || enterCallback === void 0 || enterCallback();
170
+ }
171
+ const renderFormItem = (schema, slots) => {
172
+ if (isFunc(schema.type)) return h(schema.type, {});
173
+ return h(ElFormItem, {
174
+ label: schema.label,
175
+ prop: schema.prop,
176
+ size
177
+ }, () => {
178
+ if (slots[schema.prop]) {
179
+ var _slots$schema$prop;
180
+ return (_slots$schema$prop = slots[schema.prop]) === null || _slots$schema$prop === void 0 ? void 0 : _slots$schema$prop.call(slots);
181
+ }
182
+ const component = getComponent(schema.type);
183
+ if (!component) return null;
184
+ return h(component, {
185
+ "modelValue": getFormData(formData.value, schema.prop),
186
+ "onUpdate:modelValue": (val) => {
187
+ return setFormData(formData.value, schema.prop, val);
188
+ },
189
+ "onKeydown": (e) => {
190
+ if (e.key === "Enter" && !e.ctrlKey && !e.metaKey) {
191
+ e.preventDefault();
192
+ e.stopPropagation();
193
+ handleEnter();
194
+ }
195
+ },
196
+ ...schema.componentProps || {}
197
+ });
198
+ });
199
+ };
200
+ const renderFormItems = (slots) => {
201
+ return schemas.map((schema) => {
202
+ if (inline) return renderFormItem(schema, slots);
203
+ return h(ElCol, {
204
+ span: schema.span ? schema.span : colSpan,
205
+ propItem: schema.prop
206
+ }, () => renderFormItem(schema, slots));
207
+ });
208
+ };
209
+ onMounted(async () => {
210
+ await nextTick();
211
+ formInstanceRef.value = formRef.value;
212
+ });
213
+ const formRules = computed(() => {
214
+ if (isFunc(rules)) {
215
+ var _ref;
216
+ return (_ref = rules === null || rules === void 0 ? void 0 : rules(formData.value)) !== null && _ref !== void 0 ? _ref : void 0;
217
+ }
218
+ return rules;
219
+ });
220
+ return () => {
221
+ return h(ElForm, {
222
+ model: formData.value,
223
+ rules: formRules.value,
224
+ size,
225
+ validateOnRuleChange,
226
+ labelWidth,
227
+ labelPosition,
228
+ labelSuffix,
229
+ ref: "formRef",
230
+ inline,
231
+ disabled,
232
+ scrollToError,
233
+ scrollIntoViewOptions,
234
+ statusIcon,
235
+ requireAsteriskPosition,
236
+ showMessage,
237
+ inlineMessage,
238
+ ...props
239
+ }, () => {
240
+ const children = [];
241
+ if (inline) children.push(renderFormItems(slots));
242
+ else children.push(h(ElRow, { gutter: rowGutter }, () => renderFormItems(slots)));
243
+ if (slots === null || slots === void 0 ? void 0 : slots.footer) children.push(slots.footer());
244
+ return children;
245
+ });
246
+ };
247
+ });
248
+ async function validate(callback) {
249
+ var _formInstanceRef$valu;
250
+ return await ((_formInstanceRef$valu = formInstanceRef.value) === null || _formInstanceRef$valu === void 0 ? void 0 : _formInstanceRef$valu.validate((valid) => {
251
+ if (valid) callback === null || callback === void 0 || callback(formData.value);
252
+ }));
253
+ }
254
+ function reset() {
255
+ var _formInstanceRef$valu2;
256
+ formData.value = copyInitFormData;
257
+ (_formInstanceRef$valu2 = formInstanceRef.value) === null || _formInstanceRef$valu2 === void 0 || _formInstanceRef$valu2.resetFields();
258
+ }
259
+ function getData() {
260
+ return formData.value;
261
+ }
262
+ function setData(newData) {
263
+ formData.value = newData;
264
+ }
265
+ return [FormComp, {
266
+ validate,
267
+ reset,
268
+ getData,
269
+ setData
270
+ }];
271
+ }
272
+ //#endregion
273
+ //#region src/table/useTable/index.ts
274
+ function useTable(options) {
275
+ const { columns, align, headerAlign, ...rest } = options;
276
+ return [defineComponent((props, { slots }) => {
277
+ const renderTableItemNodes = (column) => {
278
+ var _column$children;
279
+ if (isFunc(column.render)) return { default: ({ row }) => column.render(row) };
280
+ const tableItemNodes = [];
281
+ if (column === null || column === void 0 || (_column$children = column.children) === null || _column$children === void 0 ? void 0 : _column$children.length) tableItemNodes.push(...renderTableItems(column.children));
282
+ return () => tableItemNodes;
283
+ };
284
+ const renderTableItem = (column) => {
285
+ const { align: columnAlign, headerAlign: columnHeaderAlign } = column;
286
+ const tableItemAlign = columnAlign || align || "left";
287
+ const tableItemHeaderAlign = columnHeaderAlign || headerAlign || "left";
288
+ return h(ElTableColumn, {
289
+ ...column,
290
+ align: tableItemAlign,
291
+ headerAlign: tableItemHeaderAlign
292
+ }, renderTableItemNodes(column));
293
+ };
294
+ const renderTableItems = (columns) => {
295
+ return columns.filter(Boolean).map((column) => {
296
+ return renderTableItem(column);
297
+ });
298
+ };
299
+ return () => {
300
+ return h(ElTable, {
301
+ ...rest,
302
+ ...props
303
+ }, {
304
+ default: () => {
305
+ const tableNodes = [];
306
+ if (columns.length) tableNodes.push(...renderTableItems(columns));
307
+ if (slots.actions) tableNodes.push(slots.actions());
308
+ return tableNodes;
309
+ },
310
+ append: slots === null || slots === void 0 ? void 0 : slots.append,
311
+ empty: slots === null || slots === void 0 ? void 0 : slots.empty
312
+ });
313
+ };
314
+ })];
315
+ }
316
+ //#endregion
317
+ export { setDeepProperty, useAsyncConfirm, useForm, useMessage, useTable };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vuetkit/components",
3
3
  "type": "module",
4
- "version": "0.0.8",
4
+ "version": "0.0.10",
5
5
  "description": "Collection of business development components for Vue3 projects",
6
6
  "author": "Kalu5",
7
7
  "license": "MIT",
@@ -20,6 +20,9 @@
20
20
  "element-plus": "^2.14.1",
21
21
  "vue": "^3.5.35"
22
22
  },
23
+ "dependencies": {
24
+ "@vuetkit/shared": "0.0.10"
25
+ },
23
26
  "scripts": {
24
27
  "build": "tsdown --config-loader tsx"
25
28
  }