@bupple/vss-plugin-typography 1.0.0
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/chunk-5IHSZBVF.cjs +2174 -0
- package/dist/chunk-YI7CKNOM.js +2169 -0
- package/dist/index.cjs +49 -0
- package/dist/index.d.cts +67 -0
- package/dist/index.d.ts +67 -0
- package/dist/index.js +40 -0
- package/dist/panel-4TAKN5WR.cjs +320 -0
- package/dist/panel-NCQBLXF6.js +318 -0
- package/dist/panel-mobile-3RLRYQAK.cjs +198 -0
- package/dist/panel-mobile-4A4K4QIO.js +196 -0
- package/package.json +42 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { getCategories } from './chunk-YI7CKNOM.js';
|
|
2
|
+
import { createDefaultTransform } from '@bupple/vss/core';
|
|
3
|
+
import { useIconMap, useStudioStore, findAvailableTrack, ensureFontLoaded } from '@bupple/vss/ui';
|
|
4
|
+
import { useState, useMemo, useCallback, useEffect } from 'react';
|
|
5
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
function TextPanelMobile() {
|
|
8
|
+
const icons = useIconMap();
|
|
9
|
+
const settings = useStudioStore((s) => s.project.settings);
|
|
10
|
+
const addItem = useStudioStore((s) => s.addItem);
|
|
11
|
+
const addTrack = useStudioStore((s) => s.addTrack);
|
|
12
|
+
const currentFrame = useStudioStore((s) => s.player.currentFrame);
|
|
13
|
+
const tracks = useStudioStore((s) => s.timeline.tracks);
|
|
14
|
+
const items = useStudioStore((s) => s.timeline.items);
|
|
15
|
+
const itemIdsByTrack = useStudioStore((s) => s.timeline.itemIdsByTrack);
|
|
16
|
+
const selectItem = useStudioStore((s) => s.selectItem);
|
|
17
|
+
const categories = getCategories();
|
|
18
|
+
const [search, setSearch] = useState("");
|
|
19
|
+
useMemo(
|
|
20
|
+
() => categories.flatMap((c) => c.presets),
|
|
21
|
+
[categories]
|
|
22
|
+
);
|
|
23
|
+
const filteredCategories = useMemo(() => {
|
|
24
|
+
if (!search.trim()) return categories;
|
|
25
|
+
const q = search.toLowerCase();
|
|
26
|
+
return categories.map((cat) => ({
|
|
27
|
+
...cat,
|
|
28
|
+
presets: cat.presets.filter(
|
|
29
|
+
(p) => p.label.toLowerCase().includes(q) || (p.style.fontFamily ?? "").toLowerCase().includes(q)
|
|
30
|
+
)
|
|
31
|
+
})).filter((cat) => cat.presets.length > 0);
|
|
32
|
+
}, [categories, search]);
|
|
33
|
+
const handleAddText = useCallback(
|
|
34
|
+
(preset) => {
|
|
35
|
+
const fps = settings.fps;
|
|
36
|
+
const durationFrames = fps * 5;
|
|
37
|
+
const available = findAvailableTrack(
|
|
38
|
+
tracks,
|
|
39
|
+
itemIdsByTrack,
|
|
40
|
+
items,
|
|
41
|
+
"track",
|
|
42
|
+
currentFrame,
|
|
43
|
+
durationFrames
|
|
44
|
+
);
|
|
45
|
+
let trackId;
|
|
46
|
+
if (available) {
|
|
47
|
+
trackId = available.id;
|
|
48
|
+
} else {
|
|
49
|
+
trackId = addTrack({
|
|
50
|
+
name: "Text",
|
|
51
|
+
kind: "track",
|
|
52
|
+
order: tracks.length,
|
|
53
|
+
locked: false,
|
|
54
|
+
muted: false,
|
|
55
|
+
hidden: false
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
const transform = createDefaultTransform(settings);
|
|
59
|
+
transform.width = Math.round(settings.width * 0.8);
|
|
60
|
+
transform.height = Math.round(settings.height * 0.3);
|
|
61
|
+
transform.x = Math.round(settings.width * 0.1);
|
|
62
|
+
transform.y = Math.round(settings.height * 0.4);
|
|
63
|
+
const itemId = addItem({
|
|
64
|
+
type: "text",
|
|
65
|
+
trackId,
|
|
66
|
+
startFrame: currentFrame,
|
|
67
|
+
durationFrames,
|
|
68
|
+
zIndex: 10,
|
|
69
|
+
locked: false,
|
|
70
|
+
disabled: false,
|
|
71
|
+
source: {},
|
|
72
|
+
transform,
|
|
73
|
+
style: preset.style,
|
|
74
|
+
filters: [],
|
|
75
|
+
metadata: { name: preset.label, presetId: preset.id }
|
|
76
|
+
});
|
|
77
|
+
if (itemId) selectItem(itemId, false);
|
|
78
|
+
},
|
|
79
|
+
[
|
|
80
|
+
settings,
|
|
81
|
+
currentFrame,
|
|
82
|
+
tracks,
|
|
83
|
+
items,
|
|
84
|
+
itemIdsByTrack,
|
|
85
|
+
addItem,
|
|
86
|
+
addTrack,
|
|
87
|
+
selectItem
|
|
88
|
+
]
|
|
89
|
+
);
|
|
90
|
+
const handleAddBlank = useCallback(() => {
|
|
91
|
+
handleAddText({
|
|
92
|
+
id: "blank",
|
|
93
|
+
label: "Text",
|
|
94
|
+
category: "basic",
|
|
95
|
+
style: {
|
|
96
|
+
text: "Your text here",
|
|
97
|
+
fontFamily: "Inter",
|
|
98
|
+
fontSize: 48,
|
|
99
|
+
fontWeight: 400,
|
|
100
|
+
color: "#ffffff",
|
|
101
|
+
textAlign: "center"
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}, [handleAddText]);
|
|
105
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
106
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
107
|
+
/* @__PURE__ */ jsx(icons.search, { className: "absolute left-3 top-1/2 -translate-y-1/2 size-4 text-white/30" }),
|
|
108
|
+
/* @__PURE__ */ jsx(
|
|
109
|
+
"input",
|
|
110
|
+
{
|
|
111
|
+
type: "text",
|
|
112
|
+
value: search,
|
|
113
|
+
onChange: (e) => setSearch(e.target.value),
|
|
114
|
+
placeholder: "Search text styles...",
|
|
115
|
+
className: "w-full h-10 pl-9 pr-3 bg-white/5 border border-white/10 rounded-xl text-sm text-white placeholder:text-white/30 focus:outline-none focus:ring-1 focus:ring-white/20"
|
|
116
|
+
}
|
|
117
|
+
)
|
|
118
|
+
] }),
|
|
119
|
+
/* @__PURE__ */ jsxs(
|
|
120
|
+
"button",
|
|
121
|
+
{
|
|
122
|
+
onClick: handleAddBlank,
|
|
123
|
+
className: "w-full h-11 rounded-xl bg-white/10 text-sm font-medium text-white flex items-center justify-center gap-2 active:bg-white/15 transition-colors",
|
|
124
|
+
children: [
|
|
125
|
+
/* @__PURE__ */ jsx(icons.add, { className: "size-4" }),
|
|
126
|
+
"Add blank text"
|
|
127
|
+
]
|
|
128
|
+
}
|
|
129
|
+
),
|
|
130
|
+
filteredCategories.map((category) => /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
131
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] text-white/40 uppercase tracking-wider font-medium", children: category.name }),
|
|
132
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-1.5", children: category.presets.map((preset) => /* @__PURE__ */ jsx(
|
|
133
|
+
PresetCard,
|
|
134
|
+
{
|
|
135
|
+
preset,
|
|
136
|
+
onAdd: handleAddText
|
|
137
|
+
},
|
|
138
|
+
preset.id
|
|
139
|
+
)) })
|
|
140
|
+
] }, category.id)),
|
|
141
|
+
filteredCategories.length === 0 && /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center py-8", children: [
|
|
142
|
+
/* @__PURE__ */ jsx(icons.itemText, { className: "size-6 text-white/15 mb-2" }),
|
|
143
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-white/30", children: "No presets found" })
|
|
144
|
+
] })
|
|
145
|
+
] });
|
|
146
|
+
}
|
|
147
|
+
function PresetCard({
|
|
148
|
+
preset,
|
|
149
|
+
onAdd
|
|
150
|
+
}) {
|
|
151
|
+
const style = preset.style;
|
|
152
|
+
useEffect(() => {
|
|
153
|
+
if (style.fontFamily) ensureFontLoaded(style.fontFamily);
|
|
154
|
+
}, [style.fontFamily]);
|
|
155
|
+
return /* @__PURE__ */ jsxs(
|
|
156
|
+
"button",
|
|
157
|
+
{
|
|
158
|
+
onClick: () => onAdd(preset),
|
|
159
|
+
className: "rounded-xl bg-white/5 border border-white/5 overflow-hidden active:bg-white/10 transition-colors",
|
|
160
|
+
children: [
|
|
161
|
+
/* @__PURE__ */ jsx(
|
|
162
|
+
"div",
|
|
163
|
+
{
|
|
164
|
+
className: "h-14 flex items-center justify-center px-2 overflow-hidden",
|
|
165
|
+
style: {
|
|
166
|
+
backgroundColor: style.backgroundColor ?? "transparent"
|
|
167
|
+
},
|
|
168
|
+
children: /* @__PURE__ */ jsx(
|
|
169
|
+
"span",
|
|
170
|
+
{
|
|
171
|
+
className: "truncate",
|
|
172
|
+
style: {
|
|
173
|
+
fontFamily: style.fontFamily ?? "Inter",
|
|
174
|
+
fontSize: Math.min((style.fontSize ?? 48) / 2.5, 20),
|
|
175
|
+
fontWeight: style.fontWeight ?? 400,
|
|
176
|
+
fontStyle: style.fontStyle,
|
|
177
|
+
color: style.color ?? style.fontColor ?? "#ffffff",
|
|
178
|
+
letterSpacing: style.letterSpacing ? style.letterSpacing / 2 : void 0,
|
|
179
|
+
textTransform: style.textTransform,
|
|
180
|
+
textShadow: style.shadow ? `${style.shadow.offsetX ?? 0}px ${style.shadow.offsetY ?? 2}px ${style.shadow.blur ?? 4}px ${style.shadow.color ?? "rgba(0,0,0,0.5)"}` : void 0
|
|
181
|
+
},
|
|
182
|
+
children: style.text ?? preset.label
|
|
183
|
+
}
|
|
184
|
+
)
|
|
185
|
+
}
|
|
186
|
+
),
|
|
187
|
+
/* @__PURE__ */ jsxs("div", { className: "px-2 py-1.5 border-t border-white/5", children: [
|
|
188
|
+
/* @__PURE__ */ jsx("p", { className: "text-[10px] text-white/50 truncate", children: preset.label }),
|
|
189
|
+
/* @__PURE__ */ jsx("p", { className: "text-[9px] text-white/25 truncate", children: style.fontFamily ?? "Inter" })
|
|
190
|
+
] })
|
|
191
|
+
]
|
|
192
|
+
}
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export { TextPanelMobile as default };
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bupple/vss-plugin-typography",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public",
|
|
8
|
+
"registry": "https://registry.npmjs.org"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"require": "./dist/index.cjs"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
21
|
+
"peerDependencies": {
|
|
22
|
+
"react": ">=18",
|
|
23
|
+
"react-dom": ">=18",
|
|
24
|
+
"@bupple/vss": "1.0.0"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/node": "^20.19.39",
|
|
28
|
+
"@types/react": "^19.0.0",
|
|
29
|
+
"@types/react-dom": "^19.0.0",
|
|
30
|
+
"react": "^19.0.0",
|
|
31
|
+
"react-dom": "^19.0.0",
|
|
32
|
+
"tsup": "^8.3.0",
|
|
33
|
+
"typescript": "^5.9.0",
|
|
34
|
+
"@bupple/vss": "1.0.0",
|
|
35
|
+
"@bupple/typescript-config": "0.0.0"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsup",
|
|
39
|
+
"lint": "eslint .",
|
|
40
|
+
"check-types": "tsc --noEmit"
|
|
41
|
+
}
|
|
42
|
+
}
|