@signal24/vue-foundation 4.16.0 → 4.17.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/demo/components/demo-root.vue +21 -0
- package/demo/components/demo-vf-smart-select.vue +28 -0
- package/demo/index.html +14 -0
- package/demo/index.ts +10 -0
- package/demo/vite.config.ts +23 -0
- package/dist/demo/components/demo-root.vue.d.ts +2 -0
- package/dist/demo/components/demo-vf-smart-select.vue.d.ts +2 -0
- package/dist/demo/index.d.ts +1 -0
- package/dist/demo/vite.config.d.ts +2 -0
- package/dist/src/components/index.d.ts +5 -5
- package/dist/src/components/overlay-anchor.vue.d.ts +1 -1
- package/dist/src/components/overlay-container.d.ts +1 -1
- package/dist/src/components/toast-helpers.d.ts +1 -1
- package/dist/src/components/vf-ajax-select.vue.d.ts +26 -0
- package/dist/src/components/{alert-modal.vue.d.ts → vf-alert-modal.vue.d.ts} +1 -1
- package/dist/src/components/{ez-smart-select.vue.d.ts → vf-ez-smart-select.vue.d.ts} +13 -5
- package/dist/src/components/{modal.vue.d.ts → vf-modal.vue.d.ts} +1 -1
- package/dist/src/components/vf-smart-select.vue.d.ts +47 -0
- package/dist/src/components/{toast.vue.d.ts → vf-toast.vue.d.ts} +1 -1
- package/dist/vue-foundation.es.js +828 -893
- package/eslint.config.mjs +67 -0
- package/package.json +10 -8
- package/src/components/alert-helpers.ts +1 -1
- package/src/components/index.ts +5 -5
- package/src/components/overlay-container.ts +5 -1
- package/src/components/toast-helpers.ts +1 -1
- package/src/components/vf-ajax-select.vue +61 -0
- package/src/components/{alert-modal.vue → vf-alert-modal.vue} +5 -5
- package/src/components/{ez-smart-select.vue → vf-ez-smart-select.vue} +12 -8
- package/src/components/{modal.vue → vf-modal.vue} +3 -3
- package/src/components/vf-smart-select.vue +585 -0
- package/src/directives/duration.ts +3 -3
- package/src/directives/hotkey.ts +1 -0
- package/src/filters/index.ts +1 -0
- package/src/helpers/array.ts +1 -0
- package/src/helpers/error.ts +4 -0
- package/src/helpers/object.ts +1 -0
- package/tsconfig.app.json +1 -1
- package/tsconfig.node.json +1 -1
- package/tsconfig.vitest.json +2 -2
- package/.eslintrc.cjs +0 -35
- package/dist/src/components/ajax-select.vue.d.ts +0 -19
- package/dist/src/components/smart-select.vue.d.ts +0 -115
- package/src/components/ajax-select.vue +0 -75
- package/src/components/smart-select.vue +0 -609
- /package/src/components/{toast.vue → vf-toast.vue} +0 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import eslint from '@eslint/js';
|
|
2
|
+
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
|
|
3
|
+
import simpleImportSort from 'eslint-plugin-simple-import-sort';
|
|
4
|
+
import unusedImports from 'eslint-plugin-unused-imports';
|
|
5
|
+
import pluginVue from 'eslint-plugin-vue';
|
|
6
|
+
import tseslint from 'typescript-eslint';
|
|
7
|
+
|
|
8
|
+
export default tseslint.config(
|
|
9
|
+
// global ignores
|
|
10
|
+
{
|
|
11
|
+
ignores: ['dist/', '.yarn/']
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
// presets
|
|
15
|
+
eslint.configs.recommended,
|
|
16
|
+
...tseslint.configs.recommended,
|
|
17
|
+
...pluginVue.configs['flat/recommended'],
|
|
18
|
+
|
|
19
|
+
// register parser options
|
|
20
|
+
{
|
|
21
|
+
files: ['**/*.ts', '**/*.tsx', '**/*.vue'],
|
|
22
|
+
languageOptions: {
|
|
23
|
+
parserOptions: {
|
|
24
|
+
parser: tseslint.parser,
|
|
25
|
+
extraFileExtensions: ['.vue'],
|
|
26
|
+
projectService: true
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
// not sure why some of these TS generics are needed.
|
|
30
|
+
// will circle back when Vue officially updates to ESLint 9
|
|
31
|
+
globals: {
|
|
32
|
+
Omit: false,
|
|
33
|
+
Record: false
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
// plugins & configs
|
|
39
|
+
eslintPluginPrettierRecommended,
|
|
40
|
+
{
|
|
41
|
+
plugins: {
|
|
42
|
+
'unused-imports': unusedImports,
|
|
43
|
+
'simple-import-sort': simpleImportSort
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
rules: {
|
|
47
|
+
'no-console': 'off',
|
|
48
|
+
'no-debugger': 'off',
|
|
49
|
+
'simple-import-sort/imports': 'error',
|
|
50
|
+
'simple-import-sort/exports': 'error',
|
|
51
|
+
'prettier/prettier': 'warn',
|
|
52
|
+
'no-unused-vars': 'off',
|
|
53
|
+
'unused-imports/no-unused-imports': 'error',
|
|
54
|
+
'unused-imports/no-unused-vars': [
|
|
55
|
+
'warn',
|
|
56
|
+
{
|
|
57
|
+
vars: 'all',
|
|
58
|
+
varsIgnorePattern: '^_',
|
|
59
|
+
args: 'all',
|
|
60
|
+
argsIgnorePattern: '^_'
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
// 'vue/multi-word-component-names': 'off',
|
|
64
|
+
// 'vue/no-reserved-component-names': 'off'
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@signal24/vue-foundation",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "4.
|
|
4
|
+
"version": "4.17.0",
|
|
5
5
|
"description": "Common components, directives, and helpers for Vue 3 apps",
|
|
6
6
|
"module": "./dist/vue-foundation.es.js",
|
|
7
7
|
"exports": {
|
|
@@ -23,13 +23,13 @@
|
|
|
23
23
|
"dev": "vite",
|
|
24
24
|
"build": "rm -rf dist && vite build && vue-tsc -p tsconfig.app.json && tsc -p tsconfig.vite-plugins.json && find dist -name '*.tsbuildinfo' -delete",
|
|
25
25
|
"build:watch": "fswatch -o src | xargs -n1 -I{} yarn build",
|
|
26
|
-
"
|
|
26
|
+
"demo": "vite -c ./demo/vite.config.ts ./demo",
|
|
27
27
|
"test:types": "vue-tsc -p tsconfig.vitest.json",
|
|
28
28
|
"test:unit": "vitest",
|
|
29
29
|
"test:e2e": "start-server-and-test preview http://localhost:4173 'cypress run --e2e'",
|
|
30
30
|
"test:e2e:dev": "start-server-and-test 'vite dev --port 4173' http://localhost:4173 'cypress open --e2e'",
|
|
31
31
|
"test:e2e:dev:remote": "DISPLAY=:0 yarn test:e2e:dev",
|
|
32
|
-
"lint": "eslint
|
|
32
|
+
"lint": "eslint --fix .",
|
|
33
33
|
"format": "prettier --write ."
|
|
34
34
|
},
|
|
35
35
|
"license": "MIT",
|
|
@@ -43,10 +43,11 @@
|
|
|
43
43
|
"vue": "^3.4.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
+
"@eslint/js": "^9.9.1",
|
|
46
47
|
"@nabla/vite-plugin-eslint": "^2.0.4",
|
|
47
|
-
"@rushstack/eslint-patch": "^1.10.4",
|
|
48
48
|
"@signal24/openapi-client-codegen": "^1.1.0",
|
|
49
49
|
"@tsconfig/node20": "^20.1.4",
|
|
50
|
+
"@types/eslint__js": "^8.42.3",
|
|
50
51
|
"@types/jsdom": "^21.1.7",
|
|
51
52
|
"@types/lodash": "^4.17.7",
|
|
52
53
|
"@types/node": "^20.16.3",
|
|
@@ -58,7 +59,7 @@
|
|
|
58
59
|
"@vue/tsconfig": "^0.5.1",
|
|
59
60
|
"cypress": "^13.14.1",
|
|
60
61
|
"date-fns": "^3.6.0",
|
|
61
|
-
"eslint": "^
|
|
62
|
+
"eslint": "^9.9.1",
|
|
62
63
|
"eslint-plugin-cypress": "^3.5.0",
|
|
63
64
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
64
65
|
"eslint-plugin-unused-imports": "^4.1.3",
|
|
@@ -66,13 +67,14 @@
|
|
|
66
67
|
"jsdom": "^25.0.0",
|
|
67
68
|
"lodash": "^4.17.21",
|
|
68
69
|
"prettier": "^3.3.3",
|
|
69
|
-
"sass": "^1.
|
|
70
|
+
"sass": "^1.78.0",
|
|
70
71
|
"start-server-and-test": "^2.0.5",
|
|
71
72
|
"type-fest": "^4.26.0",
|
|
72
|
-
"typescript": "
|
|
73
|
+
"typescript": "^5.5.4",
|
|
74
|
+
"typescript-eslint": "^8.4.0",
|
|
73
75
|
"vite": "^5.4.3",
|
|
74
76
|
"vitest": "^2.0.5",
|
|
75
|
-
"vue": "^3.5.
|
|
77
|
+
"vue": "^3.5.1",
|
|
76
78
|
"vue-tsc": "^2.1.4"
|
|
77
79
|
},
|
|
78
80
|
"packageManager": "yarn@4.0.2"
|
package/src/components/index.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import VfAjaxSelect from './ajax-select.vue';
|
|
2
|
-
import VfAlertModal from './alert-modal.vue';
|
|
3
|
-
import VfEzSmartSelect from './ez-smart-select.vue';
|
|
4
|
-
import VfModal from './modal.vue';
|
|
5
|
-
import VfSmartSelect from './smart-select.vue';
|
|
1
|
+
import VfAjaxSelect from './vf-ajax-select.vue';
|
|
2
|
+
import VfAlertModal from './vf-alert-modal.vue';
|
|
3
|
+
import VfEzSmartSelect from './vf-ez-smart-select.vue';
|
|
4
|
+
import VfModal from './vf-modal.vue';
|
|
5
|
+
import VfSmartSelect from './vf-smart-select.vue';
|
|
6
6
|
|
|
7
7
|
export * from './alert-helpers';
|
|
8
8
|
export * from './overlay-container';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
1
2
|
import type { Writable } from 'type-fest';
|
|
2
3
|
import {
|
|
3
4
|
type AllowedComponentProps,
|
|
@@ -69,6 +70,7 @@ export type ObjectComponentProps<T extends Vue__ComponentPublicInstanceConstruct
|
|
|
69
70
|
Omit<ObjectComponentConfig<T>['$props'], keyof VNodeProps | keyof AllowedComponentProps>
|
|
70
71
|
>;
|
|
71
72
|
|
|
73
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
72
74
|
type ObjectOrDefault<T> = T extends object ? T : PropsWithCallback<{}>;
|
|
73
75
|
export type OverlayComponent = Vue__ComponentPublicInstanceConstructor | ((props: any) => any);
|
|
74
76
|
export type OverlayComponentConfig<T> = T extends Vue__ComponentPublicInstanceConstructor
|
|
@@ -124,7 +126,9 @@ export function createOverlayInjection<C extends OverlayComponent, R extends Com
|
|
|
124
126
|
}
|
|
125
127
|
|
|
126
128
|
export function dismissOverlayInjectionByInstance(instance: AnyComponentPublicInstance) {
|
|
127
|
-
|
|
129
|
+
if (instance.$) {
|
|
130
|
+
dismissOverlayInjectionByInternalInstance(instance.$);
|
|
131
|
+
}
|
|
128
132
|
}
|
|
129
133
|
|
|
130
134
|
export function dismissOverlayInjectionByInternalInstance(instance: ComponentInternalInstance) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createOverlayInjection, type OverlayInjection, removeOverlayInjection } from './overlay-container';
|
|
2
|
-
import Toast, { type IToastOptions } from './toast.vue';
|
|
2
|
+
import Toast, { type IToastOptions } from './vf-toast.vue';
|
|
3
3
|
|
|
4
4
|
export function showToast(options: IToastOptions) {
|
|
5
5
|
const injection: OverlayInjection<typeof Toast, unknown> = createOverlayInjection(Toast, {
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<select v-if="!renderOptions" disabled>
|
|
3
|
+
<option>{{ props.loadingText || 'Loading...' }}</option>
|
|
4
|
+
</select>
|
|
5
|
+
<select v-else v-model="selectedItem">
|
|
6
|
+
<option v-if="props.nullText" :value="null">{{ props.nullText }}</option>
|
|
7
|
+
<option v-for="(renderOption, index) in renderOptions" :key="index" :value="options?.[index]">
|
|
8
|
+
{{ renderOption }}
|
|
9
|
+
</option>
|
|
10
|
+
</select>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<script setup lang="ts" generic="T">
|
|
14
|
+
import { computed, defineEmits, defineProps, onMounted, ref, watch } from 'vue';
|
|
15
|
+
|
|
16
|
+
// todo: make type safe when Vue alpha is released
|
|
17
|
+
|
|
18
|
+
const props = defineProps<{
|
|
19
|
+
modelValue: T;
|
|
20
|
+
loadFn: () => Promise<T[]>;
|
|
21
|
+
nullText?: string;
|
|
22
|
+
loadingText?: string;
|
|
23
|
+
displayKey?: keyof T;
|
|
24
|
+
preprocesor?: (option: T) => string;
|
|
25
|
+
}>();
|
|
26
|
+
|
|
27
|
+
const emit = defineEmits<{
|
|
28
|
+
'update:modelValue': [T];
|
|
29
|
+
}>();
|
|
30
|
+
|
|
31
|
+
const options = ref<T[] | null>(null);
|
|
32
|
+
const renderOptions = computed(() => {
|
|
33
|
+
if (!options.value) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const result = options.value.map(option => {
|
|
38
|
+
const typedOption = option as T;
|
|
39
|
+
if (props.preprocesor) return props.preprocesor(typedOption);
|
|
40
|
+
if (props.displayKey) return typedOption[props.displayKey];
|
|
41
|
+
return '';
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
return result;
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const selectedItem = ref<T | null>(props.modelValue ?? null);
|
|
48
|
+
|
|
49
|
+
watch(() => props.loadFn, load);
|
|
50
|
+
watch(
|
|
51
|
+
() => props.modelValue,
|
|
52
|
+
() => (selectedItem.value = props.modelValue)
|
|
53
|
+
);
|
|
54
|
+
watch(selectedItem, () => emit('update:modelValue', selectedItem.value));
|
|
55
|
+
|
|
56
|
+
async function load() {
|
|
57
|
+
options.value = await props.loadFn();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
onMounted(load);
|
|
61
|
+
</script>
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<Modal :class="['vf-alert', ...(classes ?? [])]">
|
|
3
|
-
<template v-if="title"
|
|
3
|
+
<template v-if="title" #header>
|
|
4
4
|
<h1>{{ title }}</h1>
|
|
5
5
|
</template>
|
|
6
6
|
|
|
7
7
|
<div v-if="isHtml" :innerHtml="message" class="user-message"></div>
|
|
8
8
|
<div v-else :innerText="textMessage"></div>
|
|
9
9
|
|
|
10
|
-
<template v-if="!isBare"
|
|
10
|
+
<template v-if="!isBare" #footer>
|
|
11
11
|
<template v-if="shouldConfirm">
|
|
12
|
-
<button class="primary" @click="() => callback(true)"
|
|
12
|
+
<button v-autofocus class="primary" @click="() => callback(true)">Confirm</button>
|
|
13
13
|
<button class="default" @click="() => callback(false)">Cancel</button>
|
|
14
14
|
</template>
|
|
15
|
-
<button v-else class="default" @click="() => callback(true)"
|
|
15
|
+
<button v-else v-autofocus class="default" @click="() => callback(true)">OK</button>
|
|
16
16
|
</template>
|
|
17
17
|
</Modal>
|
|
18
18
|
</template>
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
import { computed } from 'vue';
|
|
22
22
|
|
|
23
23
|
import { formatError } from '../helpers/error';
|
|
24
|
-
import Modal from './modal.vue';
|
|
24
|
+
import Modal from './vf-modal.vue';
|
|
25
25
|
|
|
26
26
|
const props = defineProps<{
|
|
27
27
|
isBare?: boolean;
|
|
@@ -6,16 +6,19 @@
|
|
|
6
6
|
import { isEqual } from 'lodash';
|
|
7
7
|
import { computed, ref, watch } from 'vue';
|
|
8
8
|
|
|
9
|
-
import VfSmartSelect from './smart-select.vue';
|
|
9
|
+
import VfSmartSelect from './vf-smart-select.vue';
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
interface IComputedOption {
|
|
12
|
+
value: string;
|
|
13
|
+
label: string;
|
|
14
|
+
}
|
|
12
15
|
|
|
13
16
|
const props = defineProps<{
|
|
14
17
|
modelValue: string | null | undefined;
|
|
15
18
|
nullTitle?: string;
|
|
16
19
|
placeholder?: string;
|
|
17
|
-
options:
|
|
18
|
-
formatter?: (
|
|
20
|
+
options: { [K: string]: string } | string[];
|
|
21
|
+
formatter?: (item: IComputedOption) => string;
|
|
19
22
|
}>();
|
|
20
23
|
|
|
21
24
|
const computedOpts = computed(() => {
|
|
@@ -29,16 +32,16 @@ const computedOpts = computed(() => {
|
|
|
29
32
|
|
|
30
33
|
const ezFormatter = computed(() => {
|
|
31
34
|
if (props.formatter) {
|
|
32
|
-
return (o:
|
|
35
|
+
return (o: IComputedOption) => props.formatter!(o);
|
|
33
36
|
}
|
|
34
|
-
return (o:
|
|
37
|
+
return (o: IComputedOption) => o.label;
|
|
35
38
|
});
|
|
36
39
|
|
|
37
40
|
const emit = defineEmits<{
|
|
38
41
|
(e: 'update:modelValue', value: string | null): void;
|
|
39
42
|
}>();
|
|
40
43
|
|
|
41
|
-
const selectedItem = ref<
|
|
44
|
+
const selectedItem = ref<IComputedOption | null>(computedOpts.value.find(o => o.value === props.modelValue) ?? null);
|
|
42
45
|
watch(
|
|
43
46
|
() => props.modelValue,
|
|
44
47
|
value => {
|
|
@@ -46,6 +49,7 @@ watch(
|
|
|
46
49
|
}
|
|
47
50
|
);
|
|
48
51
|
watch(selectedItem, value => {
|
|
49
|
-
|
|
52
|
+
const emitValue = value ? computedOpts.value.find(o => isEqual(o, value))?.value : null;
|
|
53
|
+
emit('update:modelValue', emitValue ?? null);
|
|
50
54
|
});
|
|
51
55
|
</script>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div :id="id" class="vf-overlay vf-modal-wrap" :class="classList"
|
|
3
|
-
<form action="." class="vf-modal" :class="{ scrolls }" @submit.prevent="$emit('formSubmit')"
|
|
2
|
+
<div :id="id" ref="overlay" class="vf-overlay vf-modal-wrap" :class="classList">
|
|
3
|
+
<form ref="form" action="." class="vf-modal" :class="{ scrolls }" @submit.prevent="$emit('formSubmit')">
|
|
4
4
|
<div v-if="$slots.header" class="vf-modal-header">
|
|
5
5
|
<slot name="header" />
|
|
6
6
|
<i v-if="props.closeX" class="close" @click="closeParent"></i>
|
|
@@ -57,7 +57,7 @@ onBeforeUnmount(() => {
|
|
|
57
57
|
window.removeEventListener('keydown', handleEscapeKey);
|
|
58
58
|
|
|
59
59
|
const areOtherModalsOpen = document.body.querySelectorAll('.vf-modal').length > 0;
|
|
60
|
-
areOtherModalsOpen
|
|
60
|
+
if (!areOtherModalsOpen) document.body.classList.remove('vf-modal-open');
|
|
61
61
|
});
|
|
62
62
|
|
|
63
63
|
function handleOverlayClick(e: MouseEvent) {
|