@hostlink/nuxt-light 1.73.2 → 1.74.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.
Files changed (50) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/runtime/components/L/App.vue +0 -19
  3. package/dist/runtime/components/L/AppMain.d.vue.ts +3 -3
  4. package/dist/runtime/components/L/AppMain.vue +43 -10
  5. package/dist/runtime/components/L/AppMain.vue.d.ts +3 -3
  6. package/dist/runtime/components/L/FacebookButton.d.vue.ts +19 -3
  7. package/dist/runtime/components/L/FacebookButton.vue +73 -12
  8. package/dist/runtime/components/L/FacebookButton.vue.d.ts +19 -3
  9. package/dist/runtime/components/L/FavMenu.d.vue.ts +2 -0
  10. package/dist/runtime/components/L/FavMenu.vue +10 -2
  11. package/dist/runtime/components/L/FavMenu.vue.d.ts +2 -0
  12. package/dist/runtime/components/L/Form.d.vue.ts +29 -56
  13. package/dist/runtime/components/L/Form.vue +70 -80
  14. package/dist/runtime/components/L/Form.vue.d.ts +29 -56
  15. package/dist/runtime/components/L/FormLayout.d.vue.ts +36 -0
  16. package/dist/runtime/components/L/FormLayout.vue +36 -0
  17. package/dist/runtime/components/L/FormLayout.vue.d.ts +36 -0
  18. package/dist/runtime/components/L/Login.vue +1 -1
  19. package/dist/runtime/components/L/Menu.d.vue.ts +2 -0
  20. package/dist/runtime/components/L/Menu.vue +4 -4
  21. package/dist/runtime/components/L/Menu.vue.d.ts +2 -0
  22. package/dist/runtime/composables/useLight.d.ts +0 -4
  23. package/dist/runtime/formkit/Form.d.vue.ts +2 -2
  24. package/dist/runtime/formkit/Form.vue +15 -18
  25. package/dist/runtime/formkit/Form.vue.d.ts +2 -2
  26. package/dist/runtime/formkit/index.js +2 -0
  27. package/dist/runtime/locales/en.json +8 -2
  28. package/dist/runtime/locales/zh-hk.json +8 -2
  29. package/dist/runtime/pages/Role/[name]/update-child.vue +4 -1
  30. package/dist/runtime/pages/System/fs.vue +116 -15
  31. package/dist/runtime/pages/User/setting/index.vue +29 -12
  32. package/dist/runtime/pages/User/setting/information.vue +4 -28
  33. package/dist/runtime/pages/User/setting/open_id.vue +132 -91
  34. package/dist/runtime/pages/User/setting/two-factor-auth.vue +388 -143
  35. package/dist/runtime/pages/User/setting.vue +3 -4
  36. package/dist/runtime/types/form.d.ts +2 -0
  37. package/dist/runtime/types/form.js +0 -0
  38. package/dist/runtime/utils/filterMenuItems.d.ts +10 -0
  39. package/dist/runtime/utils/filterMenuItems.js +34 -0
  40. package/dist/runtime/utils/formLayout.d.ts +2 -0
  41. package/dist/runtime/utils/formLayout.js +7 -0
  42. package/dist/runtime/utils/socialSdk.d.ts +2 -0
  43. package/dist/runtime/utils/socialSdk.js +127 -0
  44. package/package.json +14 -14
  45. package/dist/runtime/pages/Role/add2.d.vue.ts +0 -3
  46. package/dist/runtime/pages/Role/add2.vue +0 -57
  47. package/dist/runtime/pages/Role/add2.vue.d.ts +0 -3
  48. package/dist/runtime/pages/System/test.d.vue.ts +0 -3
  49. package/dist/runtime/pages/System/test.vue +0 -13
  50. package/dist/runtime/pages/System/test.vue.d.ts +0 -3
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "light",
3
3
  "configKey": "light",
4
- "version": "1.73.2",
4
+ "version": "1.74.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
@@ -62,25 +62,6 @@ if (app.value.version != "dev") {
62
62
  });
63
63
  }
64
64
  }
65
- if (app.value.facebookAppId) {
66
- (function(d, s, id) {
67
- var js, fjs = d.getElementsByTagName(s)[0];
68
- if (d.getElementById(id)) {
69
- return;
70
- }
71
- js = d.createElement(s);
72
- js.id = id;
73
- js.src = "https://connect.facebook.net/en_US/sdk.js";
74
- fjs.parentNode.insertBefore(js, fjs);
75
- })(document, "script", "facebook-jssdk");
76
- window.fbAsyncInit = function() {
77
- FB.init({
78
- appId: app.value.facebookAppId,
79
- xfbml: true,
80
- version: "v21.0"
81
- });
82
- };
83
- }
84
65
  </script>
85
66
 
86
67
  <template>
@@ -20,7 +20,7 @@ type __VLS_Props = {
20
20
  userMenuItems?: UserMenuItem[];
21
21
  headerButtons?: HeaderButton[];
22
22
  };
23
- declare var __VLS_38: {}, __VLS_61: `header-button-menu-${string}`, __VLS_62: {}, __VLS_132: `header-button-menu-${string}`, __VLS_133: {}, __VLS_340: {}, __VLS_482: {}, __VLS_489: {};
23
+ declare var __VLS_38: {}, __VLS_61: `header-button-menu-${string}`, __VLS_62: {}, __VLS_132: `header-button-menu-${string}`, __VLS_133: {}, __VLS_340: {}, __VLS_501: {}, __VLS_508: {};
24
24
  type __VLS_Slots = {} & {
25
25
  [K in NonNullable<typeof __VLS_61>]?: (props: typeof __VLS_62) => any;
26
26
  } & {
@@ -30,9 +30,9 @@ type __VLS_Slots = {} & {
30
30
  } & {
31
31
  'user-menu'?: (props: typeof __VLS_340) => any;
32
32
  } & {
33
- 'page-top'?: (props: typeof __VLS_482) => any;
33
+ 'page-top'?: (props: typeof __VLS_501) => any;
34
34
  } & {
35
- 'page-bottom'?: (props: typeof __VLS_489) => any;
35
+ 'page-bottom'?: (props: typeof __VLS_508) => any;
36
36
  };
37
37
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
38
38
  logout: (...args: any[]) => void;
@@ -6,6 +6,7 @@ import { useI18n } from "vue-i18n";
6
6
  import { ref, computed, reactive, provide, watch, toRaw, onMounted } from "vue";
7
7
  import { useRuntimeConfig } from "nuxt/app";
8
8
  import { logout } from "@hostlink/light";
9
+ import { filterMenuItems } from "../../utils/filterMenuItems";
9
10
  const emits = defineEmits(["logout"]);
10
11
  defineProps({
11
12
  userMenuItems: { type: Array, required: false },
@@ -69,9 +70,6 @@ light.setMyFavorites(toRaw(my.myFavorites));
69
70
  const myFavorites = computed(() => {
70
71
  return light.getMyFavorites();
71
72
  });
72
- const myFavoritesCount = computed(() => {
73
- return light.getMyFavorites().length;
74
- });
75
73
  $q.dark.set(light.isDarkMode());
76
74
  const i18n = useI18n();
77
75
  i18n.locale.value = my.language || "en";
@@ -81,8 +79,26 @@ for (let t of app.i18nMessages) {
81
79
  messages[t.name] = t.value;
82
80
  }
83
81
  i18n.setLocaleMessage(i18n.locale.value, messages);
84
- const menus = ref(app.menus);
85
- const my_menus = ref(my.menu);
82
+ const menus = ref(app.menus || []);
83
+ const my_menus = ref(my.menu || []);
84
+ const menuSearch = ref("");
85
+ const translateMenuLabel = (label) => i18n.t(label);
86
+ const filteredMenus = computed(
87
+ () => filterMenuItems(menus.value, menuSearch.value, translateMenuLabel)
88
+ );
89
+ const filteredMyMenus = computed(
90
+ () => filterMenuItems(my_menus.value, menuSearch.value, translateMenuLabel)
91
+ );
92
+ const filteredMyFavorites = computed(
93
+ () => filterMenuItems(myFavorites.value, menuSearch.value, translateMenuLabel)
94
+ );
95
+ const normalizedMenuSearch = computed(
96
+ () => (menuSearch.value || "").trim().toLocaleLowerCase()
97
+ );
98
+ const isSearchingMenu = computed(() => normalizedMenuSearch.value.length > 0);
99
+ const hasMenuSearchResults = computed(
100
+ () => filteredMyMenus.value.length > 0 || filteredMyFavorites.value.length > 0 || filteredMenus.value.length > 0
101
+ );
86
102
  const leftDrawerOpen = ref(false);
87
103
  const rightDrawerOpen = ref(false);
88
104
  const toggleLeftDrawer = () => {
@@ -142,8 +158,8 @@ const reloadMenu = async () => {
142
158
  menu: true
143
159
  }
144
160
  });
145
- menus.value = app2.menus;
146
- my_menus.value.menu = my2.menu;
161
+ menus.value = app2.menus || [];
162
+ my_menus.value = my2.menu || [];
147
163
  };
148
164
  provide("reloadMenu", reloadMenu);
149
165
  watch(() => style.footer, async (value) => await light.setStyle("footer", value));
@@ -413,9 +429,26 @@ const onLogout = async () => {
413
429
  <!-- drawer content -->
414
430
  <q-scroll-area class="fit">
415
431
  <div class="q-mx-xs q-mt-xs">
416
- <l-menu :value="my_menus" :dense="style.dense" />
417
- <l-fav-menu :value="myFavorites" :dense="style.dense" v-if="myFavoritesCount > 0" />
418
- <l-menu :value="menus" :dense="style.dense" />
432
+ <q-input v-if="!isMini" v-model="menuSearch" :placeholder="$t('Search menu')" dense outlined
433
+ clearable :dark="menuDark" class="q-mb-sm" input-class="text-body2"
434
+ :aria-label="$t('Search menu')" @keyup.esc="menuSearch = ''">
435
+ <template #prepend>
436
+ <q-icon name="sym_o_search" />
437
+ </template>
438
+ </q-input>
439
+
440
+ <l-menu :key="`my-${normalizedMenuSearch}`" :value="filteredMyMenus" :dense="style.dense"
441
+ :expand-all="isSearchingMenu" />
442
+ <l-fav-menu :value="filteredMyFavorites" :dense="style.dense"
443
+ :expand-all="isSearchingMenu" v-if="filteredMyFavorites.length > 0" />
444
+ <l-menu :key="`app-${normalizedMenuSearch}`" :value="filteredMenus" :dense="style.dense"
445
+ :expand-all="isSearchingMenu" />
446
+
447
+ <div v-if="isSearchingMenu && !hasMenuSearchResults"
448
+ class="column items-center text-grey-6 q-pa-lg">
449
+ <q-icon name="sym_o_search_off" size="md" class="q-mb-sm" />
450
+ <span>{{ $t("No matching menu") }}</span>
451
+ </div>
419
452
  </div>
420
453
  </q-scroll-area>
421
454
  </q-drawer>
@@ -20,7 +20,7 @@ type __VLS_Props = {
20
20
  userMenuItems?: UserMenuItem[];
21
21
  headerButtons?: HeaderButton[];
22
22
  };
23
- declare var __VLS_38: {}, __VLS_61: `header-button-menu-${string}`, __VLS_62: {}, __VLS_132: `header-button-menu-${string}`, __VLS_133: {}, __VLS_340: {}, __VLS_482: {}, __VLS_489: {};
23
+ declare var __VLS_38: {}, __VLS_61: `header-button-menu-${string}`, __VLS_62: {}, __VLS_132: `header-button-menu-${string}`, __VLS_133: {}, __VLS_340: {}, __VLS_501: {}, __VLS_508: {};
24
24
  type __VLS_Slots = {} & {
25
25
  [K in NonNullable<typeof __VLS_61>]?: (props: typeof __VLS_62) => any;
26
26
  } & {
@@ -30,9 +30,9 @@ type __VLS_Slots = {} & {
30
30
  } & {
31
31
  'user-menu'?: (props: typeof __VLS_340) => any;
32
32
  } & {
33
- 'page-top'?: (props: typeof __VLS_482) => any;
33
+ 'page-top'?: (props: typeof __VLS_501) => any;
34
34
  } & {
35
- 'page-bottom'?: (props: typeof __VLS_489) => any;
35
+ 'page-bottom'?: (props: typeof __VLS_508) => any;
36
36
  };
37
37
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
38
38
  logout: (...args: any[]) => void;
@@ -1,7 +1,23 @@
1
1
  declare const _default: typeof __VLS_export;
2
2
  export default _default;
3
- declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
3
+ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
4
+ appId: {
5
+ type: StringConstructor;
6
+ required: true;
7
+ };
8
+ disable: BooleanConstructor;
9
+ }>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
10
+ error: (...args: any[]) => void;
4
11
  login: (...args: any[]) => void;
5
- }, string, import("vue").PublicProps, Readonly<{}> & Readonly<{
12
+ }, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
13
+ appId: {
14
+ type: StringConstructor;
15
+ required: true;
16
+ };
17
+ disable: BooleanConstructor;
18
+ }>> & Readonly<{
19
+ onError?: ((...args: any[]) => any) | undefined;
6
20
  onLogin?: ((...args: any[]) => any) | undefined;
7
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
21
+ }>, {
22
+ disable: boolean;
23
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
@@ -1,20 +1,81 @@
1
1
  <script setup>
2
- import { ref, onMounted } from "vue";
3
- const button = ref(null);
4
- const emits = defineEmits(["login"]);
5
- onMounted(() => {
6
- if (window.FB) {
7
- window.FB.XFBML.parse();
8
- }
2
+ import { onMounted, ref } from "vue";
3
+ import { useQuasar } from "quasar";
4
+ import { fabFacebookF } from "@quasar/extras/fontawesome-v7";
5
+ import { loadFacebookSdk } from "../../utils/socialSdk";
6
+ const props = defineProps({
7
+ appId: {
8
+ type: String,
9
+ required: true
10
+ },
11
+ disable: Boolean
9
12
  });
10
- window.onFacebookLogin = () => {
11
- window.FB.getAccessToken((accessToken) => {
12
- emits("login", accessToken);
13
+ const emit = defineEmits(["login", "error"]);
14
+ const $q = useQuasar();
15
+ const sdk = ref(null);
16
+ const loading = ref(true);
17
+ const loggingIn = ref(false);
18
+ const unavailable = ref(false);
19
+ const errorMessage = (error) => error instanceof Error ? error.message : String(error);
20
+ const loadSdk = async () => {
21
+ loading.value = true;
22
+ unavailable.value = false;
23
+ try {
24
+ sdk.value = await loadFacebookSdk(props.appId);
25
+ } catch (error) {
26
+ unavailable.value = true;
27
+ emit("error", error);
28
+ $q.notify({
29
+ message: errorMessage(error),
30
+ color: "negative",
31
+ icon: "sym_o_error"
32
+ });
33
+ } finally {
34
+ loading.value = false;
35
+ }
36
+ };
37
+ const onClick = async () => {
38
+ if (props.disable || loggingIn.value) {
39
+ return;
40
+ }
41
+ if (!sdk.value) {
42
+ await loadSdk();
43
+ }
44
+ if (!sdk.value) {
45
+ return;
46
+ }
47
+ loggingIn.value = true;
48
+ sdk.value.login((response) => {
49
+ loggingIn.value = false;
50
+ const accessToken = response.authResponse?.accessToken;
51
+ if (accessToken) {
52
+ emit("login", accessToken);
53
+ return;
54
+ }
55
+ if (response.status && response.status !== "unknown") {
56
+ const error = new Error("Facebook login did not return an access token");
57
+ emit("error", error);
58
+ $q.notify({
59
+ message: error.message,
60
+ color: "negative",
61
+ icon: "sym_o_error"
62
+ });
63
+ }
64
+ }, {
65
+ scope: "public_profile,email"
13
66
  });
14
67
  };
68
+ onMounted(loadSdk);
15
69
  </script>
16
70
 
17
71
  <template>
18
- <div class="fb-login-button" data-size="large" data-button-type="" data-layout="" data-auto-logout-link="false"
19
- data-use-continue-as="false" ref="button" data-onlogin="onFacebookLogin()"></div>
72
+ <q-btn
73
+ no-caps
74
+ color="indigo"
75
+ :icon="fabFacebookF"
76
+ :label="$t('Continue with Facebook')"
77
+ :loading="loading || loggingIn"
78
+ :disable="disable || unavailable"
79
+ @click="onClick"
80
+ />
20
81
  </template>
@@ -1,7 +1,23 @@
1
1
  declare const _default: typeof __VLS_export;
2
2
  export default _default;
3
- declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
3
+ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
4
+ appId: {
5
+ type: StringConstructor;
6
+ required: true;
7
+ };
8
+ disable: BooleanConstructor;
9
+ }>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
10
+ error: (...args: any[]) => void;
4
11
  login: (...args: any[]) => void;
5
- }, string, import("vue").PublicProps, Readonly<{}> & Readonly<{
12
+ }, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
13
+ appId: {
14
+ type: StringConstructor;
15
+ required: true;
16
+ };
17
+ disable: BooleanConstructor;
18
+ }>> & Readonly<{
19
+ onError?: ((...args: any[]) => any) | undefined;
6
20
  onLogin?: ((...args: any[]) => any) | undefined;
7
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
21
+ }>, {
22
+ disable: boolean;
23
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
@@ -3,7 +3,9 @@ export default _default;
3
3
  declare const __VLS_export: import("vue").DefineComponent<{
4
4
  value?: any;
5
5
  dense?: any;
6
+ expandAll?: any;
6
7
  }, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{
7
8
  value?: any;
8
9
  dense?: any;
10
+ expandAll?: any;
9
11
  }> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
@@ -2,7 +2,7 @@
2
2
  import { computed, ref, watch } from "vue";
3
3
  import { useLight } from "#imports";
4
4
  const light = useLight();
5
- const props = defineProps(["value", "dense"]);
5
+ const props = defineProps(["value", "dense", "expandAll"]);
6
6
  const favMenuExpanded = ref(
7
7
  typeof window !== "undefined" ? JSON.parse(localStorage.getItem("fav-menu-expanded") || "false") : false
8
8
  );
@@ -11,6 +11,14 @@ watch(favMenuExpanded, (newValue) => {
11
11
  localStorage.setItem("fav-menu-expanded", JSON.stringify(newValue));
12
12
  }
13
13
  });
14
+ const displayedExpanded = computed({
15
+ get: () => props.expandAll || favMenuExpanded.value,
16
+ set: (value) => {
17
+ if (!props.expandAll) {
18
+ favMenuExpanded.value = value;
19
+ }
20
+ }
21
+ });
14
22
  const color1 = computed(() => {
15
23
  return light.getColorValue();
16
24
  });
@@ -27,7 +35,7 @@ const color2 = computed(() => {
27
35
 
28
36
  <template>
29
37
  <q-expansion-item :label="$t('My favorite')" :dense="dense" icon="sym_o_favorite"
30
- v-model="favMenuExpanded">
38
+ v-model="displayedExpanded">
31
39
  <q-list class="q-pl-md menu-list" :dense="dense">
32
40
  <q-item v-ripple :to="item.path" v-for="item in value">
33
41
  <q-item-section avatar>
@@ -3,7 +3,9 @@ export default _default;
3
3
  declare const __VLS_export: import("vue").DefineComponent<{
4
4
  value?: any;
5
5
  dense?: any;
6
+ expandAll?: any;
6
7
  }, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{
7
8
  value?: any;
8
9
  dense?: any;
10
+ expandAll?: any;
9
11
  }> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
@@ -1,70 +1,43 @@
1
- declare var __VLS_23: {};
1
+ import type { LFormGutter, LFormLayout } from "../../types/form.js";
2
+ type __VLS_Props = {
3
+ actions?: boolean;
4
+ bordered?: boolean;
5
+ gutter?: LFormGutter;
6
+ layout?: LFormLayout;
7
+ modelValue?: Record<string, unknown>;
8
+ noRedirect?: boolean;
9
+ submitIcon?: string;
10
+ submitLabel?: string;
11
+ };
12
+ declare var __VLS_17: {}, __VLS_20: {
13
+ loading: boolean;
14
+ }, __VLS_22: {
15
+ loading: boolean;
16
+ };
2
17
  type __VLS_Slots = {} & {
3
- default?: (props: typeof __VLS_23) => any;
18
+ default?: (props: typeof __VLS_17) => any;
19
+ } & {
20
+ actions?: (props: typeof __VLS_20) => any;
21
+ } & {
22
+ buttons?: (props: typeof __VLS_22) => any;
4
23
  };
5
- declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
6
- modelValue: {
7
- type: ObjectConstructor;
8
- };
9
- bordered: {
10
- type: BooleanConstructor;
11
- default: boolean;
12
- };
13
- gutter: {
14
- type: StringConstructor;
15
- default: string;
16
- };
17
- submitLabel: {
18
- type: StringConstructor;
19
- default: string;
20
- };
21
- submitIcon: {
22
- type: StringConstructor;
23
- default: string;
24
- };
25
- noRedirect: {
26
- type: BooleanConstructor;
27
- default: boolean;
28
- };
29
- }>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
24
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
30
25
  submit: (...args: any[]) => void;
31
26
  save: (...args: any[]) => void;
32
27
  submitted: (...args: any[]) => void;
33
- }, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
34
- modelValue: {
35
- type: ObjectConstructor;
36
- };
37
- bordered: {
38
- type: BooleanConstructor;
39
- default: boolean;
40
- };
41
- gutter: {
42
- type: StringConstructor;
43
- default: string;
44
- };
45
- submitLabel: {
46
- type: StringConstructor;
47
- default: string;
48
- };
49
- submitIcon: {
50
- type: StringConstructor;
51
- default: string;
52
- };
53
- noRedirect: {
54
- type: BooleanConstructor;
55
- default: boolean;
56
- };
57
- }>> & Readonly<{
28
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
58
29
  onSubmit?: ((...args: any[]) => any) | undefined;
59
30
  onSave?: ((...args: any[]) => any) | undefined;
60
31
  onSubmitted?: ((...args: any[]) => any) | undefined;
61
32
  }>, {
33
+ actions: boolean;
62
34
  bordered: boolean;
63
- gutter: string;
64
- submitLabel: string;
65
- submitIcon: string;
35
+ gutter: LFormGutter;
36
+ layout: LFormLayout;
66
37
  noRedirect: boolean;
67
- }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
38
+ submitIcon: string;
39
+ submitLabel: string;
40
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
68
41
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
69
42
  declare const _default: typeof __VLS_export;
70
43
  export default _default;
@@ -1,48 +1,41 @@
1
1
  <script setup>
2
- import { computed, ref, useAttrs, getCurrentInstance } from "vue";
3
- import { useRouter, useRoute } from "vue-router";
4
- import { useQuasar, QForm } from "quasar";
2
+ import { computed, getCurrentInstance, ref, useAttrs, useSlots } from "vue";
3
+ import { useRoute, useRouter } from "vue-router";
4
+ import { useQuasar } from "quasar";
5
5
  import { model } from "#imports";
6
+ defineOptions({ inheritAttrs: false });
7
+ const props = defineProps({
8
+ actions: { type: Boolean, required: false, default: true },
9
+ bordered: { type: Boolean, required: false, default: true },
10
+ gutter: { type: String, required: false, default: "md" },
11
+ layout: { type: String, required: false, default: "grid" },
12
+ modelValue: { type: Object, required: false },
13
+ noRedirect: { type: Boolean, required: false, default: false },
14
+ submitIcon: { type: String, required: false, default: "sym_o_save" },
15
+ submitLabel: { type: String, required: false, default: "Save" }
16
+ });
17
+ const emit = defineEmits(["save", "submit", "submitted"]);
18
+ const attrs = useAttrs();
19
+ const slots = useSlots();
6
20
  const route = useRoute();
7
21
  const router = useRouter();
8
22
  const quasar = useQuasar();
9
23
  const form = ref(null);
10
- const props = defineProps({
11
- modelValue: {
12
- type: Object
13
- },
14
- bordered: {
15
- type: Boolean,
16
- default: true
17
- },
18
- gutter: {
19
- type: String,
20
- default: "md"
21
- },
22
- submitLabel: {
23
- type: String,
24
- default: "Save"
25
- },
26
- submitIcon: {
27
- type: String,
28
- default: "sym_o_save"
29
- },
30
- noRedirect: {
31
- type: Boolean,
32
- default: false
33
- }
34
- });
35
24
  const loading = ref(false);
36
- const attrs = useAttrs();
37
- const emit = defineEmits(["save", "submit", "submitted"]);
38
25
  const instance = getCurrentInstance();
39
- const hasSubmitListener = computed(() => {
40
- return !!instance?.vnode?.props?.onSubmit;
41
- });
26
+ const hasSubmitListener = computed(() => Boolean(instance?.vnode.props?.onSubmit));
27
+ const hasCustomActions = computed(() => Boolean(slots.actions || slots.buttons));
28
+ const contentClass = computed(() => attrs.class);
29
+ const formAttrs = computed(() => Object.fromEntries(
30
+ Object.entries(attrs).filter(([key]) => key !== "class")
31
+ ));
32
+ const routeParts = computed(() => typeof route.name === "string" ? route.name.split("-") : []);
33
+ const modelName = computed(() => routeParts.value[0]);
34
+ const idName = computed(() => routeParts.value[1]);
42
35
  const save = async () => {
43
- if (!form.value) return;
44
- let valid = await form.value.validate();
45
- if (!valid) return;
36
+ if (!form.value || !await form.value.validate()) {
37
+ return;
38
+ }
46
39
  emit("save");
47
40
  if (hasSubmitListener.value) {
48
41
  emit("submit", () => {
@@ -50,57 +43,54 @@ const save = async () => {
50
43
  });
51
44
  return;
52
45
  }
53
- if (props.modelValue && route.name) {
54
- try {
55
- if (route.params[id_name]) {
56
- if (await model(module).update(parseInt(route.params[id_name]), props.modelValue)) {
57
- if (!props.noRedirect) {
58
- router.push(`/${module}`);
59
- }
60
- emit("submitted");
61
- return;
62
- }
63
- } else {
64
- if (await model(module).add(props.modelValue)) {
65
- if (!props.noRedirect) {
66
- router.push(`/${module}`);
67
- }
68
- emit("submitted");
69
- return;
70
- }
71
- }
72
- } catch (e) {
73
- quasar.dialog({
74
- title: "Error",
75
- message: e.message,
76
- ok: "OK"
77
- });
46
+ if (!props.modelValue || !modelName.value) {
47
+ return;
48
+ }
49
+ loading.value = true;
50
+ try {
51
+ const routeId = idName.value ? route.params[idName.value] : void 0;
52
+ const saved = routeId ? await model(modelName.value).update(Number(routeId), props.modelValue) : await model(modelName.value).add(props.modelValue);
53
+ if (!saved) {
54
+ return;
78
55
  }
56
+ if (!props.noRedirect) {
57
+ await router.push(`/${modelName.value}`);
58
+ }
59
+ emit("submitted");
60
+ } catch (error) {
61
+ quasar.dialog({
62
+ title: "Error",
63
+ message: error instanceof Error ? error.message : String(error),
64
+ ok: "OK"
65
+ });
66
+ } finally {
67
+ loading.value = false;
79
68
  }
80
- loading.value = false;
81
69
  };
82
- const onSubmit = (e) => {
83
- e.preventDefault();
84
- save();
70
+ const onSubmit = async (event) => {
71
+ event.preventDefault();
72
+ await save();
85
73
  };
86
- const localClass = computed(() => {
87
- if (attrs.class) {
88
- return attrs.class;
89
- }
90
- return "row q-col-gutter-" + props.gutter;
91
- });
92
74
  </script>
93
75
 
94
76
  <template>
95
- <q-form ref="form" @submit="onSubmit">
96
- <l-card :bordered="bordered">
97
- <q-card-section :class="localClass">
98
- <slot></slot>
99
- </q-card-section>
77
+ <q-form ref="form" v-bind="formAttrs" @submit="onSubmit">
78
+ <l-form-layout
79
+ :actions="actions"
80
+ :bordered="bordered"
81
+ :content-class="contentClass"
82
+ :gutter="gutter"
83
+ :layout="layout"
84
+ :submit-icon="submitIcon"
85
+ :submit-label="submitLabel"
86
+ :submit-loading="loading"
87
+ >
88
+ <slot />
100
89
 
101
- <q-card-actions align="right">
102
- <l-btn :icon="submitIcon" :label="submitLabel" :loading="loading" type="submit" />
103
- </q-card-actions>
104
- </l-card>
90
+ <template v-if="hasCustomActions" #actions>
91
+ <slot v-if="$slots.actions" name="actions" :loading="loading" />
92
+ <slot v-else name="buttons" :loading="loading" />
93
+ </template>
94
+ </l-form-layout>
105
95
  </q-form>
106
96
  </template>