rimelight-components 1.5.0 → 1.5.2

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.
@@ -2,9 +2,9 @@
2
2
 
3
3
  <template>
4
4
  <footer class="py-8 lg:py-12">
5
- <UContainer class="flex flex-col justify-between gap-xl lg:flex-row">
5
+ <UContainer class="gap-xl flex flex-col justify-between lg:flex-row">
6
6
  <div
7
- class="order-last flex flex-col items-center justify-between gap-xl lg:order-1 lg:flex-1 lg:items-start"
7
+ class="gap-xl order-last flex flex-col items-center justify-between lg:order-1 lg:items-start"
8
8
  >
9
9
  <slot name="left" />
10
10
  </div>
@@ -12,7 +12,7 @@
12
12
  <slot name="center" />
13
13
  </div>
14
14
  <div
15
- class="order-first flex flex-col items-center justify-between gap-xl lg:order-3 lg:flex-1 lg:items-end"
15
+ class="gap-xl order-first flex flex-col items-center justify-between lg:order-3 lg:flex-1 lg:items-end"
16
16
  >
17
17
  <slot name="right" />
18
18
  </div>
@@ -34,7 +34,7 @@ const tooltip = computed(() => config.value.tooltip);
34
34
  </template>
35
35
  <template #close>
36
36
  <UTooltip :text="tooltip">
37
- <UIcon name="lucide:circle-question-mark" class="size-4" />
37
+ <UIcon name="lucide:circle-question-mark" class="size-5" />
38
38
  </UTooltip>
39
39
  </template>
40
40
  </UAlert>
@@ -4,9 +4,9 @@ type __VLS_Props = {
4
4
  title: string;
5
5
  description?: string;
6
6
  };
7
- declare var __VLS_11: {};
7
+ declare var __VLS_24: {};
8
8
  type __VLS_Slots = {} & {
9
- default?: (props: typeof __VLS_11) => any;
9
+ default?: (props: typeof __VLS_24) => any;
10
10
  };
11
11
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
12
12
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -1,8 +1,30 @@
1
1
  <script setup>
2
+ import { useClipboard, useToast, useRoute, computed } from "#imports";
2
3
  import { tv } from "tailwind-variants";
4
+ import { slugify } from "../../utils";
5
+ const { copy } = useClipboard();
6
+ const toast = useToast();
7
+ const route = useRoute();
8
+ const copyToClipboard = async (text) => {
9
+ try {
10
+ await copy(`${text}`);
11
+ toast.add({
12
+ title: "Heading copied to clipboard!",
13
+ description: text,
14
+ color: "success"
15
+ });
16
+ } catch {
17
+ toast.add({
18
+ title: "Failed to copy heading to clipboard.",
19
+ description: "An unexpected error occurred. Please try again.",
20
+ color: "error"
21
+ });
22
+ }
23
+ };
3
24
  const sectionVariants = tv({
4
25
  slots: {
5
- sectionSlot: "flex flex-col py-4",
26
+ sectionSlot: "flex flex-col py-4 scroll-mt-24",
27
+ linkSlot: "",
6
28
  headingSlot: "font-bold",
7
29
  descriptionSlot: "text-muted",
8
30
  separatorSlot: "py-2",
@@ -12,55 +34,104 @@ const sectionVariants = tv({
12
34
  level: {
13
35
  1: {
14
36
  sectionSlot: "gap-2",
37
+ linkSlot: "",
15
38
  headingSlot: "text-4xl",
16
- descriptionSlot: "text-2xl"
39
+ descriptionSlot: "text-2xl",
40
+ separatorSlot: "",
41
+ contentSlot: ""
17
42
  },
18
43
  2: {
19
44
  sectionSlot: "gap-1.5",
45
+ linkSlot: "",
20
46
  headingSlot: "text-3xl",
21
- descriptionSlot: "text-xl"
47
+ descriptionSlot: "text-xl",
48
+ separatorSlot: "",
49
+ contentSlot: ""
22
50
  },
23
51
  3: {
24
52
  sectionSlot: "gap-1",
53
+ linkSlot: "",
25
54
  headingSlot: "text-2xl",
26
- descriptionSlot: "text-lg"
55
+ descriptionSlot: "text-lg",
56
+ separatorSlot: "",
57
+ contentSlot: ""
27
58
  },
28
59
  4: {
29
60
  sectionSlot: "gap-0.5",
61
+ linkSlot: "",
30
62
  headingSlot: "text-xl",
31
- descriptionSlot: "text-md"
63
+ descriptionSlot: "text-md",
64
+ separatorSlot: "",
65
+ contentSlot: ""
32
66
  },
33
67
  5: {
34
68
  sectionSlot: "gap-0.25",
69
+ linkSlot: "",
35
70
  headingSlot: "text-lg",
36
- descriptionSlot: "text-sm"
71
+ descriptionSlot: "text-sm",
72
+ separatorSlot: "",
73
+ contentSlot: ""
37
74
  },
38
75
  6: {
39
76
  sectionSlot: "gap-0.125",
77
+ linkSlot: "",
40
78
  headingSlot: "text-base",
41
- descriptionSlot: "text-xs"
79
+ descriptionSlot: "text-xs",
80
+ separatorSlot: "",
81
+ contentSlot: ""
42
82
  }
43
83
  }
44
84
  }
45
85
  });
46
- const { level = 1 } = defineProps({
86
+ const {
87
+ level = 1,
88
+ title,
89
+ description
90
+ } = defineProps({
47
91
  level: { type: Number, required: false },
48
92
  title: { type: String, required: true },
49
93
  description: { type: String, required: false }
50
94
  });
51
95
  const {
52
96
  sectionSlot,
97
+ linkSlot,
53
98
  headingSlot,
54
99
  descriptionSlot,
55
100
  separatorSlot,
56
101
  contentSlot
57
102
  } = sectionVariants({ level });
103
+ const sectionId = computed(() => slugify(title));
104
+ const sectionHash = computed(() => `#${sectionId.value}`);
105
+ const fullSectionUrl = computed(() => {
106
+ if (typeof window === "undefined") return sectionHash.value;
107
+ return `${window.location.origin}${route.path}${sectionHash.value}`;
108
+ });
58
109
  </script>
59
110
 
60
111
  <template>
61
- <section :class="sectionSlot()" v-bind="$attrs">
62
- <component :is="`h${level}`" :class="headingSlot()">
63
- {{ title }}
112
+ <section :id="sectionId" :class="sectionSlot()" v-bind="$attrs">
113
+ <component
114
+ :id="sectionId"
115
+ :is="`h${level}`"
116
+ :class="headingSlot()"
117
+ class="relative"
118
+ >
119
+ <NuxtLink
120
+ :href="`#${sectionId}`"
121
+ :class="linkSlot()"
122
+ :id="sectionId"
123
+ class="group lg:-ms-2 lg:ps-2"
124
+ >
125
+ <UButton
126
+ variant="soft"
127
+ color="primary"
128
+ leading-icon="lucide:link"
129
+ :to="`#${sectionId}`"
130
+ class="absolute top-1 -ms-8 hidden rounded-md p-1 opacity-0 transition group-hover:opacity-100 group-focus:opacity-100 lg:flex"
131
+ @click="copyToClipboard(fullSectionUrl)"
132
+ />
133
+ {{ title }}
134
+ </NuxtLink>
64
135
  </component>
65
136
  <p v-if="description" :class="descriptionSlot()">{{ description }}</p>
66
137
  <USeparator :class="separatorSlot()" />
@@ -4,9 +4,9 @@ type __VLS_Props = {
4
4
  title: string;
5
5
  description?: string;
6
6
  };
7
- declare var __VLS_11: {};
7
+ declare var __VLS_24: {};
8
8
  type __VLS_Slots = {} & {
9
- default?: (props: typeof __VLS_11) => any;
9
+ default?: (props: typeof __VLS_24) => any;
10
10
  };
11
11
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
12
12
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -1,7 +1,5 @@
1
1
  <script setup>
2
- import { useClipboard } from "#imports";
3
- import { useToast } from "#imports";
4
- import { computed } from "#imports";
2
+ import { useClipboard, useToast, computed } from "#imports";
5
3
  const { copy } = useClipboard();
6
4
  const toast = useToast();
7
5
  const { name, hex, rgb, hsl, oklch, cmyk } = defineProps({
@@ -16,15 +14,15 @@ const copyToClipboard = async (text) => {
16
14
  try {
17
15
  await copy(`${text}`);
18
16
  toast.add({
19
- title: `Color copied to clipboard!`,
17
+ title: "Color copied to clipboard!",
20
18
  description: text,
21
- color: `success`
19
+ color: "success"
22
20
  });
23
21
  } catch {
24
22
  toast.add({
25
- title: `Failed to copy color to clipboard.`,
26
- description: `An unexpected error occurred. Please try again.`,
27
- color: `error`
23
+ title: "Failed to copy color to clipboard.",
24
+ description: "An unexpected error occurred. Please try again.",
25
+ color: "error"
28
26
  });
29
27
  }
30
28
  };
@@ -39,59 +37,59 @@ const color = computed(() => {
39
37
  </script>
40
38
 
41
39
  <template>
42
- <UCard variant="subtle" class="w-fit rounded-none">
43
- <template #header v-if="name">
44
- <h3 class="text-lg font-bold">{{ name }}</h3>
45
- </template>
46
- <div class="gap-sm flex flex-row">
47
- <div class="aspect-square size-48" :style="{ backgroundColor: color }" />
48
- <div class="gap-sm flex flex-col justify-center">
49
- <UButton
50
- v-if="hex"
51
- variant="outline"
52
- size="sm"
53
- icon="lucide:copy"
54
- label="Copy HEX"
55
- class="w-36"
56
- @click="copyToClipboard(hex)"
57
- />
58
- <UButton
59
- v-if="rgb"
60
- variant="outline"
61
- size="sm"
62
- icon="lucide:copy"
63
- label="Copy RGB"
64
- class="w-36"
65
- @click="copyToClipboard(rgb)"
66
- />
67
- <UButton
68
- v-if="hsl"
69
- variant="outline"
70
- size="sm"
71
- icon="lucide:copy"
72
- label="Copy HSL"
73
- class="w-36"
74
- @click="copyToClipboard(hsl)"
75
- />
76
- <UButton
77
- v-if="oklch"
78
- variant="outline"
79
- size="sm"
80
- icon="lucide:copy"
81
- label="Copy OKLCH"
82
- class="w-36"
83
- @click="copyToClipboard(oklch)"
84
- />
85
- <UButton
86
- v-if="cmyk"
87
- variant="outline"
88
- size="sm"
89
- icon="lucide:copy"
90
- label="Copy CMYK"
91
- class="w-36"
92
- @click="copyToClipboard(cmyk)"
93
- />
94
- </div>
95
- </div>
96
- </UCard>
40
+ <UCard variant="subtle" class="w-full rounded-none xl:w-fit">
41
+ <template #header v-if="name">
42
+ <h3 class="text-lg font-bold">{{ name }}</h3>
43
+ </template>
44
+ <div class="gap-sm flex flex-col items-center xl:flex-row xl:items-start">
45
+ <div class="aspect-square size-48" :style="{ backgroundColor: color }" />
46
+ <div class="gap-sm flex w-full flex-col justify-center">
47
+ <UButton
48
+ v-if="hex"
49
+ variant="outline"
50
+ size="sm"
51
+ icon="lucide:copy"
52
+ label="Copy HEX"
53
+ class="w-full xl:w-36"
54
+ @click="copyToClipboard(hex)"
55
+ />
56
+ <UButton
57
+ v-if="rgb"
58
+ variant="outline"
59
+ size="sm"
60
+ icon="lucide:copy"
61
+ label="Copy RGB"
62
+ class="w-full xl:w-36"
63
+ @click="copyToClipboard(rgb)"
64
+ />
65
+ <UButton
66
+ v-if="hsl"
67
+ variant="outline"
68
+ size="sm"
69
+ icon="lucide:copy"
70
+ label="Copy HSL"
71
+ class="w-full xl:w-36"
72
+ @click="copyToClipboard(hsl)"
73
+ />
74
+ <UButton
75
+ v-if="oklch"
76
+ variant="outline"
77
+ size="sm"
78
+ icon="lucide:copy"
79
+ label="Copy OKLCH"
80
+ class="w-full xl:w-36"
81
+ @click="copyToClipboard(oklch)"
82
+ />
83
+ <UButton
84
+ v-if="cmyk"
85
+ variant="outline"
86
+ size="sm"
87
+ icon="lucide:copy"
88
+ label="Copy CMYK"
89
+ class="w-full xl:w-36"
90
+ @click="copyToClipboard(cmyk)"
91
+ />
92
+ </div>
93
+ </div>
94
+ </UCard>
97
95
  </template>
@@ -1,6 +1,4 @@
1
1
  <script setup>
2
- import { useClipboard } from "#imports";
3
- import { useToast } from "#imports";
4
2
  import { computed } from "#imports";
5
3
  const { name, jpg, png, webp, svg } = defineProps({
6
4
  name: { type: String, required: false },
@@ -19,20 +17,20 @@ const image = computed(() => {
19
17
  </script>
20
18
 
21
19
  <template>
22
- <UCard variant="subtle" class="w-fit rounded-none">
20
+ <UCard variant="subtle" class="w-full rounded-none xl:w-fit">
23
21
  <template #header v-if="name">
24
22
  <h3 class="text-lg font-bold">{{ name }}</h3>
25
23
  </template>
26
- <div class="gap-sm flex flex-row">
24
+ <div class="gap-sm flex flex-col items-center xl:flex-row xl:items-start">
27
25
  <NuxtImg :src="image" class="size-48" />
28
- <div class="gap-sm flex flex-col justify-center">
26
+ <div class="gap-sm flex w-full flex-col justify-center">
29
27
  <UButton
30
28
  v-if="jpg"
31
29
  variant="outline"
32
30
  size="sm"
33
31
  icon="lucide:download"
34
32
  label="Download JPG"
35
- class="w-36"
33
+ class="w-full xl:w-36"
36
34
  :to="jpg"
37
35
  target="_blank"
38
36
  />
@@ -42,7 +40,7 @@ const image = computed(() => {
42
40
  size="sm"
43
41
  icon="lucide:download"
44
42
  label="Download PNG"
45
- class="w-36"
43
+ class="w-full xl:w-36"
46
44
  :to="png"
47
45
  target="_blank"
48
46
  />
@@ -52,7 +50,7 @@ const image = computed(() => {
52
50
  size="sm"
53
51
  icon="lucide:download"
54
52
  label="Download WEBP"
55
- class="w-36"
53
+ class="w-full xl:w-36"
56
54
  :to="webp"
57
55
  target="_blank"
58
56
  />
@@ -62,7 +60,7 @@ const image = computed(() => {
62
60
  size="sm"
63
61
  icon="lucide:download"
64
62
  label="Download SVG"
65
- class="w-36"
63
+ class="w-full xl:w-36"
66
64
  :to="svg"
67
65
  target="_blank"
68
66
  />
@@ -1,3 +1,14 @@
1
1
  import { type ClassValue } from "clsx";
2
2
  export declare function cn(...inputs: ClassValue[]): string;
3
3
  export type ObjectValues<T> = T[keyof T];
4
+ /**
5
+ * Converts a string into a URL-friendly slug.
6
+ * - Converts to lowercase.
7
+ * - Replaces non-alphanumeric characters (except hyphens and spaces) with nothing.
8
+ * - Replaces spaces and multiple hyphens with a single hyphen.
9
+ * - Trims leading/trailing hyphens.
10
+ *
11
+ * @param text The string to convert (e.g., "My Awesome Section Title!").
12
+ * @returns The resulting slug (e.g., "my-awesome-section-title").
13
+ */
14
+ export declare function slugify(text: string): string;
@@ -3,3 +3,9 @@ import { twMerge } from "tailwind-merge";
3
3
  export function cn(...inputs) {
4
4
  return twMerge(clsx(inputs));
5
5
  }
6
+ export function slugify(text) {
7
+ if (!text) {
8
+ return "";
9
+ }
10
+ return text.toString().normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().trim().replace(/\s+/g, "-").replace(/[^\w\-]+/g, "").replace(/\-\-+/g, "-").replace(/^-+/, "").replace(/-+$/, "");
11
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rimelight-components",
3
- "version": "1.5.0",
3
+ "version": "1.5.2",
4
4
  "description": "My new Nuxt module",
5
5
  "repository": "Rimelight Entertainment/rimelight-components",
6
6
  "license": "MIT",