@code-coaching/vuetiful 0.14.2 → 0.15.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 (29) hide show
  1. package/README.md +0 -3
  2. package/dist/style.css +2 -2
  3. package/dist/styles/all.css +172 -95
  4. package/dist/types/components/atoms/VAvatar.vue.d.ts +69 -0
  5. package/dist/types/{utils/dark-mode/dark-mode.vue.d.ts → components/atoms/VLightSwitch.vue.d.ts} +1 -1
  6. package/dist/types/components/atoms/index.d.ts +4 -1
  7. package/dist/types/services/highlight.service.test.d.ts +1 -0
  8. package/dist/types/services/index.d.ts +3 -1
  9. package/dist/types/utils/index.d.ts +1 -4
  10. package/dist/vuetiful.es.mjs +3298 -3244
  11. package/dist/vuetiful.umd.js +14 -15
  12. package/package.json +1 -1
  13. package/src/components/atoms/VAvatar.test.ts +105 -0
  14. package/src/components/atoms/VAvatar.vue +51 -0
  15. package/src/{utils/code-block → components/atoms}/VCodeBlock.vue +1 -1
  16. package/src/{utils/dark-mode/dark-mode.vue → components/atoms/VLightSwitch.vue} +1 -2
  17. package/src/components/atoms/index.ts +6 -0
  18. package/src/{utils/dark-mode → services}/dark-mode.service.test.ts +11 -11
  19. package/src/{utils/dark-mode → services}/dark-mode.service.ts +1 -1
  20. package/src/services/index.ts +3 -1
  21. package/src/utils/index.ts +1 -4
  22. package/src/utils/theme/theme-switcher.vue +10 -15
  23. /package/dist/types/{utils/code-block/highlight.service.test.d.ts → components/atoms/VAvatar.test.d.ts} +0 -0
  24. /package/dist/types/{utils/code-block → components/atoms}/VCodeBlock.vue.d.ts +0 -0
  25. /package/dist/types/{utils/dark-mode → services}/dark-mode.service.d.ts +0 -0
  26. /package/dist/types/{utils/dark-mode → services}/dark-mode.service.test.d.ts +0 -0
  27. /package/dist/types/{utils/code-block → services}/highlight.service.d.ts +0 -0
  28. /package/src/{utils/code-block → services}/highlight.service.test.ts +0 -0
  29. /package/src/{utils/code-block → services}/highlight.service.ts +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-coaching/vuetiful",
3
- "version": "0.14.2",
3
+ "version": "0.15.1",
4
4
  "license": "MIT",
5
5
  "scripts": {
6
6
  "dev": "onchange 'src/**/*.vue' 'src/**/*.ts' 'src/**/*.css' -- npm run build",
@@ -0,0 +1,105 @@
1
+ import { mount } from "@vue/test-utils";
2
+ import { describe, expect, test } from "vitest";
3
+ import { VAvatar } from ".";
4
+
5
+ test("VAvatar", () => {
6
+ expect(VAvatar).toBeTruthy();
7
+ });
8
+
9
+ describe("VAvatar props", () => {
10
+ test("VAvatar defaults", () => {
11
+ const wrapper = mount(VAvatar);
12
+
13
+ expect(wrapper.classes()).toContain("avatar");
14
+ expect(wrapper.classes()).toContain("w-16");
15
+ expect(wrapper.classes()).toContain("rounded-token");
16
+
17
+ const avatarText = wrapper.find(".avatar-text");
18
+ expect(avatarText.exists()).toBe(true);
19
+ expect(avatarText.text()).toBe("");
20
+ expect(avatarText.classes()).toContain("dark:fill-on-surface-token");
21
+ expect(avatarText.classes()).toContain("fill-base-token");
22
+ });
23
+
24
+ test("VAvatar initials", () => {
25
+ const wrapper = mount(VAvatar, {
26
+ props: {
27
+ initials: "JD",
28
+ class: "variant-filled",
29
+ },
30
+ });
31
+
32
+ expect(wrapper.classes()).toContain("avatar");
33
+ expect(wrapper.classes()).toContain("w-16");
34
+ expect(wrapper.classes()).toContain("rounded-token");
35
+
36
+ const avatarText = wrapper.find(".avatar-text");
37
+ expect(avatarText.exists()).toBe(true);
38
+ expect(avatarText.text()).toBe("JD");
39
+ expect(avatarText.classes()).toContain("dark:fill-base-token");
40
+ expect(avatarText.classes()).toContain("fill-on-surface-token");
41
+
42
+ const avatarImage = wrapper.find(".avatar-image");
43
+ expect(avatarImage.exists()).toBe(false);
44
+ });
45
+
46
+ test("VAvatar initials fill", () => {
47
+ const wrapper = mount(VAvatar, {
48
+ props: {
49
+ initials: "JD",
50
+ fill: "custom-fill-class",
51
+ },
52
+ });
53
+
54
+ expect(wrapper.classes()).toContain("avatar");
55
+ expect(wrapper.classes()).toContain("w-16");
56
+ expect(wrapper.classes()).toContain("rounded-token");
57
+
58
+ const avatarText = wrapper.find(".avatar-text");
59
+ expect(avatarText.exists()).toBe(true);
60
+ expect(avatarText.text()).toBe("JD");
61
+ expect(avatarText.classes()).toContain("custom-fill-class");
62
+ });
63
+
64
+ test("VAvatar image", () => {
65
+ const wrapper = mount(VAvatar, {
66
+ props: {
67
+ src: "https://via.placeholder.com/150",
68
+ },
69
+ });
70
+
71
+ expect(wrapper.classes()).toContain("avatar");
72
+ expect(wrapper.classes()).toContain("w-16");
73
+ expect(wrapper.classes()).toContain("rounded-token");
74
+
75
+ const avatarText = wrapper.find(".avatar-text");
76
+ expect(avatarText.exists()).toBe(false);
77
+
78
+ const avatarImage = wrapper.find(".avatar-image");
79
+ expect(avatarImage.exists()).toBe(true);
80
+ expect(avatarImage.attributes("src")).toBe("https://via.placeholder.com/150");
81
+ });
82
+
83
+ test("VAvatar image fallback", async () => {
84
+ const wrapper = mount(VAvatar, {
85
+ props: {
86
+ src: "https://via.placeholder.com/150",
87
+ fallback: "/image/john-duck.png",
88
+ },
89
+ });
90
+
91
+ expect(wrapper.classes()).toContain("avatar");
92
+ expect(wrapper.classes()).toContain("w-16");
93
+ expect(wrapper.classes()).toContain("rounded-token");
94
+
95
+ const avatarText = wrapper.find(".avatar-text");
96
+ expect(avatarText.exists()).toBe(false);
97
+
98
+ const avatarImage = wrapper.find(".avatar-image");
99
+ avatarImage.trigger("error");
100
+ await wrapper.vm.$nextTick();
101
+
102
+ expect(avatarImage.exists()).toBe(true);
103
+ expect(avatarImage.attributes("src")).toBe("/image/john-duck.png");
104
+ });
105
+ });
@@ -0,0 +1,51 @@
1
+ <script setup lang="ts">
2
+ import { CssClasses } from "@/index";
3
+ import { computed, ref, useAttrs } from "vue";
4
+
5
+ const props = defineProps({
6
+ // Initials
7
+ initials: { type: String, default: "" },
8
+ fill: { type: String as () => CssClasses, default: "" },
9
+
10
+ // Image
11
+ src: { type: String, default: "" },
12
+ alt: { type: String, default: "" },
13
+ fallback: { type: String, default: "" },
14
+
15
+ width: { type: String as () => CssClasses, default: "w-16" },
16
+ rounded: { type: String as () => CssClasses, default: "rounded-token" },
17
+ });
18
+
19
+ const imgSrc = ref(props.src);
20
+ const fillInitials = computed(() => {
21
+ if (props.fill) return props.fill;
22
+
23
+ const attrs = useAttrs();
24
+ const classString = attrs.class as string | undefined;
25
+
26
+ if (classString?.includes("variant-filled")) {
27
+ return "fill-on-surface-token dark:fill-base-token";
28
+ }
29
+ return "dark:fill-on-surface-token fill-base-token";
30
+ });
31
+ </script>
32
+ <template>
33
+ <figure
34
+ :class="`avatar isolate flex aspect-square items-center justify-center overflow-hidden font-semibold ${rounded} ${width}`"
35
+ >
36
+ <img class="avatar-image" v-if="src" :src="imgSrc" :alt="alt" @error="() => (imgSrc = fallback)" />
37
+ <svg v-else class="avatar-initials h-full w-full" viewBox="0 0 512 512">
38
+ <text
39
+ x="50%"
40
+ y="50%"
41
+ dominant-baseline="central"
42
+ text-anchor="middle"
43
+ font-weight="bold"
44
+ :font-size="150"
45
+ :class="`avatar-text ${fillInitials}`"
46
+ >
47
+ {{ String(initials).substring(0, 2).toUpperCase() }}
48
+ </text>
49
+ </svg>
50
+ </figure>
51
+ </template>
@@ -1,8 +1,8 @@
1
1
  <script setup lang="ts">
2
2
  import { CssClasses, vClipboard } from "@/index";
3
+ import { useHighlight } from "@/services/highlight.service";
3
4
  import "highlight.js/styles/github-dark.css";
4
5
  import { ref } from "vue";
5
- import { useHighlight } from "./highlight.service";
6
6
 
7
7
  const { highlight } = useHighlight();
8
8
 
@@ -24,9 +24,8 @@
24
24
  </template>
25
25
 
26
26
  <script lang="ts">
27
- import type { CssClasses } from "@/index";
27
+ import { CssClasses, useDarkMode } from "@/index";
28
28
  import { computed, ComputedRef, defineComponent, onMounted } from "vue";
29
- import { useDarkMode } from "./dark-mode.service";
30
29
 
31
30
  export default defineComponent({
32
31
  props: {
@@ -1,6 +1,9 @@
1
+ import VAvatar from "./VAvatar.vue";
1
2
  import VBadge from "./VBadge.vue";
2
3
  import VButton from "./VButton.vue";
3
4
  import VChip from "./VChip.vue";
5
+ import VCodeBlock from "./VCodeBlock.vue";
6
+ import VLightSwitch from "./VLightSwitch.vue";
4
7
 
5
8
  import VRadioDescription from "./VRadio/VRadioDescription.vue";
6
9
  import VRadioGroup from "./VRadio/VRadioGroup.vue";
@@ -13,9 +16,12 @@ import VSwitchGroup from "./VSwitch/VSwitchGroup.vue";
13
16
  import VSwitchLabel from "./VSwitch/VSwitchLabel.vue";
14
17
 
15
18
  export {
19
+ VAvatar,
16
20
  VButton,
17
21
  VBadge,
18
22
  VChip,
23
+ VCodeBlock,
24
+ VLightSwitch,
19
25
  VRadioGroup,
20
26
  VRadioItem,
21
27
  VRadioLabel,
@@ -13,7 +13,7 @@ describe("useDarkMode", () => {
13
13
  describe("getModeUserPrefers", () => {
14
14
  describe("given not in browser", () => {
15
15
  it("should return default modeUserPrefers", async () => {
16
- const platform = await import("../platform/platform.service");
16
+ const platform = await import("../utils/platform/platform.service");
17
17
  vi.spyOn(platform, "usePlatform").mockReturnValueOnce({ isBrowser: false });
18
18
 
19
19
  const { useDarkMode } = await import("./dark-mode.service");
@@ -25,7 +25,7 @@ describe("useDarkMode", () => {
25
25
  describe("given in browser", () => {
26
26
  describe("given no modeUserPrefers in localStorage", () => {
27
27
  it("should return default modeUserPrefers", async () => {
28
- const platform = await import("../platform/platform.service");
28
+ const platform = await import("../utils/platform/platform.service");
29
29
  vi.spyOn(platform, "usePlatform").mockReturnValueOnce({ isBrowser: true });
30
30
 
31
31
  const { useDarkMode } = await import("./dark-mode.service");
@@ -39,7 +39,7 @@ describe("useDarkMode", () => {
39
39
  });
40
40
  describe("given modeUserPrefers in localStorage", () => {
41
41
  it("should return the value", async () => {
42
- const platform = await import("../platform/platform.service");
42
+ const platform = await import("../utils/platform/platform.service");
43
43
  vi.spyOn(platform, "usePlatform").mockReturnValueOnce({ isBrowser: true });
44
44
 
45
45
  const { useDarkMode } = await import("./dark-mode.service");
@@ -57,7 +57,7 @@ describe("useDarkMode", () => {
57
57
  describe("getModeOsPrefers", () => {
58
58
  describe("given not in browser", () => {
59
59
  it("should return default modeOsPrefers", async () => {
60
- const platform = await import("../platform/platform.service");
60
+ const platform = await import("../utils/platform/platform.service");
61
61
  vi.spyOn(platform, "usePlatform").mockReturnValueOnce({ isBrowser: false });
62
62
 
63
63
  const { useDarkMode } = await import("./dark-mode.service");
@@ -69,7 +69,7 @@ describe("useDarkMode", () => {
69
69
  describe("given in browser", () => {
70
70
  describe("given prefers-color-scheme: light", () => {
71
71
  it("should return true", async () => {
72
- const platform = await import("../platform/platform.service");
72
+ const platform = await import("../utils/platform/platform.service");
73
73
  vi.spyOn(platform, "usePlatform").mockReturnValueOnce({ isBrowser: true });
74
74
  const localStorageSpy = vi.spyOn(window.localStorage, "setItem");
75
75
 
@@ -83,7 +83,7 @@ describe("useDarkMode", () => {
83
83
  });
84
84
  describe("given prefers-color-scheme: dark", () => {
85
85
  it("should return false", async () => {
86
- const platform = await import("../platform/platform.service");
86
+ const platform = await import("../utils/platform/platform.service");
87
87
  vi.spyOn(platform, "usePlatform").mockReturnValueOnce({ isBrowser: true });
88
88
  const localStorageSpy = vi.spyOn(window.localStorage, "setItem");
89
89
 
@@ -101,7 +101,7 @@ describe("useDarkMode", () => {
101
101
  describe("getModeAutoPrefers", () => {
102
102
  describe("given not in browser", () => {
103
103
  it("should return default modeAutoPrefers", async () => {
104
- const platform = await import("../platform/platform.service");
104
+ const platform = await import("../utils/platform/platform.service");
105
105
  vi.spyOn(platform, "usePlatform").mockReturnValueOnce({ isBrowser: false });
106
106
 
107
107
  const { useDarkMode } = await import("./dark-mode.service");
@@ -113,7 +113,7 @@ describe("useDarkMode", () => {
113
113
  describe("given in browser", () => {
114
114
  describe("given no modeUserPrefers in localStorage", () => {
115
115
  it("should return default modeAutoPrefers", async () => {
116
- const platform = await import("../platform/platform.service");
116
+ const platform = await import("../utils/platform/platform.service");
117
117
  vi.spyOn(platform, "usePlatform").mockReturnValueOnce({ isBrowser: true });
118
118
 
119
119
  const { useDarkMode } = await import("./dark-mode.service");
@@ -127,7 +127,7 @@ describe("useDarkMode", () => {
127
127
  });
128
128
  describe("given modeUserPrefers in localStorage", () => {
129
129
  it("should return the value", async () => {
130
- const platform = await import("../platform/platform.service");
130
+ const platform = await import("../utils/platform/platform.service");
131
131
  vi.spyOn(platform, "usePlatform").mockReturnValueOnce({ isBrowser: true });
132
132
 
133
133
  const { useDarkMode } = await import("./dark-mode.service");
@@ -145,7 +145,7 @@ describe("useDarkMode", () => {
145
145
  describe("setModeUserPrefers", () => {
146
146
  describe("given not in browser", () => {
147
147
  it("should set modeUserPrefers", async () => {
148
- const platform = await import("../platform/platform.service");
148
+ const platform = await import("../utils/platform/platform.service");
149
149
  vi.spyOn(platform, "usePlatform").mockReturnValueOnce({ isBrowser: false });
150
150
  vi.spyOn(window.localStorage, "setItem");
151
151
 
@@ -160,7 +160,7 @@ describe("useDarkMode", () => {
160
160
 
161
161
  describe("given in browser", () => {
162
162
  it("should set modeUserPrefers and localStorage", async () => {
163
- const platform = await import("../platform/platform.service");
163
+ const platform = await import("../utils/platform/platform.service");
164
164
  vi.spyOn(platform, "usePlatform").mockReturnValueOnce({ isBrowser: true });
165
165
 
166
166
  const { useDarkMode } = await import("./dark-mode.service");
@@ -1,5 +1,5 @@
1
1
  import { readonly, Ref, ref } from "vue";
2
- import { usePlatform } from "../platform/platform.service";
2
+ import { usePlatform } from "../utils/platform/platform.service";
3
3
 
4
4
  const { isBrowser } = usePlatform();
5
5
 
@@ -1,4 +1,6 @@
1
1
  import { useRail } from "./rail.service";
2
2
  import { useDrawer } from "./drawer.service";
3
+ import { useHighlight } from "./highlight.service";
4
+ import { useDarkMode } from "./dark-mode.service";
3
5
 
4
- export { useRail, useDrawer };
6
+ export { useRail, useDrawer, useHighlight, useDarkMode };
@@ -1,7 +1,4 @@
1
- import VCodeBlock from "./code-block/VCodeBlock.vue";
2
- import { useDarkMode } from "./dark-mode/dark-mode.service";
3
- import DarkModeSwitch from "./dark-mode/dark-mode.vue";
4
1
  import ThemeSwitcher from "./theme/theme-switcher.vue";
5
2
  import { useTheme } from "./theme/theme.service";
6
3
 
7
- export { DarkModeSwitch, ThemeSwitcher, useDarkMode, useTheme, VCodeBlock };
4
+ export { ThemeSwitcher, useTheme };
@@ -4,11 +4,15 @@
4
4
  Theme
5
5
  </VButton>
6
6
 
7
- <div v-if="showPopup" class="cc-theme-switcher__popup p-4 shadow-xl" :class="classes">
7
+ <div
8
+ v-if="showPopup"
9
+ class="cc-theme-switcher__popup absolute z-10 mt-1 p-4 shadow-xl"
10
+ :class="classes"
11
+ >
8
12
  <div class="space-y-4">
9
13
  <section class="flex items-center justify-between">
10
14
  <h6>Mode</h6>
11
- <DarkModeSwitch />
15
+ <v-light-switch />
12
16
  </section>
13
17
  <nav class="list-nav -m-4 overflow-y-auto p-4" :class="listClasses">
14
18
  <ul class="flex flex-col gap-4">
@@ -33,12 +37,12 @@
33
37
  </template>
34
38
 
35
39
  <script lang="ts">
36
- import { CssClasses, DarkModeSwitch, useDarkMode, useTheme, VButton } from "@/index";
40
+ import { CssClasses, useDarkMode, useTheme, VButton, VLightSwitch } from "@/index";
37
41
  import { computed, defineComponent, onMounted, ref } from "vue";
38
42
 
39
43
  export default defineComponent({
40
44
  components: {
41
- DarkModeSwitch,
45
+ VLightSwitch,
42
46
  VButton,
43
47
  },
44
48
  props: {
@@ -83,7 +87,7 @@ export default defineComponent({
83
87
  default: "w-24",
84
88
  },
85
89
  },
86
- setup(props, { attrs }) {
90
+ setup(props) {
87
91
  const { initializeTheme, loadTheme, themes, chosenTheme } = useTheme();
88
92
  const { currentMode, MODE } = useDarkMode();
89
93
 
@@ -116,8 +120,7 @@ export default defineComponent({
116
120
  ${text.value}
117
121
  ${props.width}
118
122
  ${props.ring}
119
- ${props.roundedContainer}
120
- ${attrs.class ?? ""}`;
123
+ ${props.roundedContainer}`;
121
124
  });
122
125
 
123
126
  const listClasses = computed(() => {
@@ -145,11 +148,3 @@ export default defineComponent({
145
148
  },
146
149
  });
147
150
  </script>
148
-
149
- <style scoped>
150
- .cc-theme-switcher__popup {
151
- position: absolute;
152
- z-index: 1;
153
- margin-top: 0.25rem;
154
- }
155
- </style>