rimelight-components 2.0.46 → 2.0.48
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.
- package/dist/module.json +1 -1
- package/dist/module.mjs +4 -2
- package/dist/runtime/components/app/ScrollToTop.d.vue.ts +4 -1
- package/dist/runtime/components/app/ScrollToTop.vue.d.ts +4 -1
- package/dist/runtime/components/blocks/Block.d.vue.ts +4 -17
- package/dist/runtime/components/blocks/Block.vue +25 -46
- package/dist/runtime/components/blocks/Block.vue.d.ts +4 -17
- package/dist/runtime/components/blocks/BlockEditRenderer.vue +55 -0
- package/dist/runtime/components/blocks/BlockEditor.d.vue.ts +15 -0
- package/dist/runtime/components/blocks/BlockEditor.vue +110 -0
- package/dist/runtime/components/blocks/BlockEditor.vue.d.ts +15 -0
- package/dist/runtime/components/{renderers/BlockRenderer.vue → blocks/BlockViewRenderer.vue} +21 -18
- package/dist/runtime/components/blocks/editor/CalloutBlockEditor.vue +1 -1
- package/dist/runtime/components/blocks/editor/CardBlockEditor.vue +1 -1
- package/dist/runtime/components/blocks/editor/ParagraphBlockEditor.d.vue.ts +5 -2
- package/dist/runtime/components/blocks/editor/ParagraphBlockEditor.vue +53 -5
- package/dist/runtime/components/blocks/editor/ParagraphBlockEditor.vue.d.ts +5 -2
- package/dist/runtime/components/blocks/editor/SectionBlockEditor.d.vue.ts +5 -2
- package/dist/runtime/components/blocks/editor/SectionBlockEditor.vue +101 -45
- package/dist/runtime/components/blocks/editor/SectionBlockEditor.vue.d.ts +5 -2
- package/dist/runtime/components/blocks/editor/TestBlockEditor.d.vue.ts +7 -0
- package/dist/runtime/components/blocks/editor/TestBlockEditor.vue +44 -0
- package/dist/runtime/components/blocks/editor/TestBlockEditor.vue.d.ts +7 -0
- package/dist/runtime/components/blocks/renderer/CalloutBlockRenderer.vue +1 -1
- package/dist/runtime/components/blocks/renderer/CardBlockRenderer.vue +1 -1
- package/dist/runtime/components/blocks/renderer/ParagraphBlockRenderer.vue +1 -3
- package/dist/runtime/components/blocks/renderer/SectionBlockRenderer.vue +2 -1
- package/dist/runtime/components/blocks/renderer/TestBlockRenderer.d.vue.ts +4 -0
- package/dist/runtime/components/blocks/renderer/TestBlockRenderer.vue +9 -0
- package/dist/runtime/components/blocks/renderer/TestBlockRenderer.vue.d.ts +4 -0
- package/dist/runtime/components/cards/TeamCard.d.vue.ts +2 -2
- package/dist/runtime/components/cards/TeamCard.vue.d.ts +2 -2
- package/dist/runtime/components/content/Callout.d.vue.ts +2 -2
- package/dist/runtime/components/content/Callout.vue.d.ts +2 -2
- package/dist/runtime/components/content/Section.d.vue.ts +5 -5
- package/dist/runtime/components/content/Section.vue +13 -8
- package/dist/runtime/components/content/Section.vue.d.ts +5 -5
- package/dist/runtime/components/content/Test.d.vue.ts +16 -0
- package/dist/runtime/components/content/Test.vue +13 -0
- package/dist/runtime/components/content/Test.vue.d.ts +16 -0
- package/dist/runtime/composables/useBlockEditor.d.ts +27233 -0
- package/dist/runtime/composables/useBlockEditor.js +163 -0
- package/dist/runtime/types/blocks.d.ts +9 -2
- package/dist/runtime/utils/index.d.ts +0 -3
- package/dist/runtime/utils/index.js +0 -5
- package/dist/runtime/utils/richTextHelpers.d.ts +16 -0
- package/dist/runtime/utils/richTextHelpers.js +17 -0
- package/package.json +19 -25
- package/dist/runtime/components/backgrounds/AnimateGrid.d.vue.ts +0 -17
- package/dist/runtime/components/backgrounds/AnimateGrid.vue +0 -84
- package/dist/runtime/components/backgrounds/AnimateGrid.vue.d.ts +0 -17
- package/dist/runtime/components/backgrounds/FlickeringGrid.d.vue.ts +0 -13
- package/dist/runtime/components/backgrounds/FlickeringGrid.vue +0 -136
- package/dist/runtime/components/backgrounds/FlickeringGrid.vue.d.ts +0 -13
- package/dist/runtime/components/backgrounds/InteractiveGrid.d.vue.ts +0 -11
- package/dist/runtime/components/backgrounds/InteractiveGrid.vue +0 -58
- package/dist/runtime/components/backgrounds/InteractiveGrid.vue.d.ts +0 -11
- package/dist/runtime/components/renderers/BlockEditor.vue +0 -63
- package/dist/runtime/composables/useBlockContentEditor.d.ts +0 -32
- package/dist/runtime/composables/useBlockContentEditor.js +0 -63
- /package/dist/runtime/components/{renderers/BlockEditor.d.vue.ts → blocks/BlockEditRenderer.d.vue.ts} +0 -0
- /package/dist/runtime/components/{renderers/BlockEditor.vue.d.ts → blocks/BlockEditRenderer.vue.d.ts} +0 -0
- /package/dist/runtime/components/{renderers/BlockRenderer.d.vue.ts → blocks/BlockViewRenderer.d.vue.ts} +0 -0
- /package/dist/runtime/components/{renderers/BlockRenderer.vue.d.ts → blocks/BlockViewRenderer.vue.d.ts} +0 -0
- /package/dist/runtime/components/{renderers → blocks}/TOC.d.vue.ts +0 -0
- /package/dist/runtime/components/{renderers → blocks}/TOC.vue +0 -0
- /package/dist/runtime/components/{renderers → blocks}/TOC.vue.d.ts +0 -0
- /package/dist/runtime/components/{renderers → blocks}/TextRenderer.d.vue.ts +0 -0
- /package/dist/runtime/components/{renderers → blocks}/TextRenderer.vue +0 -0
- /package/dist/runtime/components/{renderers → blocks}/TextRenderer.vue.d.ts +0 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { computed, ref, shallowRef } from "vue";
|
|
2
|
+
import { v7 as uuidv7 } from "uuid";
|
|
3
|
+
function findBlockLocation(blocks, id) {
|
|
4
|
+
for (let i = 0; i < blocks.length; i++) {
|
|
5
|
+
const block = blocks[i];
|
|
6
|
+
if (!block) continue;
|
|
7
|
+
if (block.id === id) {
|
|
8
|
+
return { parentArray: blocks, index: i };
|
|
9
|
+
}
|
|
10
|
+
if ("children" in block.props && Array.isArray(block.props.children)) {
|
|
11
|
+
const result = findBlockLocation(block.props.children, id);
|
|
12
|
+
if (result) return result;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
function regenerateIds(block) {
|
|
18
|
+
block.id = uuidv7();
|
|
19
|
+
if ("children" in block.props && Array.isArray(block.props.children)) {
|
|
20
|
+
block.props.children.forEach((child) => regenerateIds(child));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function useBlockEditor(initialBlocks, maxHistorySize = 100) {
|
|
24
|
+
const history = shallowRef([]);
|
|
25
|
+
const future = shallowRef([]);
|
|
26
|
+
const committedBlocks = ref(JSON.parse(JSON.stringify(initialBlocks.value)));
|
|
27
|
+
const captureSnapshot = () => {
|
|
28
|
+
const snapshot = JSON.parse(JSON.stringify(initialBlocks.value));
|
|
29
|
+
future.value = [];
|
|
30
|
+
const newHistory = [...history.value, snapshot];
|
|
31
|
+
if (newHistory.length > maxHistorySize) {
|
|
32
|
+
newHistory.shift();
|
|
33
|
+
}
|
|
34
|
+
history.value = newHistory;
|
|
35
|
+
};
|
|
36
|
+
const executeMutation = (mutationFn) => {
|
|
37
|
+
captureSnapshot();
|
|
38
|
+
mutationFn();
|
|
39
|
+
};
|
|
40
|
+
const undo = () => {
|
|
41
|
+
if (history.value.length === 0) return;
|
|
42
|
+
const currentState = JSON.parse(JSON.stringify(initialBlocks.value));
|
|
43
|
+
future.value = [currentState, ...future.value];
|
|
44
|
+
const previousState = history.value[history.value.length - 1];
|
|
45
|
+
if (previousState) {
|
|
46
|
+
initialBlocks.value.splice(0, initialBlocks.value.length, ...previousState);
|
|
47
|
+
const newHistory = [...history.value];
|
|
48
|
+
newHistory.pop();
|
|
49
|
+
history.value = newHistory;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
const redo = () => {
|
|
53
|
+
if (future.value.length === 0) return;
|
|
54
|
+
const currentState = JSON.parse(JSON.stringify(initialBlocks.value));
|
|
55
|
+
let newHistory = [...history.value, currentState];
|
|
56
|
+
if (newHistory.length > maxHistorySize) {
|
|
57
|
+
newHistory.shift();
|
|
58
|
+
}
|
|
59
|
+
history.value = newHistory;
|
|
60
|
+
const nextState = future.value[0];
|
|
61
|
+
if (nextState) {
|
|
62
|
+
initialBlocks.value.splice(0, initialBlocks.value.length, ...nextState);
|
|
63
|
+
const newFuture = [...future.value];
|
|
64
|
+
newFuture.shift();
|
|
65
|
+
future.value = newFuture;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
const canUndo = computed(() => history.value.length > 0);
|
|
69
|
+
const canRedo = computed(() => future.value.length > 0);
|
|
70
|
+
const updateBlockProps = (id, newProps) => {
|
|
71
|
+
executeMutation(() => {
|
|
72
|
+
const loc = findBlockLocation(initialBlocks.value, id);
|
|
73
|
+
if (!loc) return;
|
|
74
|
+
const oldBlock = loc.parentArray[loc.index];
|
|
75
|
+
if (!oldBlock) return;
|
|
76
|
+
const newPropsObject = {
|
|
77
|
+
...oldBlock.props,
|
|
78
|
+
...newProps
|
|
79
|
+
};
|
|
80
|
+
const newBlock = {
|
|
81
|
+
id: oldBlock.id,
|
|
82
|
+
// Explicitly carry over the required BaseBlock properties
|
|
83
|
+
type: oldBlock.type,
|
|
84
|
+
// Explicitly carry over the required BaseBlock properties
|
|
85
|
+
props: newPropsObject
|
|
86
|
+
};
|
|
87
|
+
loc.parentArray.splice(loc.index, 1, newBlock);
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
const removeBlock = (id) => {
|
|
91
|
+
executeMutation(() => {
|
|
92
|
+
const loc = findBlockLocation(initialBlocks.value, id);
|
|
93
|
+
if (!loc) return;
|
|
94
|
+
loc.parentArray.splice(loc.index, 1);
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
const moveBlock = (id, direction) => {
|
|
98
|
+
executeMutation(() => {
|
|
99
|
+
const loc = findBlockLocation(initialBlocks.value, id);
|
|
100
|
+
if (!loc) return;
|
|
101
|
+
const { parentArray, index } = loc;
|
|
102
|
+
const newIndex = index + direction;
|
|
103
|
+
if (newIndex >= 0 && newIndex < parentArray.length) {
|
|
104
|
+
const movedBlock = parentArray.splice(index, 1)[0];
|
|
105
|
+
if (movedBlock) {
|
|
106
|
+
parentArray.splice(newIndex, 0, movedBlock);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
};
|
|
111
|
+
const duplicateBlock = (id) => {
|
|
112
|
+
executeMutation(() => {
|
|
113
|
+
const loc = findBlockLocation(initialBlocks.value, id);
|
|
114
|
+
if (!loc) return;
|
|
115
|
+
const original = loc.parentArray[loc.index];
|
|
116
|
+
const clone = JSON.parse(JSON.stringify(original));
|
|
117
|
+
regenerateIds(clone);
|
|
118
|
+
loc.parentArray.splice(loc.index + 1, 0, clone);
|
|
119
|
+
});
|
|
120
|
+
};
|
|
121
|
+
const insertBlock = (newBlockType, targetId = null, position = "after") => {
|
|
122
|
+
executeMutation(() => {
|
|
123
|
+
const newBlock = {
|
|
124
|
+
id: uuidv7(),
|
|
125
|
+
type: newBlockType,
|
|
126
|
+
props: getDefaultPropsForType(newBlockType)
|
|
127
|
+
};
|
|
128
|
+
if (!targetId) {
|
|
129
|
+
initialBlocks.value.push(newBlock);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const loc = findBlockLocation(initialBlocks.value, targetId);
|
|
133
|
+
if (loc) {
|
|
134
|
+
const insertIndex = position === "after" ? loc.index + 1 : loc.index;
|
|
135
|
+
loc.parentArray.splice(insertIndex, 0, newBlock);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
};
|
|
139
|
+
const commitChanges = () => {
|
|
140
|
+
const committedSnapshot = JSON.parse(JSON.stringify(initialBlocks.value));
|
|
141
|
+
committedBlocks.value = committedSnapshot;
|
|
142
|
+
return committedSnapshot;
|
|
143
|
+
};
|
|
144
|
+
return {
|
|
145
|
+
// Mutations
|
|
146
|
+
updateBlockProps,
|
|
147
|
+
removeBlock,
|
|
148
|
+
moveBlock,
|
|
149
|
+
duplicateBlock,
|
|
150
|
+
insertBlock,
|
|
151
|
+
// History
|
|
152
|
+
undo,
|
|
153
|
+
redo,
|
|
154
|
+
canUndo,
|
|
155
|
+
canRedo,
|
|
156
|
+
// State
|
|
157
|
+
committedBlocks,
|
|
158
|
+
commitChanges
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
function getDefaultPropsForType(type) {
|
|
162
|
+
return { children: [] };
|
|
163
|
+
}
|
|
@@ -6,7 +6,7 @@ export type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
|
|
|
6
6
|
* All valid block types the application can render.
|
|
7
7
|
* This union type is the single source of truth for component names.
|
|
8
8
|
*/
|
|
9
|
-
export type BlockType = "SectionBlock" | "ParagraphBlock" | "CalloutBlock" | "ImageBlock" | "QuoteBlock" | "UnorderedListBlock" | "CardBlock" | "CollapsibleCardBlock";
|
|
9
|
+
export type BlockType = "TestBlock" | "SectionBlock" | "ParagraphBlock" | "CalloutBlock" | "ImageBlock" | "QuoteBlock" | "UnorderedListBlock" | "CardBlock" | "CollapsibleCardBlock";
|
|
10
10
|
/**
|
|
11
11
|
* Defines the common structure for any content block object.
|
|
12
12
|
* The 'type' must be one of the registered BlockType values.
|
|
@@ -16,6 +16,9 @@ export interface BaseBlock {
|
|
|
16
16
|
type: BlockType;
|
|
17
17
|
props: Record<string, any>;
|
|
18
18
|
}
|
|
19
|
+
export interface TestBlockProps {
|
|
20
|
+
text: string;
|
|
21
|
+
}
|
|
19
22
|
export interface SectionBlockProps {
|
|
20
23
|
level: HeadingLevel;
|
|
21
24
|
title: string;
|
|
@@ -56,6 +59,10 @@ export interface CollapsibleCardBlockProps {
|
|
|
56
59
|
name?: string;
|
|
57
60
|
children: Block[];
|
|
58
61
|
}
|
|
62
|
+
export interface TestBlock extends BaseBlock {
|
|
63
|
+
type: "TestBlock";
|
|
64
|
+
props: TestBlockProps;
|
|
65
|
+
}
|
|
59
66
|
export interface SectionBlock extends BaseBlock {
|
|
60
67
|
type: "SectionBlock";
|
|
61
68
|
props: SectionBlockProps;
|
|
@@ -92,7 +99,7 @@ export interface QuoteContentBlock extends BaseBlock {
|
|
|
92
99
|
* The full union type for a single block. This allows for type-checking the
|
|
93
100
|
* payload based on the block 'type'.
|
|
94
101
|
*/
|
|
95
|
-
export type Block = SectionBlock | ParagraphContentBlock | CalloutContentBlock | ImageContentBlock | QuoteContentBlock | UnorderedListContentBlock | CardBlockContent | CollapsibleCardContentBlock;
|
|
102
|
+
export type Block = TestBlock | SectionBlock | ParagraphContentBlock | CalloutContentBlock | ImageContentBlock | QuoteContentBlock | UnorderedListContentBlock | CardBlockContent | CollapsibleCardContentBlock;
|
|
96
103
|
/**
|
|
97
104
|
* Text Rendering Components
|
|
98
105
|
*/
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { RichTextContent } from "../types/blocks.js";
|
|
2
|
+
/**
|
|
3
|
+
* Helper: Converts RichTextContent array into a plain string for a simple contenteditable area.
|
|
4
|
+
* For complex editors, this would generate full HTML/DOM nodes, preserving links/mentions.
|
|
5
|
+
* For now, we only extract the text content.
|
|
6
|
+
*/
|
|
7
|
+
export declare function richTextToHtml(content: RichTextContent): string;
|
|
8
|
+
/**
|
|
9
|
+
* Helper: Converts a plain string from a contenteditable area back into RichTextContent.
|
|
10
|
+
*
|
|
11
|
+
* NOTE: For full rich text support, this function must parse complex HTML (<a>, <b>, <span>)
|
|
12
|
+
* and generate the correct InlineLink, InlineMention, and InlineText objects.
|
|
13
|
+
* Since we are only simulating the text content change, we convert the entire string
|
|
14
|
+
* into a single InlineText element.
|
|
15
|
+
*/
|
|
16
|
+
export declare function parseHtmlToRichText(html: string): RichTextContent;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { v7 as uuidv7 } from "uuid";
|
|
2
|
+
export function richTextToHtml(content) {
|
|
3
|
+
return content.map((item) => item.props.content).join("");
|
|
4
|
+
}
|
|
5
|
+
export function parseHtmlToRichText(html) {
|
|
6
|
+
if (html.trim().length === 0) {
|
|
7
|
+
return [];
|
|
8
|
+
}
|
|
9
|
+
const newTextNode = {
|
|
10
|
+
id: uuidv7(),
|
|
11
|
+
type: "text",
|
|
12
|
+
props: {
|
|
13
|
+
content: html.trim()
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
return [newTextNode];
|
|
17
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rimelight-components",
|
|
3
3
|
"description": "A component library by Rimelight Entertainment.",
|
|
4
|
-
"version": "2.0.
|
|
4
|
+
"version": "2.0.48",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -79,38 +79,32 @@
|
|
|
79
79
|
},
|
|
80
80
|
"dependencies": {
|
|
81
81
|
"@nuxt/image": "^1.11.0",
|
|
82
|
-
"@nuxt/kit": "^4.2.
|
|
83
|
-
"@nuxt/ui": "^4.1
|
|
84
|
-
"@nuxtjs/i18n": "^10.1
|
|
85
|
-
"@vueuse/core": "^14.
|
|
86
|
-
"@vueuse/nuxt": "^14.
|
|
82
|
+
"@nuxt/kit": "^4.2.2",
|
|
83
|
+
"@nuxt/ui": "^4.2.1",
|
|
84
|
+
"@nuxtjs/i18n": "^10.2.1",
|
|
85
|
+
"@vueuse/core": "^14.1.0",
|
|
86
|
+
"@vueuse/nuxt": "^14.1.0",
|
|
87
87
|
"date-fns": "^4.1.0",
|
|
88
88
|
"defu": "^6.1.4",
|
|
89
|
-
"nuxt": "^4.2.
|
|
90
|
-
"
|
|
91
|
-
"
|
|
92
|
-
"vue": "^3.5.
|
|
89
|
+
"nuxt": "^4.2.2",
|
|
90
|
+
"tailwind-variants": "^3.2.2",
|
|
91
|
+
"uuid": "^13.0.0",
|
|
92
|
+
"vue": "^3.5.25"
|
|
93
93
|
},
|
|
94
94
|
"devDependencies": {
|
|
95
|
-
"@nuxt/devtools": "^3.
|
|
95
|
+
"@nuxt/devtools": "^3.1.1",
|
|
96
96
|
"@nuxt/module-builder": "^1.0.2",
|
|
97
|
-
"@nuxt/schema": "^4.2.
|
|
98
|
-
"@nuxt/test-utils": "^3.
|
|
99
|
-
"@prettier/plugin-oxc": "^0.0.4",
|
|
97
|
+
"@nuxt/schema": "^4.2.2",
|
|
98
|
+
"@nuxt/test-utils": "^3.21.0",
|
|
100
99
|
"@types/node": "latest",
|
|
101
100
|
"changelogen": "^0.6.2",
|
|
102
|
-
"
|
|
103
|
-
"
|
|
104
|
-
"
|
|
105
|
-
"
|
|
106
|
-
"prettier": "^3.6.2",
|
|
107
|
-
"prettier-plugin-tailwindcss": "^0.7.1",
|
|
108
|
-
"release-it": "^19.0.5",
|
|
109
|
-
"tailwind-merge": "^3.3.1",
|
|
110
|
-
"tw-animate-css": "^1.4.0",
|
|
101
|
+
"oxfmt": "^0.18.0",
|
|
102
|
+
"oxlint": "^1.33.0",
|
|
103
|
+
"release-it": "^19.1.0",
|
|
104
|
+
"tailwind-merge": "^3.4.0",
|
|
111
105
|
"typescript": "~5.9.3",
|
|
112
|
-
"vitest": "^4.0.
|
|
113
|
-
"vue-tsc": "^3.1.
|
|
106
|
+
"vitest": "^4.0.15",
|
|
107
|
+
"vue-tsc": "^3.1.8"
|
|
114
108
|
},
|
|
115
109
|
"trustedDependencies": [
|
|
116
110
|
"@parcel/watcher",
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
interface Cards {
|
|
2
|
-
icon: string;
|
|
3
|
-
href?: string;
|
|
4
|
-
iconClass?: string;
|
|
5
|
-
}
|
|
6
|
-
interface Props {
|
|
7
|
-
class?: string;
|
|
8
|
-
textGlowStartColor?: string;
|
|
9
|
-
perspective?: number;
|
|
10
|
-
textGlowEndColor?: string;
|
|
11
|
-
cards: Cards[];
|
|
12
|
-
rotateX?: number;
|
|
13
|
-
rotateY?: number;
|
|
14
|
-
}
|
|
15
|
-
declare const __VLS_export: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
16
|
-
declare const _default: typeof __VLS_export;
|
|
17
|
-
export default _default;
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
<script setup>
|
|
2
|
-
import { onMounted, ref, watch } from "vue";
|
|
3
|
-
import { cn } from "../../utils";
|
|
4
|
-
import { useMouseInElement, useDebounceFn } from "@vueuse/core";
|
|
5
|
-
const card = ref();
|
|
6
|
-
const props = defineProps({
|
|
7
|
-
class: { type: String, required: false },
|
|
8
|
-
textGlowStartColor: { type: String, required: false, default: "var(--color-primary-300, #38ef7d)" },
|
|
9
|
-
perspective: { type: Number, required: false, default: 750 },
|
|
10
|
-
textGlowEndColor: { type: String, required: false, default: "var(--color-primary-500, #38ef7d)" },
|
|
11
|
-
cards: { type: Array, required: true },
|
|
12
|
-
rotateX: { type: Number, required: false, default: -1 },
|
|
13
|
-
rotateY: { type: Number, required: false, default: -15 }
|
|
14
|
-
});
|
|
15
|
-
function adjacentCardItems(i) {
|
|
16
|
-
return [i - 1, i + 1, i - 4, i + 4].filter((index) => {
|
|
17
|
-
if (index < 0 || index > 15) return false;
|
|
18
|
-
if (i % 4 === 0 && index === i - 1) return false;
|
|
19
|
-
return !(i % 4 === 3 && index === i + 1);
|
|
20
|
-
}).map((index) => card.value?.[index]);
|
|
21
|
-
}
|
|
22
|
-
function removeCardClasses(el, adjacentCards) {
|
|
23
|
-
el.classList.remove("card-raised-big");
|
|
24
|
-
adjacentCards.forEach((adjacentCard) => {
|
|
25
|
-
adjacentCard?.classList.remove("card-raised-small");
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
onMounted(() => {
|
|
29
|
-
card.value?.forEach((el, i) => {
|
|
30
|
-
const { isOutside } = useMouseInElement(el);
|
|
31
|
-
const adjacentCards = adjacentCardItems(i);
|
|
32
|
-
const removeClasses = useDebounceFn(
|
|
33
|
-
() => removeCardClasses(el, adjacentCards),
|
|
34
|
-
200
|
|
35
|
-
);
|
|
36
|
-
watch(isOutside, (isOutside2) => {
|
|
37
|
-
if (!isOutside2) {
|
|
38
|
-
el.classList.add("card-raised-big");
|
|
39
|
-
adjacentCards.forEach((adjacentCard) => {
|
|
40
|
-
adjacentCard?.classList.add("card-raised-small");
|
|
41
|
-
});
|
|
42
|
-
} else {
|
|
43
|
-
removeClasses();
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
</script>
|
|
49
|
-
|
|
50
|
-
<template>
|
|
51
|
-
<UContainer :class="cn('relative block', props.class)">
|
|
52
|
-
<div
|
|
53
|
-
:class="
|
|
54
|
-
cn(
|
|
55
|
-
'relative grid w-full max-w-full items-center justify-center gap-2',
|
|
56
|
-
props.cards.length < 4 ? `grid-cols-${props.cards.length}` : 'grid-cols-4'
|
|
57
|
-
)
|
|
58
|
-
"
|
|
59
|
-
:style="{
|
|
60
|
-
transform: `perspective(${props.perspective}px) rotateX(${props.rotateX}deg) rotateY(${props.rotateY}deg)`
|
|
61
|
-
}"
|
|
62
|
-
>
|
|
63
|
-
<div
|
|
64
|
-
v-for="(item, index) in props.cards"
|
|
65
|
-
:key="index"
|
|
66
|
-
ref="card"
|
|
67
|
-
class="card block rounded border border-transparent px-2 py-2 transition-all duration-200"
|
|
68
|
-
:style="{ zIndex: index + 1 }"
|
|
69
|
-
>
|
|
70
|
-
<NuxtLink :to="item.href" target="_blank" :index="index">
|
|
71
|
-
<UIcon
|
|
72
|
-
:name="item.icon"
|
|
73
|
-
class="icon mx-auto h-12 w-auto p-2 lg:h-16 lg:p-3"
|
|
74
|
-
:class="item.iconClass"
|
|
75
|
-
/>
|
|
76
|
-
</NuxtLink>
|
|
77
|
-
</div>
|
|
78
|
-
</div>
|
|
79
|
-
</UContainer>
|
|
80
|
-
</template>
|
|
81
|
-
|
|
82
|
-
<style scoped>
|
|
83
|
-
.icon svg{display:block;margin:0 auto;max-height:100%;max-width:100%}.grid-transform:before{background:radial-gradient(circle,#d9fbe8 0,#fff 70%,transparent 100%);content:"";height:120%;left:-10%;opacity:.5;position:absolute;top:-10%;width:150%;z-index:-1}.dark .grid-transform:before{background:radial-gradient(circle,var(--color-neutral-700) 0,var(--color-neutral-800) 70%,transparent 100%)}.card{box-shadow:2px 2px 5px var(--color-primary-300,#38ef7d),3px 3px 10px var(--color-primary-400,#38ef7d),6px 6px 20px var(--color-primary-500,#38ef7d)}.dark .card{box-shadow:2px 2px 5px oklch(from var(--color-primary-900) l c h/.25),3px 3px 10px oklch(from var(--color-primary-900) l c h/.25),6px 6px 20px oklch(from var(--color-primary-900) l c h/.25)}.card svg{opacity:.7;transition:.2s}.dark .card:hover{box-shadow:3px 3px 5px #1f2937,5px 5px 10px #1f2937,10px 10px 20px #1f2937}.card:hover svg{opacity:1}.card svg{shape-rendering:geometricPrecision}.card-raised-small{animation:text-glow-small 1.5s ease-in-out infinite alternate;border:1px solid var(--color-primary-300);transform:scale(1.05) translateX(-5px) translateY(-5px) translateZ(0)}.card-raised-big{animation:text-glow 1.5s ease-in-out infinite alternate;background-color:#fff;transform:scale(1.15) translateX(-20px) translateY(-20px) translateZ(15px)}.card-raised-big,.dark .card-raised-big{border:1px solid v-bind(textGlowStartColor)}.dark .card-raised-big{background-color:oklch(from var(--color-primary-500) l c h/.5)}.dark .card-raised-small{border:1px solid v-bind(textGlowStartColor);transform:scale(1.05) translateX(-5px) translateY(-5px) translateZ(0)}@keyframes text-glow{0%{filter:drop-shadow(0 0 2px v-bind(textGlowStartColor))}to{filter:drop-shadow(0 1px 8px v-bind(textGlowEndColor))}}@keyframes text-glow-small{0%{filter:drop-shadow(0 0 2px v-bind(textGlowEndColor))}to{filter:drop-shadow(0 1px 4px v-bind(textGlowEndColor))}}
|
|
84
|
-
</style>
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
interface Cards {
|
|
2
|
-
icon: string;
|
|
3
|
-
href?: string;
|
|
4
|
-
iconClass?: string;
|
|
5
|
-
}
|
|
6
|
-
interface Props {
|
|
7
|
-
class?: string;
|
|
8
|
-
textGlowStartColor?: string;
|
|
9
|
-
perspective?: number;
|
|
10
|
-
textGlowEndColor?: string;
|
|
11
|
-
cards: Cards[];
|
|
12
|
-
rotateX?: number;
|
|
13
|
-
rotateY?: number;
|
|
14
|
-
}
|
|
15
|
-
declare const __VLS_export: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
16
|
-
declare const _default: typeof __VLS_export;
|
|
17
|
-
export default _default;
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
interface FlickeringGridProps {
|
|
2
|
-
squareSize?: number;
|
|
3
|
-
gridGap?: number;
|
|
4
|
-
flickerChance?: number;
|
|
5
|
-
color?: string;
|
|
6
|
-
width?: number;
|
|
7
|
-
height?: number;
|
|
8
|
-
class?: string;
|
|
9
|
-
maxOpacity?: number;
|
|
10
|
-
}
|
|
11
|
-
declare const __VLS_export: import("vue").DefineComponent<FlickeringGridProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<FlickeringGridProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
12
|
-
declare const _default: typeof __VLS_export;
|
|
13
|
-
export default _default;
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
<script setup>
|
|
2
|
-
import { cn } from "../../utils";
|
|
3
|
-
import { ref, onMounted, onBeforeUnmount, toRefs, computed } from "vue";
|
|
4
|
-
const props = defineProps({
|
|
5
|
-
squareSize: { type: Number, required: false, default: 4 },
|
|
6
|
-
gridGap: { type: Number, required: false, default: 6 },
|
|
7
|
-
flickerChance: { type: Number, required: false, default: 0.3 },
|
|
8
|
-
color: { type: String, required: false, default: "rgb(0, 0, 0)" },
|
|
9
|
-
width: { type: Number, required: false },
|
|
10
|
-
height: { type: Number, required: false },
|
|
11
|
-
class: { type: String, required: false },
|
|
12
|
-
maxOpacity: { type: Number, required: false, default: 0.3 }
|
|
13
|
-
});
|
|
14
|
-
const { squareSize, gridGap, flickerChance, color, maxOpacity, width, height } = toRefs(props);
|
|
15
|
-
const containerRef = ref();
|
|
16
|
-
const canvasRef = ref();
|
|
17
|
-
const context = ref();
|
|
18
|
-
const isInView = ref(false);
|
|
19
|
-
const canvasSize = ref({ width: 0, height: 0 });
|
|
20
|
-
const computedColor = computed(() => {
|
|
21
|
-
if (!context.value) return "rgba(255, 0, 0,";
|
|
22
|
-
const hex = color.value.replace(/^#/, "");
|
|
23
|
-
const bigint = Number.parseInt(hex, 16);
|
|
24
|
-
const r = bigint >> 16 & 255;
|
|
25
|
-
const g = bigint >> 8 & 255;
|
|
26
|
-
const b = bigint & 255;
|
|
27
|
-
return `rgba(${r}, ${g}, ${b},`;
|
|
28
|
-
});
|
|
29
|
-
function setupCanvas(canvas, width2, height2) {
|
|
30
|
-
const dpr = window.devicePixelRatio || 1;
|
|
31
|
-
canvas.width = width2 * dpr;
|
|
32
|
-
canvas.height = height2 * dpr;
|
|
33
|
-
canvas.style.width = `${width2}px`;
|
|
34
|
-
canvas.style.height = `${height2}px`;
|
|
35
|
-
const cols = Math.floor(width2 / (squareSize.value + gridGap.value));
|
|
36
|
-
const rows = Math.floor(height2 / (squareSize.value + gridGap.value));
|
|
37
|
-
const squares = new Float32Array(cols * rows);
|
|
38
|
-
for (let i = 0; i < squares.length; i++) {
|
|
39
|
-
squares[i] = Math.random() * maxOpacity.value;
|
|
40
|
-
}
|
|
41
|
-
return { cols, rows, squares, dpr };
|
|
42
|
-
}
|
|
43
|
-
function updateSquares(squares, deltaTime) {
|
|
44
|
-
for (let i = 0; i < squares.length; i++) {
|
|
45
|
-
if (Math.random() < flickerChance.value * deltaTime) {
|
|
46
|
-
squares[i] = Math.random() * maxOpacity.value;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
function drawGrid(ctx, width2, height2, cols, rows, squares, dpr) {
|
|
51
|
-
ctx.clearRect(0, 0, width2, height2);
|
|
52
|
-
ctx.fillStyle = "transparent";
|
|
53
|
-
ctx.fillRect(0, 0, width2, height2);
|
|
54
|
-
for (let i = 0; i < cols; i++) {
|
|
55
|
-
for (let j = 0; j < rows; j++) {
|
|
56
|
-
const opacity = squares[i * rows + j];
|
|
57
|
-
ctx.fillStyle = `${computedColor.value}${opacity})`;
|
|
58
|
-
ctx.fillRect(
|
|
59
|
-
i * (squareSize.value + gridGap.value) * dpr,
|
|
60
|
-
j * (squareSize.value + gridGap.value) * dpr,
|
|
61
|
-
squareSize.value * dpr,
|
|
62
|
-
squareSize.value * dpr
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
const gridParams = ref();
|
|
68
|
-
function updateCanvasSize() {
|
|
69
|
-
const newWidth = width.value || containerRef.value.clientWidth;
|
|
70
|
-
const newHeight = height.value || containerRef.value.clientHeight;
|
|
71
|
-
canvasSize.value = { width: newWidth, height: newHeight };
|
|
72
|
-
gridParams.value = setupCanvas(canvasRef.value, newWidth, newHeight);
|
|
73
|
-
}
|
|
74
|
-
let animationFrameId;
|
|
75
|
-
let resizeObserver;
|
|
76
|
-
let intersectionObserver;
|
|
77
|
-
let lastTime = 0;
|
|
78
|
-
function animate(time) {
|
|
79
|
-
if (!isInView.value) return;
|
|
80
|
-
const deltaTime = (time - lastTime) / 1e3;
|
|
81
|
-
lastTime = time;
|
|
82
|
-
updateSquares(gridParams.value.squares, deltaTime);
|
|
83
|
-
drawGrid(
|
|
84
|
-
context.value,
|
|
85
|
-
canvasRef.value.width,
|
|
86
|
-
canvasRef.value.height,
|
|
87
|
-
gridParams.value.cols,
|
|
88
|
-
gridParams.value.rows,
|
|
89
|
-
gridParams.value.squares,
|
|
90
|
-
gridParams.value.dpr
|
|
91
|
-
);
|
|
92
|
-
animationFrameId = requestAnimationFrame(animate);
|
|
93
|
-
}
|
|
94
|
-
onMounted(() => {
|
|
95
|
-
if (!canvasRef.value || !containerRef.value) return;
|
|
96
|
-
context.value = canvasRef.value.getContext("2d");
|
|
97
|
-
if (!context.value) return;
|
|
98
|
-
updateCanvasSize();
|
|
99
|
-
resizeObserver = new ResizeObserver(() => {
|
|
100
|
-
updateCanvasSize();
|
|
101
|
-
});
|
|
102
|
-
intersectionObserver = new IntersectionObserver(
|
|
103
|
-
(entries) => {
|
|
104
|
-
const entry = entries[0];
|
|
105
|
-
if (entry) {
|
|
106
|
-
isInView.value = entry.isIntersecting;
|
|
107
|
-
if (isInView.value && !animationFrameId) {
|
|
108
|
-
lastTime = performance.now();
|
|
109
|
-
animationFrameId = requestAnimationFrame(animate);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
},
|
|
113
|
-
{ threshold: 0 }
|
|
114
|
-
);
|
|
115
|
-
resizeObserver.observe(containerRef.value);
|
|
116
|
-
intersectionObserver.observe(canvasRef.value);
|
|
117
|
-
});
|
|
118
|
-
onBeforeUnmount(() => {
|
|
119
|
-
if (animationFrameId) {
|
|
120
|
-
cancelAnimationFrame(animationFrameId);
|
|
121
|
-
}
|
|
122
|
-
resizeObserver?.disconnect();
|
|
123
|
-
intersectionObserver?.disconnect();
|
|
124
|
-
});
|
|
125
|
-
</script>
|
|
126
|
-
|
|
127
|
-
<template>
|
|
128
|
-
<div ref="containerRef" :class="cn('h-full w-full', props.class)">
|
|
129
|
-
<canvas
|
|
130
|
-
ref="canvasRef"
|
|
131
|
-
class="pointer-events-none"
|
|
132
|
-
:width="canvasSize.width"
|
|
133
|
-
:height="canvasSize.height"
|
|
134
|
-
/>
|
|
135
|
-
</div>
|
|
136
|
-
</template>
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
interface FlickeringGridProps {
|
|
2
|
-
squareSize?: number;
|
|
3
|
-
gridGap?: number;
|
|
4
|
-
flickerChance?: number;
|
|
5
|
-
color?: string;
|
|
6
|
-
width?: number;
|
|
7
|
-
height?: number;
|
|
8
|
-
class?: string;
|
|
9
|
-
maxOpacity?: number;
|
|
10
|
-
}
|
|
11
|
-
declare const __VLS_export: import("vue").DefineComponent<FlickeringGridProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<FlickeringGridProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
12
|
-
declare const _default: typeof __VLS_export;
|
|
13
|
-
export default _default;
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { type HTMLAttributes } from "vue";
|
|
2
|
-
interface InteractiveGridPatternProps {
|
|
3
|
-
className?: HTMLAttributes["class"];
|
|
4
|
-
squaresClassName?: HTMLAttributes["class"];
|
|
5
|
-
width?: number;
|
|
6
|
-
height?: number;
|
|
7
|
-
squares?: [number, number];
|
|
8
|
-
}
|
|
9
|
-
declare const __VLS_export: import("vue").DefineComponent<InteractiveGridPatternProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<InteractiveGridPatternProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
10
|
-
declare const _default: typeof __VLS_export;
|
|
11
|
-
export default _default;
|