@code-coaching/vuetiful 0.23.2 → 0.24.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 (36) hide show
  1. package/dist/types/components/atoms/VLightSwitch.vue.d.ts +5 -1
  2. package/dist/types/services/dark-mode.service.d.ts +13 -13
  3. package/dist/types/services/index.d.ts +2 -2
  4. package/dist/types/utils/colors/colors.service.d.ts +69 -0
  5. package/dist/types/utils/index.d.ts +5 -1
  6. package/dist/types/utils/theme/theme.service.d.ts +12 -23
  7. package/dist/types/utils/theme/themes.d.ts +39 -0
  8. package/dist/vuetiful.es.mjs +452 -146
  9. package/dist/vuetiful.umd.js +71 -16
  10. package/package.json +1 -1
  11. package/src/components/atoms/VLightSwitch.test.ts +61 -12
  12. package/src/components/atoms/VLightSwitch.vue +13 -19
  13. package/src/components/molecules/VTabs/VTab.test.ts +21 -0
  14. package/src/directives/clipboard.test.ts +2 -2
  15. package/src/services/dark-mode.service.test.ts +58 -210
  16. package/src/services/dark-mode.service.ts +32 -51
  17. package/src/services/drawer.service.test.ts +4 -4
  18. package/src/services/highlight.service.test.ts +3 -3
  19. package/src/services/index.ts +2 -2
  20. package/src/services/rail.service.test.ts +2 -2
  21. package/src/utils/colors/colors.service.ts +293 -0
  22. package/src/utils/index.ts +5 -1
  23. package/src/utils/platform/platform.service.test.ts +3 -3
  24. package/src/utils/theme/callback.test.ts +9 -5
  25. package/src/utils/theme/remove.test.ts +7 -5
  26. package/src/utils/theme/theme-switcher.vue +34 -37
  27. package/src/utils/theme/theme.service.test.ts +160 -58
  28. package/src/utils/theme/theme.service.ts +140 -78
  29. package/src/utils/theme/themes.ts +127 -0
  30. package/dist/types/components/index.test.d.ts +0 -1
  31. package/dist/types/index.test.d.ts +0 -1
  32. package/dist/types/utils/index.test.d.ts +0 -1
  33. package/src/components/index.test.ts +0 -10
  34. package/src/index.test.ts +0 -26
  35. package/src/utils/index.test.ts +0 -11
  36. /package/src/themes/{theme-vuetiful-0.0.1.css → theme-vuetiful.css} +0 -0
@@ -1,4 +1,4 @@
1
- import { defineComponent, ref, computed, openBlock, createElementBlock, normalizeClass, unref, createElementVNode, toDisplayString, readonly, reactive, renderSlot, createBlock, resolveDynamicComponent, withCtx, onMounted, Fragment, cloneVNode, h as h$1, inject, provide, watchEffect, onUnmounted, watch, nextTick, toRaw, createCommentVNode, toRefs, createVNode, Transition, renderList, createTextVNode, pushScopeId, popScopeId, isRef, withDirectives, vShow, useAttrs } from "vue";
1
+ import { defineComponent, ref, computed, openBlock, createElementBlock, normalizeClass, unref, createElementVNode, toDisplayString, readonly, reactive, renderSlot, createBlock, resolveDynamicComponent, withCtx, Fragment, cloneVNode, h as h$1, inject, provide, onMounted, watchEffect, onUnmounted, watch, nextTick, toRaw, createCommentVNode, toRefs, createVNode, Transition, renderList, createTextVNode, pushScopeId, popScopeId, isRef, withDirectives, vShow, useAttrs } from "vue";
2
2
  const Variant = {
3
3
  Filled: "filled",
4
4
  FilledPrimary: "filled-primary",
@@ -144,65 +144,49 @@ const usePlatform = () => {
144
144
  };
145
145
  const { isBrowser: isBrowser$1 } = usePlatform();
146
146
  const MODE = {
147
- LIGHT: true,
148
- DARK: false
147
+ LIGHT: "light",
148
+ DARK: "dark"
149
149
  };
150
- const modeOsPrefers = ref(MODE.DARK);
151
- const currentMode = ref(MODE.DARK);
152
- const modeUserPrefers = ref(void 0);
153
- const isDark = computed(() => currentMode.value === MODE.DARK);
150
+ const defaultMode = MODE.DARK;
151
+ const chosenMode = ref(defaultMode);
152
+ const isDark = computed(() => chosenMode.value === MODE.DARK);
154
153
  const useDarkMode = () => {
155
- const getModeOsPrefers = () => {
156
- let prefersLightMode = false;
157
- if (isBrowser$1)
158
- prefersLightMode = window.matchMedia("(prefers-color-scheme: light)").matches;
159
- setModeOsPrefers(prefersLightMode);
160
- return prefersLightMode;
161
- };
162
- const getModeUserPrefers = () => {
163
- if (isBrowser$1) {
164
- const mode = localStorage.getItem("modeUserPrefers");
165
- if (mode !== null)
166
- modeUserPrefers.value = mode === "true";
154
+ const getModeFromCookie = (cookies) => {
155
+ const cookie = cookies.split(";").find((c2) => c2.trim().startsWith("vuetiful-mode="));
156
+ if (cookie) {
157
+ const value = cookie.split("=")[1];
158
+ return value;
167
159
  }
168
- return modeUserPrefers.value;
169
- };
170
- const getModeAutoPrefers = () => {
171
- const os = getModeOsPrefers();
172
- const user = getModeUserPrefers();
173
- if (user === void 0)
174
- return os;
175
- return user;
160
+ return defaultMode;
176
161
  };
177
- const setModeOsPrefers = (value) => {
178
- modeOsPrefers.value = value;
179
- if (isBrowser$1) {
180
- localStorage.setItem("modeOsPrefers", value.toString());
181
- }
162
+ const applyModeSSR = (html, mode) => {
163
+ if (mode === MODE.DARK)
164
+ html = html.replace("<html", '<html class="dark"');
165
+ return html;
182
166
  };
183
- const setModeUserPrefers = (value) => {
184
- modeUserPrefers.value = value;
167
+ const initializeMode = () => {
185
168
  if (isBrowser$1) {
186
- localStorage.setItem("modeUserPrefers", value.toString());
169
+ const mode = getModeFromCookie(document.cookie);
170
+ applyMode(mode);
187
171
  }
188
172
  };
189
- const setModeCurrent = (value) => {
173
+ const applyMode = (value) => {
190
174
  const elemHtmlClasses = document.documentElement.classList;
191
175
  const classDark = "dark";
192
176
  value === MODE.LIGHT ? elemHtmlClasses.remove(classDark) : elemHtmlClasses.add(classDark);
193
- currentMode.value = value;
194
- };
195
- const initializeMode = () => {
196
- const mode = getModeAutoPrefers();
197
- setModeCurrent(mode);
177
+ if (isBrowser$1) {
178
+ document.cookie = `vuetiful-mode=${value};path=/;max-age=31536000;SameSite=Lax`;
179
+ }
180
+ chosenMode.value = value;
198
181
  };
199
182
  const autoModeWatcher = () => {
200
183
  const mql = window.matchMedia("(prefers-color-scheme: light)");
201
184
  const setMode = (value) => {
202
185
  const elemHtmlClasses = document.documentElement.classList;
203
186
  const classDark = `dark`;
204
- value === MODE.LIGHT ? elemHtmlClasses.remove(classDark) : elemHtmlClasses.add(classDark);
205
- setModeCurrent(value);
187
+ const mode = value ? MODE.LIGHT : MODE.DARK;
188
+ mode === MODE.LIGHT ? elemHtmlClasses.remove(classDark) : elemHtmlClasses.add(classDark);
189
+ applyMode(mode);
206
190
  };
207
191
  setMode(mql.matches);
208
192
  mql.onchange = () => {
@@ -210,17 +194,13 @@ const useDarkMode = () => {
210
194
  };
211
195
  };
212
196
  return {
213
- modeOsPrefers: readonly(modeOsPrefers),
214
- modeUserPrefers: readonly(modeUserPrefers),
215
- currentMode: readonly(currentMode),
197
+ chosenMode,
216
198
  isDark: readonly(isDark),
217
- getModeOsPrefers,
218
- getModeUserPrefers,
219
- getModeAutoPrefers,
220
- setModeUserPrefers,
221
- setModeCurrent,
222
- autoModeWatcher,
223
199
  initializeMode,
200
+ applyMode,
201
+ autoModeWatcher,
202
+ applyModeSSR,
203
+ getModeFromCookie,
224
204
  MODE
225
205
  };
226
206
  };
@@ -46824,10 +46804,7 @@ const _sfc_main$u = defineComponent({
46824
46804
  }
46825
46805
  },
46826
46806
  setup(props, { attrs }) {
46827
- const { initializeMode, setModeCurrent, setModeUserPrefers, currentMode: currentMode2, MODE: MODE2 } = useDarkMode();
46828
- onMounted(() => {
46829
- initializeMode();
46830
- });
46807
+ const { applyMode, chosenMode: chosenMode2, MODE: MODE2 } = useDarkMode();
46831
46808
  const cTransition = `transition-all duration-[200ms]`;
46832
46809
  const cTrack = "cursor-pointer";
46833
46810
  const cThumb = "aspect-square scale-[0.8] flex justify-center items-center";
@@ -46837,9 +46814,8 @@ const _sfc_main$u = defineComponent({
46837
46814
  moon: "M223.5 32C100 32 0 132.3 0 256S100 480 223.5 480c60.6 0 115.5-24.2 155.8-63.4c5-4.9 6.3-12.5 3.1-18.7s-10.1-9.7-17-8.5c-9.8 1.7-19.8 2.6-30.1 2.6c-96.9 0-175.5-78.8-175.5-176c0-65.8 36-123.1 89.3-153.3c6.1-3.5 9.2-10.5 7.7-17.3s-7.3-11.9-14.3-12.5c-6.3-.5-12.6-.8-19-.8z"
46838
46815
  };
46839
46816
  const onToggleHandler = () => {
46840
- const toggle = !currentMode2.value;
46841
- setModeUserPrefers(toggle);
46842
- setModeCurrent(toggle);
46817
+ const toggle = chosenMode2.value === MODE2.LIGHT ? MODE2.DARK : MODE2.LIGHT;
46818
+ applyMode(toggle);
46843
46819
  };
46844
46820
  const onKeyDown = (event) => {
46845
46821
  if (["Enter", "Space"].includes(event.code)) {
@@ -46847,11 +46823,11 @@ const _sfc_main$u = defineComponent({
46847
46823
  event.currentTarget.click();
46848
46824
  }
46849
46825
  };
46850
- const trackBg = computed(() => currentMode2.value === MODE2.LIGHT ? props.bgLight : props.bgDark);
46851
- const thumbBg = computed(() => currentMode2.value === MODE2.LIGHT ? props.bgDark : props.bgLight);
46852
- const thumbPosition = computed(() => currentMode2.value === MODE2.LIGHT ? "translate-x-[100%]" : "");
46826
+ const trackBg = computed(() => chosenMode2.value === MODE2.LIGHT ? props.bgLight : props.bgDark);
46827
+ const thumbBg = computed(() => chosenMode2.value === MODE2.LIGHT ? props.bgDark : props.bgLight);
46828
+ const thumbPosition = computed(() => chosenMode2.value === MODE2.LIGHT ? "translate-x-[100%]" : "");
46853
46829
  const iconFill = computed(() => {
46854
- return currentMode2.value === MODE2.LIGHT ? props.textLight : props.textDark;
46830
+ return chosenMode2.value === MODE2.LIGHT ? props.textLight : props.textDark;
46855
46831
  });
46856
46832
  const classesTrack = computed(() => {
46857
46833
  var _a;
@@ -46868,8 +46844,9 @@ const _sfc_main$u = defineComponent({
46868
46844
  svgPath,
46869
46845
  onToggleHandler,
46870
46846
  onKeyDown,
46871
- currentMode: currentMode2,
46872
- iconFill
46847
+ chosenMode: chosenMode2,
46848
+ iconFill,
46849
+ MODE: MODE2
46873
46850
  };
46874
46851
  }
46875
46852
  });
@@ -46880,12 +46857,10 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
46880
46857
  class: normalizeClass(`lightswitch-track ${_ctx.classesTrack}`),
46881
46858
  onClick: _cache[0] || (_cache[0] = (...args) => _ctx.onToggleHandler && _ctx.onToggleHandler(...args)),
46882
46859
  onKeydown: _cache[1] || (_cache[1] = (...args) => _ctx.onKeyDown && _ctx.onKeyDown(...args)),
46883
- "on:keyup": "",
46884
- "on:keypress": "",
46885
46860
  role: "switch",
46886
46861
  "aria-label": "Light Switch",
46887
- "aria-checked": _ctx.currentMode,
46888
- title: `Toggle ${_ctx.currentMode === false ? "Dark" : "Light"} Mode`,
46862
+ "aria-checked": _ctx.chosenMode === _ctx.MODE.LIGHT,
46863
+ title: `Toggle ${_ctx.chosenMode === _ctx.MODE.DARK ? "Dark" : "Light"} Mode`,
46889
46864
  tabindex: "0"
46890
46865
  }, [
46891
46866
  createElementVNode("div", {
@@ -46898,7 +46873,7 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
46898
46873
  }, [
46899
46874
  createElementVNode("path", {
46900
46875
  fill: "currentColor",
46901
- d: _ctx.currentMode ? _ctx.svgPath.sun : _ctx.svgPath.moon
46876
+ d: _ctx.chosenMode === _ctx.MODE.LIGHT ? _ctx.svgPath.sun : _ctx.svgPath.moon
46902
46877
  }, null, 8, _hoisted_2$6)
46903
46878
  ], 2))
46904
46879
  ], 2)
@@ -49852,6 +49827,198 @@ var components = /* @__PURE__ */ Object.freeze({
49852
49827
  });
49853
49828
  var main = "";
49854
49829
  var tailwind = "";
49830
+ const semanticNames = ["primary", "secondary", "tertiary", "success", "warning", "error", "surface"];
49831
+ const useColors = () => {
49832
+ function hexToRgb(hex) {
49833
+ const sanitizedHex = hex.replaceAll("##", "#");
49834
+ const colorParts = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(sanitizedHex);
49835
+ if (!colorParts)
49836
+ return null;
49837
+ const [, r2, g2, b2] = colorParts;
49838
+ return {
49839
+ r: parseInt(r2, 16),
49840
+ g: parseInt(g2, 16),
49841
+ b: parseInt(b2, 16)
49842
+ };
49843
+ }
49844
+ function hexToTailwindRgbString(hex) {
49845
+ const sanitizedHex = hex.replaceAll("##", "#");
49846
+ const colorParts = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(sanitizedHex);
49847
+ if (!colorParts)
49848
+ return "(invalid)";
49849
+ const [, r2, g2, b2] = colorParts;
49850
+ return `${parseInt(r2, 16)} ${parseInt(g2, 16)} ${parseInt(b2, 16)}`;
49851
+ }
49852
+ function rgbToHex(r2, g2, b2) {
49853
+ const toHex = (c2) => `0${c2.toString(16)}`.slice(-2);
49854
+ return `#${toHex(r2)}${toHex(g2)}${toHex(b2)}`;
49855
+ }
49856
+ function generateA11yOnColor(hex) {
49857
+ const black = calculateRatio(hex, "#000000");
49858
+ const white = calculateRatio(hex, "#FFFFFF");
49859
+ return black < white ? "0 0 0" : "255 255 255";
49860
+ }
49861
+ function lighten(hex, intensity) {
49862
+ const color = hexToRgb(`#${hex}`);
49863
+ if (!color)
49864
+ return "";
49865
+ const r2 = Math.round(color.r + (255 - color.r) * intensity);
49866
+ const g2 = Math.round(color.g + (255 - color.g) * intensity);
49867
+ const b2 = Math.round(color.b + (255 - color.b) * intensity);
49868
+ return rgbToHex(r2, g2, b2);
49869
+ }
49870
+ function darken(hex, intensity) {
49871
+ const color = hexToRgb(hex);
49872
+ if (!color)
49873
+ return "";
49874
+ const r2 = Math.round(color.r * intensity);
49875
+ const g2 = Math.round(color.g * intensity);
49876
+ const b2 = Math.round(color.b * intensity);
49877
+ return rgbToHex(r2, g2, b2);
49878
+ }
49879
+ function generatePalette2(baseColor) {
49880
+ const hexValidation = new RegExp(/^#[0-9a-f]{6}$/i);
49881
+ if (!hexValidation.test(baseColor))
49882
+ baseColor = "#CCCCCC";
49883
+ const hex500 = `#${baseColor}`.replace("##", "#");
49884
+ const response = {
49885
+ 500: { hex: hex500, rgb: hexToTailwindRgbString(hex500), on: generateA11yOnColor(hex500) }
49886
+ };
49887
+ const intensityMap = {
49888
+ 50: 0.85,
49889
+ 100: 0.8,
49890
+ 200: 0.75,
49891
+ 300: 0.6,
49892
+ 400: 0.3,
49893
+ 600: 0.9,
49894
+ 700: 0.75,
49895
+ 800: 0.6,
49896
+ 900: 0.49
49897
+ };
49898
+ [50, 100, 200, 300, 400].forEach((level) => {
49899
+ const hex = lighten(baseColor, intensityMap[level]);
49900
+ response[level] = { hex, rgb: hexToTailwindRgbString(hex), on: generateA11yOnColor(hex) };
49901
+ });
49902
+ [600, 700, 800, 900].forEach((level) => {
49903
+ const hex = darken(baseColor, intensityMap[level]);
49904
+ response[level] = { hex, rgb: hexToTailwindRgbString(hex), on: generateA11yOnColor(hex) };
49905
+ });
49906
+ return response;
49907
+ }
49908
+ const contrastLevels = {
49909
+ small: {
49910
+ AA: 1 / 4.5,
49911
+ AAA: 1 / 7
49912
+ },
49913
+ large: {
49914
+ AA: 1 / 3,
49915
+ AAA: 1 / 4.5
49916
+ }
49917
+ };
49918
+ function getLuminance(r2, g2, b2) {
49919
+ const { _r, _g, _b } = typeof r2 === "object" ? { _r: r2.r, _g: r2.g, _b: r2.b } : { _r: r2, _g: g2, _b: b2 };
49920
+ if (_r === void 0 || _g === void 0 || _b === void 0)
49921
+ throw new Error("Invalid RGB value!");
49922
+ const a2 = [_r, _g, _b].map(function(v2) {
49923
+ v2 /= 255;
49924
+ return v2 <= 0.03928 ? v2 / 12.92 : Math.pow((v2 + 0.055) / 1.055, 2.4);
49925
+ });
49926
+ return a2[0] * 0.2126 + a2[1] * 0.7152 + a2[2] * 0.0722;
49927
+ }
49928
+ function destringRgb(rgbString) {
49929
+ const rgb = rgbString.match(/(\d+),?\s*(\d+),?\s*(\d+)/);
49930
+ if (!rgb)
49931
+ throw new Error("Invalid RGB string!");
49932
+ return { r: parseInt(rgb[1], 10), g: parseInt(rgb[2], 10), b: parseInt(rgb[3], 10) };
49933
+ }
49934
+ function handleStringColor(colorString, returnType = "rgb") {
49935
+ if (colorString.includes("--")) {
49936
+ colorString = colorString.replace(/var\(|\)/g, "");
49937
+ const cssVarHydrated = getComputedStyle(document.documentElement).getPropertyValue(colorString).trim();
49938
+ return handleStringColor(cssVarHydrated, returnType);
49939
+ }
49940
+ if (colorString.includes(" ")) {
49941
+ const rgb = destringRgb(colorString);
49942
+ return returnType === "hex" ? rgbToHex(rgb.r, rgb.g, rgb.b) : rgb;
49943
+ }
49944
+ if (colorString.includes("#")) {
49945
+ const rgb = hexToRgb(colorString);
49946
+ if (!rgb)
49947
+ return "(invalid)";
49948
+ return returnType === "hex" ? colorString : rgb;
49949
+ }
49950
+ return colorString;
49951
+ }
49952
+ function calculateRatio(luminance1, luminance2) {
49953
+ const lum1 = typeof luminance1 === "string" ? getLuminance(handleStringColor(luminance1)) : luminance1;
49954
+ const lum2 = typeof luminance2 === "string" ? getLuminance(handleStringColor(luminance2)) : luminance2;
49955
+ if (lum1 === void 0 || lum2 === void 0)
49956
+ throw new Error("Luminance is undefined!");
49957
+ return lum1 > lum2 ? (lum2 + 0.05) / (lum1 + 0.05) : (lum1 + 0.05) / (lum2 + 0.05);
49958
+ }
49959
+ function textPasses(textColor, backgroundColor, size, level) {
49960
+ const ratio = calculateRatio(textColor, backgroundColor);
49961
+ return ratio <= contrastLevels[size][level];
49962
+ }
49963
+ function hexValueIsValid(textColor) {
49964
+ return /^#[0-9A-F]{6}$/i.test(textColor);
49965
+ }
49966
+ function getPassReport(textColor, backgroundColor) {
49967
+ const _textColor = handleStringColor(textColor, "hex");
49968
+ const _backgroundColor = handleStringColor(backgroundColor, "hex");
49969
+ const contrast = calculateRatio(_textColor, _backgroundColor);
49970
+ const smallAA = textPasses(_textColor, _backgroundColor, "small", "AA");
49971
+ const smallAAA = textPasses(_textColor, _backgroundColor, "small", "AAA");
49972
+ const largeAA = textPasses(_textColor, _backgroundColor, "large", "AA");
49973
+ const largeAAA = textPasses(_textColor, _backgroundColor, "large", "AAA");
49974
+ const fails = !smallAA && !smallAAA && !largeAA && !largeAAA;
49975
+ const AAAEmoji = '<i class="fa-solid fa-heart"></i>';
49976
+ const AAEmoji = '<i class="fa-solid fa-star"></i>';
49977
+ const largeAAEmoji = '<i class="fa-solid fa-star-half-stroke"></i>';
49978
+ const failEmoji = '<i class="fa-solid fa-triangle-exclamation"></i>';
49979
+ const report = {
49980
+ emoji: smallAAA ? AAAEmoji : smallAA ? AAEmoji : largeAA ? largeAAEmoji : failEmoji,
49981
+ note: `${_textColor} and ${_backgroundColor} ` + (smallAAA ? "has great contrast!" : smallAA ? "is satisfactory for larger text" : largeAA ? "has poor contrast" : "fail contrast guidelines")
49982
+ };
49983
+ return {
49984
+ textColor: _textColor,
49985
+ backgroundColor: _backgroundColor,
49986
+ contrast,
49987
+ report,
49988
+ smallAA,
49989
+ smallAAA,
49990
+ largeAA,
49991
+ largeAAA,
49992
+ fails
49993
+ };
49994
+ }
49995
+ const hexValuesAreValid2 = (colors) => {
49996
+ let valid = true;
49997
+ colors == null ? void 0 : colors.forEach((color) => {
49998
+ valid = valid && hexValueIsValid(color.hex);
49999
+ });
50000
+ return valid;
50001
+ };
50002
+ return {
50003
+ hexToRgb,
50004
+ hexToTailwindRgbString,
50005
+ rgbToHex,
50006
+ generateA11yOnColor,
50007
+ lighten,
50008
+ darken,
50009
+ generatePalette: generatePalette2,
50010
+ contrastLevels,
50011
+ getLuminance,
50012
+ destringRgb,
50013
+ handleStringColor,
50014
+ calculateRatio,
50015
+ textPasses,
50016
+ hexValueIsValid,
50017
+ getPassReport,
50018
+ hexValuesAreValid: hexValuesAreValid2,
50019
+ semanticNames
50020
+ };
50021
+ };
49855
50022
  const _hoisted_1 = { class: "vuetiful-theme-switcher" };
49856
50023
  const _hoisted_2 = { class: "flex items-center justify-between" };
49857
50024
  const _hoisted_3 = /* @__PURE__ */ createElementVNode("div", { class: "text-lg" }, "Mode", -1);
@@ -49888,11 +50055,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
49888
50055
  }
49889
50056
  },
49890
50057
  setup(__props) {
49891
- const { initializeTheme, loadTheme, themes: themes2, chosenTheme: chosenTheme2 } = useTheme();
50058
+ const { applyTheme, themes: themes2, chosenTheme: chosenTheme2 } = useTheme();
49892
50059
  const showPopup = ref(false);
49893
- onMounted(() => {
49894
- initializeTheme();
49895
- });
49896
50060
  return (_ctx, _cache) => {
49897
50061
  return openBlock(), createElementBlock("div", _hoisted_1, [
49898
50062
  createVNode(unref(_sfc_main$w), {
@@ -49917,9 +50081,9 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
49917
50081
  }, [
49918
50082
  (openBlock(true), createElementBlock(Fragment, null, renderList(unref(themes2), (theme, index2) => {
49919
50083
  return openBlock(), createBlock(unref(_sfc_main$w), {
49920
- class: normalizeClass(["vuetiful-theme-switcher__popup-list-item h-full w-full p-2 text-center capitalize hover:cursor-pointer", `${__props.classListItem} ${unref(chosenTheme2) === theme.name ? "variant-filled-surface" : ""}`]),
50084
+ class: normalizeClass(["vuetiful-theme-switcher__popup-list-item h-full w-full p-2 text-center capitalize hover:cursor-pointer", `${__props.classListItem} ${unref(chosenTheme2).name === theme.name ? "variant-filled-surface" : ""}`]),
49921
50085
  key: index2,
49922
- onClick: ($event) => unref(loadTheme)(theme.name)
50086
+ onClick: ($event) => unref(applyTheme)(theme)
49923
50087
  }, {
49924
50088
  default: withCtx(() => [
49925
50089
  createTextVNode(toDisplayString(theme.name), 1)
@@ -49933,92 +50097,234 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
49933
50097
  };
49934
50098
  }
49935
50099
  });
49936
- const { isBrowser } = usePlatform();
49937
- const THEMES = {
49938
- VUETIFUL: "vuetiful",
49939
- ROCKET: "rocket",
49940
- SAHARA: "sahara",
49941
- SEAFOAM: "seafoam",
49942
- SEASONAL: "seasonal",
49943
- SKELETON: "skeleton",
49944
- VINTAGE: "vintage"
50100
+ const themes = [
50101
+ {
50102
+ name: "vuetiful",
50103
+ gradients: {
50104
+ light: "radial-gradient(at 76% 0%, hsla(189,100%,56%,0.36) 0px, transparent 50%), radial-gradient(at 1% 0%, hsla(340,100%,76%,0.26) 0px, transparent 50%), radial-gradient(at 20% 100%, hsla(241,100%,70%,0.47) 0px, transparent 50%)",
50105
+ dark: "radial-gradient(at 76% 0%, hsla(189,100%,56%,0.20) 0px, transparent 50%), radial-gradient(at 1% 0%, hsla(340,100%,76%,0.15) 0px, transparent 50%), radial-gradient(at 20% 100%, hsla(241,100%,70%,0.30) 0px, transparent 50%)"
50106
+ },
50107
+ colors: {
50108
+ primary: { key: "primary", label: "Primary", hex: "#EC4899", rgb: "0 0 0", on: "0 0 0" },
50109
+ secondary: { key: "secondary", label: "Secondary", hex: "#06B6D4", rgb: "0 0 0", on: "0 0 0" },
50110
+ tertiary: { key: "tertiary", label: "Tertiary", hex: "#14B8A6", rgb: "0 0 0", on: "0 0 0" },
50111
+ success: { key: "success", label: "Success", hex: "#84CC16", rgb: "0 0 0", on: "0 0 0" },
50112
+ warning: { key: "warning", label: "Warning", hex: "#EAB308", rgb: "0 0 0", on: "0 0 0" },
50113
+ error: { key: "error", label: "Error", hex: "#EF4444", rgb: "0 0 0", on: "255 255 255" },
50114
+ surface: { key: "surface", label: "Surface", hex: "#6366F1", rgb: "0 0 0", on: "255 255 255" }
50115
+ },
50116
+ fonts: {
50117
+ base: "system",
50118
+ customBase: "Quicksand",
50119
+ baseImports: '@import url("https://fonts.googleapis.com/css2?family=Quicksand:wght@300;400;500;600;700&display=swap");',
50120
+ headings: "system",
50121
+ customHeadings: "Quicksand",
50122
+ headingImports: ""
50123
+ },
50124
+ textColorLight: "0 0 0",
50125
+ textColorDark: "255 255 255",
50126
+ roundedBase: "12px",
50127
+ roundedContainer: "12px",
50128
+ borderBase: "0px",
50129
+ customCss: `
50130
+ [data-theme="vuetiful"] h1,
50131
+ [data-theme="vuetiful"] h2,
50132
+ [data-theme="vuetiful"] h3,
50133
+ [data-theme="vuetiful"] h4,
50134
+ [data-theme="vuetiful"] h5,
50135
+ [data-theme="vuetiful"] h6,
50136
+ [data-theme="vuetiful"] a,
50137
+ [data-theme="vuetiful"] button {
50138
+ font-weight: bold;
50139
+ }`
50140
+ },
50141
+ {
50142
+ name: "rocket",
50143
+ gradients: {
50144
+ light: "radial-gradient(at 0% 0%, rgba(var(--color-secondary-500) / 0.33) 0px, transparent 50%), radial-gradient(at 98% 1%, rgba(var(--color-error-500) / 0.33) 0px, transparent 50%)",
50145
+ dark: "radial-gradient(at 0% 0%, rgba(var(--color-secondary-500) / 0.33) 0px, transparent 50%), radial-gradient(at 98% 1%, rgba(var(--color-error-500) / 0.33) 0px, transparent 50%)"
50146
+ },
50147
+ colors: {
50148
+ primary: { key: "primary", label: "Primary", hex: "#06b6d4", rgb: "0 0 0", on: "0 0 0" },
50149
+ secondary: { key: "secondary", label: "Secondary", hex: "#3b82f6", rgb: "0 0 0", on: "255 255 255" },
50150
+ tertiary: { key: "tertiary", label: "Tertiary", hex: "#3b82f6", rgb: "0 0 0", on: "255 255 255" },
50151
+ success: { key: "success", label: "Success", hex: "#4ccb15", rgb: "0 0 0", on: "0 0 0" },
50152
+ warning: { key: "warning", label: "Warning", hex: "#f4c12a", rgb: "0 0 0", on: "0 0 0" },
50153
+ error: { key: "error", label: "Error", hex: "#b52c55", rgb: "0 0 0", on: "255 255 255" },
50154
+ surface: { key: "surface", label: "Surface", hex: "#64748b", rgb: "0 0 0", on: "255 255 255" }
50155
+ },
50156
+ fonts: {
50157
+ base: "system",
50158
+ customBase: "Roboto",
50159
+ baseImports: '@import url("https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap");',
50160
+ headings: "system",
50161
+ customHeadings: "Space Grotesk",
50162
+ headingImports: '@import url("https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&display=swap");'
50163
+ },
50164
+ textColorLight: "0 0 0",
50165
+ textColorDark: "255 255 255",
50166
+ roundedBase: "0px",
50167
+ roundedContainer: "0px",
50168
+ borderBase: "0px",
50169
+ customCss: `
50170
+ [data-theme='rocket'] h1,
50171
+ [data-theme='rocket'] h2,
50172
+ [data-theme='rocket'] h3,
50173
+ [data-theme='rocket'] h4,
50174
+ [data-theme='rocket'] h5,
50175
+ [data-theme='rocket'] h6 {
50176
+ font-weight: bold;
50177
+ }`
50178
+ }
50179
+ ];
50180
+ const THEME = {
50181
+ VUETIFUL: themes[0],
50182
+ ROCKET: themes[1]
49945
50183
  };
49946
- const builtInUrl = (name) => {
49947
- return `https://code-coaching.dev/vuetiful-themes/theme-${name}.css`;
50184
+ const { isBrowser } = usePlatform();
50185
+ const chosenTheme = ref(themes[0]);
50186
+ const { generatePalette, hexValuesAreValid } = useColors();
50187
+ const generateColorCSS = (theme) => {
50188
+ let newCSS = "";
50189
+ const newPalette = {};
50190
+ Object.values(theme.colors).forEach((color) => {
50191
+ const colorKey = color.key;
50192
+ newPalette[color.key] = generatePalette(color.hex);
50193
+ newCSS += "\n ";
50194
+ newCSS += `/* ${colorKey} | ${newPalette[colorKey][500].hex} */
50195
+ `;
50196
+ for (let [k2, v2] of Object.entries(newPalette[colorKey])) {
50197
+ newCSS += `--color-${colorKey}-${k2}: ${v2.rgb}; /* \u2B05 ${v2.hex} */
50198
+ `;
50199
+ }
50200
+ });
50201
+ return newCSS;
49948
50202
  };
49949
- const builtInThemes = [
49950
- { name: THEMES.VUETIFUL, url: builtInUrl(`${THEMES.VUETIFUL}-0.0.1`) },
49951
- { name: THEMES.ROCKET, url: builtInUrl(`${THEMES.ROCKET}`) },
49952
- { name: THEMES.SAHARA, url: builtInUrl(`${THEMES.SAHARA}`) },
49953
- { name: THEMES.SEAFOAM, url: builtInUrl(`${THEMES.SEAFOAM}`) },
49954
- { name: THEMES.SEASONAL, url: builtInUrl(`${THEMES.SEASONAL}`) },
49955
- { name: THEMES.SKELETON, url: builtInUrl(`${THEMES.SKELETON}`) },
49956
- { name: THEMES.VINTAGE, url: builtInUrl(`${THEMES.VINTAGE}`) }
49957
- ];
49958
- const themes = ref([...builtInThemes]);
49959
- const defaultTheme = THEMES.VUETIFUL;
49960
- const chosenTheme = ref(defaultTheme);
49961
50203
  const useTheme = () => {
49962
- const saveThemeToStorage = (name) => {
49963
- const theme = themes.value.find((t2) => t2.name === name);
49964
- if (!theme)
49965
- return;
49966
- if (isBrowser) {
49967
- localStorage.setItem("vuetiful-theme", theme.name);
49968
- document.body.setAttribute("data-theme", theme.name);
49969
- }
49970
- };
49971
- const initializeTheme = (callback) => {
49972
- if (isBrowser) {
49973
- const storedTheme = localStorage.getItem("vuetiful-theme");
49974
- if (storedTheme)
49975
- loadTheme(storedTheme, callback);
49976
- else
49977
- loadTheme(defaultTheme, callback);
50204
+ const changeDataTheme = (name) => document.body.setAttribute("data-theme", name);
50205
+ const getThemeFromCookie = (cookies) => {
50206
+ const themeName = getThemeNameFromCookie(cookies);
50207
+ const theme = themes.find((t2) => t2.name === themeName);
50208
+ if (theme)
50209
+ return theme;
50210
+ return themes[0];
50211
+ };
50212
+ const getThemeNameFromCookie = (cookies) => {
50213
+ const cookie = cookies.split(";").find((c2) => c2.trim().startsWith(`vuetiful-theme=`));
50214
+ const value = cookie == null ? void 0 : cookie.split("=")[1];
50215
+ return value || "";
50216
+ };
50217
+ const applyThemeSSR = (html, theme) => {
50218
+ chosenTheme.value = theme;
50219
+ const css2 = generateCss(theme);
50220
+ html = html.replace("</head>", `<style type="text/css" id="vuetiful-theme">${css2}</style></head>`);
50221
+ html = html.replace("<body", `<body data-theme="${theme.name}"`);
50222
+ return html;
50223
+ };
50224
+ const generateCss = (theme) => {
50225
+ if (hexValuesAreValid(Object.values(theme.colors))) {
50226
+ return `${theme.fonts.baseImports}
50227
+ ${theme.fonts.headingImports}
50228
+ :root {
50229
+ /* =~= Theme Properties =~= */
50230
+ --theme-font-family-base: ${theme.fonts.customBase ? `"${theme.fonts.customBase}", ` : ""}${theme.fonts.base};
50231
+ --theme-font-family-heading: ${theme.fonts.customHeadings ? `"${theme.fonts.customHeadings}", ` : ""}${theme.fonts.headings};
50232
+ --theme-font-color-base: ${theme.textColorLight};
50233
+ --theme-font-color-dark: ${theme.textColorDark};
50234
+ --theme-rounded-base: ${theme.roundedBase};
50235
+ --theme-rounded-container: ${theme.roundedContainer};
50236
+ --theme-border-base: ${theme.borderBase};
50237
+
50238
+ /* =~= Theme On-X Colors =~= */
50239
+ --on-primary: ${theme.colors.primary.on};
50240
+ --on-secondary: ${theme.colors.secondary.on};
50241
+ --on-tertiary: ${theme.colors.tertiary.on};
50242
+ --on-success: ${theme.colors.success.on};
50243
+ --on-warning: ${theme.colors.warning.on};
50244
+ --on-error: ${theme.colors.error.on};
50245
+ --on-surface: ${theme.colors.surface.on};
50246
+
50247
+ /* =~= Theme Colors =~= */
50248
+ ${generateColorCSS(theme)}
50249
+ }
50250
+
50251
+ ${theme.gradients.light.length ? `[data-theme="${theme.name}"] {
50252
+ background-image:
50253
+ ${theme.gradients.light};
50254
+ }` : ""}
50255
+ ${theme.gradients.dark.length ? `.dark [data-theme="${theme.name}"] {
50256
+ background-image:
50257
+ ${theme.gradients.dark};
50258
+ }` : ""}
50259
+ ${theme.customCss}
50260
+ `;
49978
50261
  }
50262
+ return "";
49979
50263
  };
49980
- const loadTheme = (themeName, callback) => {
49981
- let themeToLoad = themes.value.find((t2) => t2.name === themeName);
49982
- if (!themeToLoad)
49983
- themeToLoad = themes.value.find((t2) => t2.name === defaultTheme) || themes.value[0];
49984
- const theme = themeToLoad;
49985
- chosenTheme.value = theme.name;
49986
- const existingStyle = document.getElementById("theme");
49987
- let themeUrl = theme.url;
49988
- const link = document.createElement("link");
49989
- link.id = "theme";
49990
- link.href = themeUrl;
49991
- link.type = "text/css";
49992
- link.rel = "stylesheet";
49993
- link.onload = () => {
50264
+ const applyTheme = (theme, callback) => {
50265
+ const existingStyle = document.getElementById("vuetiful-theme");
50266
+ const themeCss = generateCss(theme);
50267
+ const style = document.createElement("style");
50268
+ style.innerHTML = themeCss;
50269
+ style.id = "vuetiful-theme";
50270
+ style.onload = () => {
49994
50271
  if (existingStyle)
49995
50272
  existingStyle.remove();
49996
- saveThemeToStorage(theme.name);
49997
50273
  if (callback)
49998
50274
  callback();
49999
50275
  };
50000
50276
  const head = document.querySelector("head");
50001
50277
  if (head)
50002
- head.appendChild(link);
50003
- };
50004
- const registerAllBuiltInThemes = () => {
50005
- return [...builtInThemes];
50278
+ head.appendChild(style);
50279
+ chosenTheme.value = theme;
50280
+ if (isBrowser) {
50281
+ document.cookie = `vuetiful-theme=${theme.name};path=/;max-age=31536000;SameSite=Lax`;
50282
+ changeDataTheme(theme.name);
50283
+ }
50006
50284
  };
50007
- const registerTheme = (name, url) => {
50008
- return { url, name };
50285
+ const initializeTheme = (callback) => {
50286
+ if (isBrowser) {
50287
+ const themeName = getThemeNameFromCookie(document.cookie);
50288
+ if (themeName === "custom") {
50289
+ const storedThemeJson = localStorage.getItem("vuetiful-custom-theme");
50290
+ let storedTheme = null;
50291
+ try {
50292
+ storedTheme = storedThemeJson ? JSON.parse(storedThemeJson) : null;
50293
+ if (storedTheme) {
50294
+ applyTheme(storedTheme, callback);
50295
+ registerTheme(storedTheme);
50296
+ }
50297
+ } catch (e2) {
50298
+ applyTheme(themes[0], callback);
50299
+ }
50300
+ } else {
50301
+ const theme = themes.find((t2) => t2.name === themeName);
50302
+ if (theme) {
50303
+ applyTheme(theme, callback);
50304
+ } else {
50305
+ applyTheme(themes[0], callback);
50306
+ }
50307
+ }
50308
+ }
50009
50309
  };
50010
- const overwriteThemes = (newThemes) => {
50011
- themes.value = [...newThemes];
50310
+ const registerTheme = (theme) => {
50311
+ const existingTheme = themes.find((t2) => t2.name === theme.name);
50312
+ if (existingTheme) {
50313
+ const index2 = themes.indexOf(existingTheme);
50314
+ themes[index2] = theme;
50315
+ } else {
50316
+ themes.push(theme);
50317
+ }
50012
50318
  };
50013
50319
  return {
50014
- themes: readonly(themes),
50015
- chosenTheme: readonly(chosenTheme),
50320
+ chosenTheme,
50321
+ themes,
50322
+ THEME,
50323
+ applyThemeSSR,
50324
+ applyTheme,
50325
+ getThemeFromCookie,
50016
50326
  initializeTheme,
50017
- loadTheme,
50018
- saveThemeToStorage,
50019
- THEMES,
50020
- overwriteThemes,
50021
- registerAllBuiltInThemes,
50327
+ changeDataTheme,
50022
50328
  registerTheme
50023
50329
  };
50024
50330
  };
@@ -50033,4 +50339,4 @@ function install(app) {
50033
50339
  }
50034
50340
  }
50035
50341
  var index = { install };
50036
- export { _sfc_main as ThemeSwitcher, _sfc_main$8 as VAccordion, VAccordionItem, VAlert, _sfc_main$y as VAvatar, _sfc_main$x as VBadge, _sfc_main$w as VButton, _sfc_main$5 as VCard, _sfc_main$4 as VCardBody, _sfc_main$3 as VCardFooter, _sfc_main$2 as VCardHeader, _sfc_main$v as VChip, _sfc_main$1 as VCodeBlock, _sfc_main$l as VDrawer, VLightSwitch, _sfc_main$d as VListbox, VListboxButton, _sfc_main$c as VListboxItem, _sfc_main$f as VListboxItems, _sfc_main$e as VListboxLabel, VPreview, _sfc_main$t as VRadioDescription, _sfc_main$s as VRadioGroup, _sfc_main$r as VRadioItem, _sfc_main$q as VRadioLabel, _sfc_main$j as VRail, _sfc_main$i as VRailTile, _sfc_main$h as VShell, _sfc_main$p as VSwitch, _sfc_main$o as VSwitchDescription, _sfc_main$n as VSwitchGroup, _sfc_main$m as VSwitchLabel, _sfc_main$b as VTab, _sfc_main$a as VTabPanel, _sfc_main$9 as VTabs, index as default, useDarkMode, useDrawer, useHighlight, useRail, useSettings, useTheme, clipboard as vClipboard };
50342
+ export { _sfc_main as ThemeSwitcher, _sfc_main$8 as VAccordion, VAccordionItem, VAlert, _sfc_main$y as VAvatar, _sfc_main$x as VBadge, _sfc_main$w as VButton, _sfc_main$5 as VCard, _sfc_main$4 as VCardBody, _sfc_main$3 as VCardFooter, _sfc_main$2 as VCardHeader, _sfc_main$v as VChip, _sfc_main$1 as VCodeBlock, _sfc_main$l as VDrawer, VLightSwitch, _sfc_main$d as VListbox, VListboxButton, _sfc_main$c as VListboxItem, _sfc_main$f as VListboxItems, _sfc_main$e as VListboxLabel, VPreview, _sfc_main$t as VRadioDescription, _sfc_main$s as VRadioGroup, _sfc_main$r as VRadioItem, _sfc_main$q as VRadioLabel, _sfc_main$j as VRail, _sfc_main$i as VRailTile, _sfc_main$h as VShell, _sfc_main$p as VSwitch, _sfc_main$o as VSwitchDescription, _sfc_main$n as VSwitchGroup, _sfc_main$m as VSwitchLabel, _sfc_main$b as VTab, _sfc_main$a as VTabPanel, _sfc_main$9 as VTabs, index as default, useColors, useDarkMode, useDrawer, useHighlight, usePlatform, useRail, useSettings, useTheme, clipboard as vClipboard };