create-nuxt-base 0.1.23 → 0.2.1

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 (74) hide show
  1. package/.eslintignore +14 -0
  2. package/.eslintrc +3 -0
  3. package/.github/workflows/release.yml +1 -1
  4. package/.prettierignore +5 -0
  5. package/.prettierrc +6 -0
  6. package/.vscode/settings.json +10 -10
  7. package/CHANGELOG.md +22 -33
  8. package/README.md +1 -0
  9. package/index.js +30 -29
  10. package/nuxt-base-template/.env.example +8 -0
  11. package/nuxt-base-template/.eslintrc +1 -1
  12. package/nuxt-base-template/.vscode/settings.json +6 -16
  13. package/nuxt-base-template/e2e/init.spec.ts +18 -0
  14. package/nuxt-base-template/nuxt.config.ts +27 -12
  15. package/nuxt-base-template/package-lock.json +6050 -4606
  16. package/nuxt-base-template/package.json +47 -29
  17. package/nuxt-base-template/playwright.config.ts +77 -0
  18. package/nuxt-base-template/src/app.vue +4 -4
  19. package/nuxt-base-template/src/assets/css/tailwind.css +42 -33
  20. package/nuxt-base-template/src/components/SocialMediaBubble.vue +1 -1
  21. package/nuxt-base-template/src/components/base/BaseAccordion.vue +16 -13
  22. package/nuxt-base-template/src/components/base/BaseButton.vue +10 -13
  23. package/nuxt-base-template/src/components/base/BaseContainer.vue +1 -1
  24. package/nuxt-base-template/src/components/base/BaseContextMenuContainer.vue +61 -0
  25. package/nuxt-base-template/src/components/base/BaseInfinityList.vue +1 -1
  26. package/nuxt-base-template/src/components/base/BaseProgressbar.vue +28 -30
  27. package/nuxt-base-template/src/components/base/BaseToggle.vue +17 -10
  28. package/nuxt-base-template/src/components/form/FormInput.vue +34 -0
  29. package/nuxt-base-template/src/components/form/FormPassword.vue +47 -0
  30. package/nuxt-base-template/src/components/form/FormSelect.vue +40 -0
  31. package/nuxt-base-template/src/components/form/FormSubmit.vue +18 -0
  32. package/nuxt-base-template/src/components/form/FormTextarea.vue +36 -0
  33. package/nuxt-base-template/src/components/form/FormToggle.vue +27 -0
  34. package/nuxt-base-template/src/components/modal/Modal.vue +48 -0
  35. package/nuxt-base-template/src/components/modal/ModalConfirm.vue +38 -0
  36. package/nuxt-base-template/src/components/{base/BaseModalContainer.vue → modal/ModalContainer.vue} +1 -1
  37. package/nuxt-base-template/src/components/modal/ModalInfo.vue +22 -0
  38. package/nuxt-base-template/src/components/{ModalShare.vue → modal/ModalShare.vue} +7 -11
  39. package/nuxt-base-template/src/components/{base/BaseNotification.vue → notification/Notification.vue} +8 -7
  40. package/nuxt-base-template/src/components/{base/BaseNotificationContainer.vue → notification/NotificationContainer.vue} +11 -5
  41. package/nuxt-base-template/src/components/pwa/pwa-install-banner.vue +224 -0
  42. package/nuxt-base-template/src/components/transition/TransitionFade.vue +10 -7
  43. package/nuxt-base-template/src/components/transition/TransitionFadeScale.vue +10 -7
  44. package/nuxt-base-template/src/composables/use-auth-fetch.ts +22 -8
  45. package/nuxt-base-template/src/composables/use-context-menu.ts +19 -0
  46. package/nuxt-base-template/src/composables/use-file.ts +1 -2
  47. package/nuxt-base-template/src/composables/use-modal.ts +1 -1
  48. package/nuxt-base-template/src/composables/use-notification.ts +2 -2
  49. package/nuxt-base-template/src/composables/use-share.ts +3 -3
  50. package/nuxt-base-template/src/error.vue +10 -14
  51. package/nuxt-base-template/src/layouts/default.vue +13 -0
  52. package/nuxt-base-template/src/middleware/auth.global.ts +2 -2
  53. package/nuxt-base-template/src/pages/index.vue +31 -6
  54. package/nuxt-base-template/src/plugins/auth.server.ts +72 -0
  55. package/nuxt-base-template/src/plugins/form.plugin.ts +21 -0
  56. package/nuxt-base-template/src/plugins/pwa.plugin.ts +110 -0
  57. package/nuxt-base-template/src/tests/init.test.ts +1 -1
  58. package/nuxt-base-template/tailwind.config.js +33 -23
  59. package/nuxt-base-template/vitest.config.js +3 -3
  60. package/package.json +3 -1
  61. package/nuxt-base-template/formkit-theme.js +0 -137
  62. package/nuxt-base-template/formkit.config.js +0 -33
  63. package/nuxt-base-template/src/components/hello-world.vue +0 -10
  64. package/nuxt-base-template/src/composables/use-form-helper.ts +0 -100
  65. package/nuxt-base-template/src/composables/use-helper.ts +0 -52
  66. package/nuxt-base-template/src/forms/inputs/InputCheckbox.vue +0 -29
  67. package/nuxt-base-template/src/forms/inputs/InputFreeTags.vue +0 -98
  68. package/nuxt-base-template/src/forms/inputs/InputImage.vue +0 -65
  69. package/nuxt-base-template/src/forms/inputs/InputTags.vue +0 -112
  70. package/nuxt-base-template/src/forms/inputs/InputToggle.vue +0 -18
  71. package/nuxt-base-template/src/forms/plugins/asterisk-plugin.ts +0 -29
  72. package/nuxt-base-template/src/forms/plugins/scroll-error-plugin.ts +0 -36
  73. package/nuxt-base-template/src/forms/plugins/value-changes-plugin.ts +0 -13
  74. package/nuxt-base-template/src/plugins/4.auth.server.ts +0 -70
@@ -5,10 +5,15 @@
5
5
  "init": "npm install",
6
6
  "reinit": "rm -rf node_modules && rm -rf package-lock.json && yes | npx nuxt cleanup && npm cache clean --force && npm i",
7
7
  "build": "nuxt build",
8
- "build:test": "API_URL=123 nuxt build",
8
+ "build:develop": "npm run build",
9
+ "build:test": "npm run build",
9
10
  "start": "nuxt dev",
10
- "start:test": "API_URL=123 node .output/server/index.mjs",
11
- "generate-types": "GENERATE_TYPES=1 nuxt dev",
11
+ "start:develop": "node .output/server/index.mjs",
12
+ "start:test": "node .output/server/index.mjs",
13
+ "start:prod": "node .output/server/index.mjs",
14
+ "start:tunnel": "nuxt dev --tunnel",
15
+ "start:extern": "HOST=0.0.0.0 nuxt dev",
16
+ "generate-types": "rm -rf ./src/base && GENERATE_TYPES=1 nuxt dev",
12
17
  "dev": "nuxt dev",
13
18
  "generate": "nuxt generate",
14
19
  "preview": "nuxt preview",
@@ -20,40 +25,53 @@
20
25
  "postbuild": "cd .output/server/node_modules/tslib; npm pkg set 'exports[.].import.node'='./tslib.es6.mjs'"
21
26
  },
22
27
  "dependencies": {
23
- "@egoist/tailwindcss-icons": "1.2.0",
24
- "@formkit/icons": "1.1.0",
25
- "@formkit/nuxt": "1.1.0",
26
- "@iconify-json/bi": "1.1.20",
27
- "@lenne.tech/nuxt-base": "latest",
28
- "@vueuse/core": "10.4.1",
29
- "@vueuse/integrations": "10.4.1",
30
- "@vueuse/nuxt": "10.4.1",
31
- "pinia": "2.1.6",
32
- "tailwind-merge": "1.14.0"
28
+ "@egoist/tailwindcss-icons": "1.7.4",
29
+ "@iconify-json/bi": "1.1.23",
30
+ "@lenne.tech/nuxt-base": "3.4.0",
31
+ "@nuxt/image": "1.3.0",
32
+ "@vee-validate/yup": "4.12.5",
33
+ "@vueuse/core": "10.7.2",
34
+ "@vueuse/integrations": "10.7.2",
35
+ "@vueuse/nuxt": "10.7.2",
36
+ "ios-pwa-splash": "1.0.0",
37
+ "pinia": "2.1.7",
38
+ "tailwind-merge": "2.2.1",
39
+ "vee-validate": "4.12.5"
33
40
  },
34
41
  "devDependencies": {
35
- "@kevinmarrec/nuxt-pwa": "0.17.0",
36
- "@lenne.tech/eslint-config-vue": "0.0.10",
37
- "@nuxt/devtools": "0.8.5",
38
- "@nuxt/test-utils": "3.7.4",
39
- "@nuxtjs/color-mode": "3.3.0",
40
- "@nuxtjs/google-fonts": "3.0.2",
41
- "@nuxtjs/tailwindcss": "6.8.0",
42
- "@tailwindcss/forms": "0.5.6",
42
+ "@lenne.tech/eslint-config-vue": "0.0.16",
43
+ "@nuxt/devtools": "1.0.8",
44
+ "@nuxt/test-utils": "3.11.0",
45
+ "@nuxtjs/color-mode": "3.3.2",
46
+ "@nuxtjs/google-fonts": "3.1.3",
47
+ "@nuxtjs/plausible": "0.2.4",
48
+ "@nuxtjs/sitemap": "5.1.0",
49
+ "@nuxtjs/tailwindcss": "6.11.2",
50
+ "@playwright/test": "1.41.2",
51
+ "@tailwindcss/forms": "0.5.7",
43
52
  "@tailwindcss/typography": "0.5.10",
44
- "@vitejs/plugin-vue": "4.3.4",
45
- "@vue/test-utils": "2.4.1",
46
- "eslint": "8.50.0",
47
- "jsdom": "22.1.0",
48
- "nuxt": "3.7.4",
49
- "ts-loader": "9.4.4",
50
- "typescript": "5.2.2",
51
- "vitest": "0.34.2"
53
+ "@types/node": "20.11.16",
54
+ "@vitejs/plugin-vue": "5.0.3",
55
+ "@vue/test-utils": "2.4.4",
56
+ "eslint": "8.56.0",
57
+ "jsdom": "24.0.0",
58
+ "nuxt": "3.10.0",
59
+ "nuxt-simple-robots": "4.0.0-rc.14",
60
+ "ts-loader": "9.5.1",
61
+ "typescript": "5.3.3",
62
+ "vitest": "1.2.2"
52
63
  },
53
64
  "overrides": {
54
65
  "vue": "latest",
55
66
  "@nuxt/test-utils": {
56
67
  "vitest": "$vitest"
57
68
  }
69
+ },
70
+ "exports": {
71
+ ".": {
72
+ "import": {
73
+ "node": "./tslib.es6.mjs"
74
+ }
75
+ }
58
76
  }
59
77
  }
@@ -0,0 +1,77 @@
1
+ import { defineConfig, devices } from '@playwright/test';
2
+
3
+ /**
4
+ * Read environment variables from file.
5
+ * https://github.com/motdotla/dotenv
6
+ */
7
+ // require('dotenv').config();
8
+
9
+ /**
10
+ * See https://playwright.dev/docs/test-configuration.
11
+ */
12
+ export default defineConfig({
13
+ testDir: './e2e',
14
+ /* Run tests in files in parallel */
15
+ fullyParallel: true,
16
+ /* Fail the build on CI if you accidentally left test.only in the source code. */
17
+ forbidOnly: !!process.env.CI,
18
+ /* Retry on CI only */
19
+ retries: process.env.CI ? 2 : 0,
20
+ /* Opt out of parallel tests on CI. */
21
+ workers: process.env.CI ? 1 : undefined,
22
+ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
23
+ reporter: 'html',
24
+ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
25
+ use: {
26
+ /* Base URL to use in actions like `await page.goto('/')`. */
27
+ // baseURL: 'http://127.0.0.1:3000',
28
+
29
+ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
30
+ trace: 'on-first-retry',
31
+ },
32
+
33
+ /* Configure projects for major browsers */
34
+ projects: [
35
+ {
36
+ name: 'chromium',
37
+ use: { ...devices['Desktop Chrome'] },
38
+ },
39
+
40
+ {
41
+ name: 'firefox',
42
+ use: { ...devices['Desktop Firefox'] },
43
+ },
44
+
45
+ {
46
+ name: 'webkit',
47
+ use: { ...devices['Desktop Safari'] },
48
+ },
49
+
50
+ /* Test against mobile viewports. */
51
+ // {
52
+ // name: 'Mobile Chrome',
53
+ // use: { ...devices['Pixel 5'] },
54
+ // },
55
+ // {
56
+ // name: 'Mobile Safari',
57
+ // use: { ...devices['iPhone 12'] },
58
+ // },
59
+
60
+ /* Test against branded browsers. */
61
+ // {
62
+ // name: 'Microsoft Edge',
63
+ // use: { ...devices['Desktop Edge'], channel: 'msedge' },
64
+ // },
65
+ // {
66
+ // name: 'Google Chrome',
67
+ // use: { ...devices['Desktop Chrome'], channel: 'chrome' },
68
+ // },
69
+ ],
70
+
71
+ /* Run your local dev server before starting the tests */
72
+ // webServer: {
73
+ // command: 'npm run start',
74
+ // url: 'http://127.0.0.1:3000',
75
+ // reuseExistingServer: !process.env.CI,
76
+ // },
77
+ });
@@ -1,8 +1,8 @@
1
1
  <template>
2
2
  <NuxtLayout>
3
- <BaseModalContainer />
4
- <BaseNotificationContainer />
5
- <NuxtLoadingIndicator />
3
+ <ModalContainer />
4
+ <NotificationContainer />
5
+ <BaseContextMenuContainer />
6
6
  <NuxtPage />
7
7
  </NuxtLayout>
8
- </template>
8
+ </template>
@@ -1,40 +1,49 @@
1
1
  @tailwind base;
2
2
 
3
3
  @layer base {
4
- body {
5
- @apply transition-colors duration-300;
6
- }
7
- h1 {
8
- @apply text-[32px] leading-[140%] lg:text-[66px] lg:leading-[140%] font-bold;
9
- }
10
- h2 {
11
- @apply text-[28px] leading-[140%] lg:text-[51px] lg:leading-[140%] font-semibold;
12
- }
13
- h3 {
14
- @apply text-[21px] leading-[140%] lg:text-[39px] lg:leading-[140%] font-bold;
15
- }
16
- h4 {
17
- @apply text-[18px] leading-[140%] lg:text-[30px] lg:leading-[140%] font-bold;
18
- }
19
- h5 {
20
- @apply text-[15px] leading-[140%] lg:text-[23px] lg:leading-[140%] font-bold;
21
- }
22
- h6 {
23
- @apply text-[16px] leading-[140%] lg:text-[18px] lg:leading-[140%] font-bold;
24
- }
25
- p {
26
- @apply text-[15px] leading-[140%] lg:text-[18px] lg:leading-[140%];
27
- }
28
- small {
29
- @apply text-[14px] leading-[140%] lg:text-[16px] lg:leading-[140%];
30
- }
31
- a {
32
- @apply text-[15px] leading-[140%] lg:text-[18px] lg:leading-[140%];
33
- }
34
- button {
35
- @apply text-[15px] leading-[140%] lg:text-[18px] lg:leading-[140%] font-semibold;
36
- }
4
+ body {
5
+ @apply transition-colors duration-300;
6
+ }
7
+ h1 {
8
+ @apply text-[32px] leading-[140%] lg:text-[66px] lg:leading-[140%] font-bold;
9
+ }
10
+ h2 {
11
+ @apply text-[28px] leading-[140%] lg:text-[51px] lg:leading-[140%] font-semibold;
12
+ }
13
+ h3 {
14
+ @apply text-[21px] leading-[140%] lg:text-[39px] lg:leading-[140%] font-bold;
15
+ }
16
+ h4 {
17
+ @apply text-[18px] leading-[140%] lg:text-[30px] lg:leading-[140%] font-bold;
18
+ }
19
+ h5 {
20
+ @apply text-[15px] leading-[140%] lg:text-[23px] lg:leading-[140%] font-bold;
21
+ }
22
+ h6 {
23
+ @apply text-[16px] leading-[140%] lg:text-[18px] lg:leading-[140%] font-bold;
24
+ }
25
+ p {
26
+ @apply text-[15px] leading-[140%] lg:text-[18px] lg:leading-[140%];
27
+ }
28
+ small {
29
+ @apply text-[14px] leading-[140%] lg:text-[16px] lg:leading-[140%];
30
+ }
31
+ a {
32
+ @apply text-[15px] leading-[140%] lg:text-[18px] lg:leading-[140%];
33
+ }
34
+ button {
35
+ @apply text-[15px] leading-[140%] lg:text-[18px] lg:leading-[140%];
36
+ }
37
37
  }
38
38
 
39
39
  @tailwind components;
40
40
  @tailwind utilities;
41
+
42
+ html,
43
+ body {
44
+ height: 100%;
45
+ }
46
+
47
+ #__nuxt {
48
+ height: 100%;
49
+ }
@@ -13,4 +13,4 @@ const props = defineProps<{
13
13
  </span>
14
14
  <small class="text-sm font-light mt-2">{{ title }}</small>
15
15
  </button>
16
- </template>
16
+ </template>
@@ -2,19 +2,25 @@
2
2
  import { computed, ref } from 'vue';
3
3
  import { useElementSize } from '@vueuse/core';
4
4
 
5
- const props = withDefaults(defineProps<{
6
- expanded?: boolean;
7
- }>(), {
8
- expanded: false,
9
- });
5
+ const props = withDefaults(
6
+ defineProps<{
7
+ expanded?: boolean;
8
+ }>(),
9
+ {
10
+ expanded: false,
11
+ }
12
+ );
10
13
 
11
14
  const show = ref(false);
12
15
  const contents = ref<HTMLElement>();
13
16
 
14
- watch(() => props.expanded, () => {
15
- show.value = props.expanded;
16
- }, { immediate: true });
17
-
17
+ watch(
18
+ () => props.expanded,
19
+ () => {
20
+ show.value = props.expanded;
21
+ },
22
+ { immediate: true }
23
+ );
18
24
 
19
25
  const { height: targetHeight } = useElementSize(contents, undefined, {
20
26
  box: 'border-box',
@@ -31,10 +37,7 @@ const height = computed(() => (show.value ? targetHeight.value : 0));
31
37
  </div>
32
38
 
33
39
  <div class="relative ml-auto flex items-center justify-center">
34
- <span
35
- :class="{ 'rotate-180': show, 'rotate-45': !show }"
36
- class="i-bi-x h-5 w-5 text-gray-900 transition-transform duration-500 md:h-6 md:w-6 dark:bg-white"
37
- ></span>
40
+ <span :class="{ 'rotate-180': show, 'rotate-45': !show }" class="i-bi-x h-5 w-5 text-gray-900 transition-transform duration-500 md:h-6 md:w-6 dark:bg-white"></span>
38
41
  </div>
39
42
  </div>
40
43
 
@@ -8,9 +8,9 @@ const props = withDefaults(
8
8
  href?: string;
9
9
  to?: RouteLocationRaw;
10
10
  appearance?: 'regular' | 'outline' | 'none';
11
- size?: 'sm' | 'md' | 'lg' | 'auto' | 'calendar';
12
- color?: 'primary' | 'secondary' | 'green' | 'yellow' | 'lightgrey' | 'lightprimary' | 'danger';
13
- textColor?: 'white' | 'black' | 'primary' | 'fullblack' | 'gray' | '';
11
+ size?: 'sm' | 'md' | 'lg' | 'auto';
12
+ color?: 'primary' | 'secondary' | 'green' | 'yellow' | 'lightprimary' | 'danger';
13
+ textColor?: 'white' | 'black' | 'primary' | 'gray' | '';
14
14
  type?: 'submit' | 'button';
15
15
  loading?: boolean;
16
16
  loadingText?: string;
@@ -25,20 +25,19 @@ const props = withDefaults(
25
25
  loading: false,
26
26
  loadingText: 'Loading',
27
27
  block: false,
28
- },
28
+ }
29
29
  );
30
30
 
31
31
  const appearanceClasses: Record<typeof props.appearance, string> = {
32
- regular: 'rounded-full text-white',
32
+ regular: 'rounded-md text-white',
33
33
  outline:
34
- 'rounded-full border border-primary bg-white hover:bg-primary-500 text-primary hover:text-white disabled:bg-transparent disabled:text-gray-400 disabled:border-gray-200',
35
- none: 'bg-transparent border-transparent hover:text-primary-500 text-black hover:bg-transparent',
34
+ 'rounded-md border border-primary bg-background hover:bg-primary-500 text-primary hover:text-white disabled:bg-transparent disabled:text-gray-400 disabled:border-gray-200',
35
+ none: 'bg-transparent border-transparent hover:text-primary-500 text-foreground hover:bg-transparent',
36
36
  };
37
37
 
38
38
  const sizeClasses: Record<typeof props.size, string> = {
39
- sm: 'min-w-[110px] py-1.5 px-4 text-sm',
40
- calendar: 'min-w-[23%] py-1.5 px-5 text-base',
41
- md: 'min-w-[200px] py-2 px-3 text-base',
39
+ sm: 'min-w-[100px] py-1.5 px-1.5 text-sm',
40
+ md: 'min-w-[100px] py-1.5 px-2 text-base',
42
41
  lg: 'min-w-[240px] py-3 px-4 text-lg',
43
42
  auto: 'text-sm lg:text-lg',
44
43
  };
@@ -49,14 +48,12 @@ const colorClasses: Record<typeof props.color, string> = {
49
48
  green: 'bg-green-500 hover:bg-green-400 text-green-50',
50
49
  yellow: 'bg-yellow-500 hover:bg-yellow-400 text-yellow-950',
51
50
  danger: 'bg-red-500 hover:bg-red-400 text-red-950',
52
- lightgrey: 'bg-slate-100 text-slate-200',
53
51
  lightprimary: 'bg-primary-300 text-primary-50',
54
52
  };
55
53
 
56
54
  const textColorClasses: Record<typeof props.textColor, string> = {
57
55
  white: 'text-white',
58
- black: 'text-black dark:text-white',
59
- fullblack: 'text-black dark:text-black',
56
+ black: 'text-black',
60
57
  primary: 'text-primary',
61
58
  gray: 'text-gray-400',
62
59
  '': '',
@@ -2,4 +2,4 @@
2
2
  <div class="w-full px-4 max-w-7xl mx-auto">
3
3
  <slot></slot>
4
4
  </div>
5
- </template>
5
+ </template>
@@ -0,0 +1,61 @@
1
+ <script setup lang="ts">
2
+ import { useMouse } from '@vueuse/core';
3
+
4
+ import { useContextMenu } from '~/composables/use-context-menu';
5
+
6
+ const { activeMenu, close } = useContextMenu();
7
+ const target = ref();
8
+ const isLeft = usePageLeave();
9
+
10
+ onClickOutside(target, () => close());
11
+
12
+ const { x, y } = useMouse();
13
+ const left = ref(0);
14
+ const top = ref(0);
15
+
16
+ watch(
17
+ () => isLeft.value,
18
+ () => {
19
+ close();
20
+ }
21
+ );
22
+
23
+ watch(
24
+ () => activeMenu.value,
25
+ () => {
26
+ if (activeMenu.value?.show) {
27
+ left.value = unref(x.value);
28
+ top.value = unref(y.value);
29
+ }
30
+ }
31
+ );
32
+ </script>
33
+
34
+ <template>
35
+ <ClientOnly>
36
+ <Teleport to="body">
37
+ <TransitionFade leave-duration="150" start-duration="150">
38
+ <div
39
+ v-show="activeMenu"
40
+ @mouseleave="close()"
41
+ ref="target"
42
+ class="absolute w-56 right-0 origin-top-right border border-border bg-background overflow-hidden shadow-lg rounded-md focus:outline-none"
43
+ :style="`top: ${top}px; left: ${left}px`"
44
+ >
45
+ <button
46
+ v-for="item of activeMenu?.items"
47
+ :key="item"
48
+ type="button"
49
+ class="w-full text-left text-foreground px-4 py-2 text-sm hover:bg-hover hover:text-foreground flex justify-between items-center"
50
+ @click="
51
+ item.click();
52
+ close();
53
+ "
54
+ >
55
+ {{ item.label }}
56
+ </button>
57
+ </div>
58
+ </TransitionFade>
59
+ </Teleport>
60
+ </ClientOnly>
61
+ </template>
@@ -31,4 +31,4 @@ onUnmounted(() => {
31
31
  <div ref="scrollContainer">
32
32
  <slot></slot>
33
33
  </div>
34
- </template>
34
+ </template>
@@ -1,39 +1,42 @@
1
1
  <script setup lang="ts">
2
- const props = withDefaults(defineProps<{
3
- percent: number | `${number}`;
4
- duration?: number | `${number}`;
5
- label?: string;
6
- color?: 'primary' | 'red' | 'green' | 'blue';
7
- }>(), {
8
- duration: 400,
9
- color: 'primary',
10
- });
2
+ const props = withDefaults(
3
+ defineProps<{
4
+ percent: number | `${number}`;
5
+ duration?: number | `${number}`;
6
+ label?: string;
7
+ color?: 'primary' | 'red' | 'green' | 'blue';
8
+ }>(),
9
+ {
10
+ duration: 400,
11
+ color: 'primary',
12
+ }
13
+ );
11
14
 
12
15
  type RecordFromUnion<T extends string> = {
13
- [K in T]: string
16
+ [K in T]: string;
14
17
  };
15
18
 
16
19
  type ColorTypes = 'text' | 'background' | 'background-light';
17
20
 
18
21
  const colorClass: Record<typeof props.color, RecordFromUnion<ColorTypes>> = {
19
- 'primary': {
20
- 'text': 'text-primary-600',
21
- 'background': 'bg-primary-500',
22
+ primary: {
23
+ text: 'text-primary-600',
24
+ background: 'bg-primary-500',
22
25
  'background-light': 'bg-gray-200',
23
26
  },
24
- 'red': {
25
- 'text': 'text-red-600',
26
- 'background': 'bg-red-500',
27
+ red: {
28
+ text: 'text-red-600',
29
+ background: 'bg-red-500',
27
30
  'background-light': 'bg-red-200',
28
31
  },
29
- 'green': {
30
- 'text': 'text-green-600',
31
- 'background': 'bg-green-500',
32
+ green: {
33
+ text: 'text-green-600',
34
+ background: 'bg-green-500',
32
35
  'background-light': 'bg-green-200',
33
36
  },
34
- 'blue': {
35
- 'text': 'text-blue-600',
36
- 'background': 'bg-blue-500',
37
+ blue: {
38
+ text: 'text-blue-600',
39
+ background: 'bg-blue-500',
37
40
  'background-light': 'bg-blue-200',
38
41
  },
39
42
  };
@@ -43,17 +46,12 @@ const colorClass: Record<typeof props.color, RecordFromUnion<ColorTypes>> = {
43
46
  <div :style="`--percent: ${props.percent}%; --duration: ${props.duration}ms;`" class="relative pt-1 w-full">
44
47
  <div v-if="props.label" class="flex mb-2 items-center justify-between">
45
48
  <div>
46
- <span
47
- :class="[colorClass[props.color].text, colorClass[props.color]['background-light']]"
48
- class="text-xs font-semibold inline-block py-1 px-2 uppercase rounded-full"
49
- >
49
+ <span :class="[colorClass[props.color].text, colorClass[props.color]['background-light']]" class="text-xs font-semibold inline-block py-1 px-2 uppercase rounded-full">
50
50
  {{ props.label }}
51
51
  </span>
52
52
  </div>
53
53
  <div class="text-right">
54
- <span :class="[colorClass[props.color].text]" class="text-xs font-semibold inline-block ">
55
- {{ (+props.percent).toFixed(0) }}%
56
- </span>
54
+ <span :class="[colorClass[props.color].text]" class="text-xs font-semibold inline-block"> {{ (+props.percent).toFixed(0) }}% </span>
57
55
  </div>
58
56
  </div>
59
57
  <div :class="colorClass[props.color]['background-light']" class="overflow-hidden h-1 mb-4 text-xs flex rounded bg-[--color-light]">
@@ -63,4 +61,4 @@ const colorClass: Record<typeof props.color, RecordFromUnion<ColorTypes>> = {
63
61
  ></div>
64
62
  </div>
65
63
  </div>
66
- </template>
64
+ </template>
@@ -1,20 +1,27 @@
1
1
  <script setup lang="ts">
2
- const props = withDefaults(defineProps<{
3
- active?: boolean;
4
- }>(), {
5
- active: false,
6
- });
2
+ const props = withDefaults(
3
+ defineProps<{
4
+ active?: boolean;
5
+ }>(),
6
+ {
7
+ active: false,
8
+ }
9
+ );
7
10
  const toggleActive = ref(false);
8
11
 
9
- watch(() => props.active, () => {
10
- toggleActive.value = props.active;
11
- }, { immediate: true });
12
+ watch(
13
+ () => props.active,
14
+ () => {
15
+ toggleActive.value = props.active;
16
+ },
17
+ { immediate: true }
18
+ );
12
19
  </script>
13
20
 
14
21
  <template>
15
- <div class="flex justify-between items-center" @click="toggleActive = !toggleActive">
22
+ <div class="flex justify-between items-center cursor-pointer" @click="toggleActive = !toggleActive">
16
23
  <div class="w-8 h-5 flex items-center bg-gray-300 rounded-full p-1 duration-300 ease-in-out" :class="{ 'bg-green-400': toggleActive }">
17
24
  <div class="bg-white w-4 h-4 rounded-full shadow-md transform duration-300 ease-in-out" :class="{ 'translate-x-2': toggleActive }"></div>
18
25
  </div>
19
26
  </div>
20
- </template>
27
+ </template>
@@ -0,0 +1,34 @@
1
+ <script setup lang="ts">
2
+ import { ErrorMessage, useField } from 'vee-validate';
3
+
4
+ const props = defineProps<{
5
+ disabled?: boolean;
6
+ label?: string;
7
+ name: string;
8
+ placeholder?: string;
9
+ type: string;
10
+ }>();
11
+
12
+ const { handleBlur, handleChange, value, meta, setTouched } = useField(() => props.name);
13
+ </script>
14
+
15
+ <template>
16
+ <div class="mt-3">
17
+ <label v-if="label" :for="name" class="block text-sm font-medium leading-6 text-foreground">{{ label }}{{ meta.required ? '*' : '' }}</label>
18
+ <div class="relative mt-2 pb-2">
19
+ <input
20
+ :id="name"
21
+ :type="type"
22
+ :name="name"
23
+ v-model="value"
24
+ :disabled="disabled"
25
+ @blur="handleBlur"
26
+ @focus="setTouched(true)"
27
+ @change="handleChange"
28
+ class="bg-background block w-full rounded-md border-0 py-1.5 text-foreground shadow-sm ring-1 ring-inset ring-border placeholder:text-foreground/50 focus:ring-1 focus:ring-inset focus:ring-primary-500 sm:text-sm sm:leading-6"
29
+ :placeholder="placeholder"
30
+ />
31
+ <ErrorMessage class="absolute -bottom-2.5 text-xs font-light text-red-600" :name="name" />
32
+ </div>
33
+ </div>
34
+ </template>