@jant/core 0.3.34 → 0.3.36
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/client/assets/module-RjUF93sV.js +716 -0
- package/dist/client/assets/native-48B9X9Wg.js +1 -0
- package/dist/client/assets/url-8Dj-5CLW.js +1 -0
- package/dist/client/client.css +1 -1
- package/dist/client/client.js +3109 -2294
- package/dist/index.js +3327 -3031
- package/package.json +13 -4
- package/src/__tests__/helpers/app.ts +1 -1
- package/src/__tests__/helpers/db.ts +6 -0
- package/src/app.tsx +1 -5
- package/src/{lib → client}/avatar-upload.ts +1 -1
- package/src/{lib → client}/collection-form-bridge.ts +2 -2
- package/src/{ui → client}/components/__tests__/jant-collection-form.test.ts +26 -9
- package/src/{ui → client}/components/__tests__/jant-compose-dialog.test.ts +46 -14
- package/src/{ui → client}/components/__tests__/jant-compose-editor.test.ts +64 -24
- package/src/{ui → client}/components/__tests__/jant-post-form.test.ts +24 -14
- package/src/{ui → client}/components/__tests__/jant-settings-general.test.ts +3 -3
- package/src/client/components/collection-sidebar-types.ts +45 -0
- package/src/{ui → client}/components/collection-types.ts +3 -4
- package/src/{ui → client}/components/compose-types.ts +3 -1
- package/src/{ui → client}/components/jant-collection-form.ts +301 -182
- package/src/client/components/jant-collection-sidebar.ts +801 -0
- package/src/{ui → client}/components/jant-compose-dialog.ts +231 -1
- package/src/client/components/jant-compose-editor.ts +1249 -0
- package/src/client/components/jant-compose-fullscreen.ts +338 -0
- package/src/client/components/jant-media-lightbox.ts +257 -0
- package/src/{ui → client}/components/jant-nav-manager.ts +143 -84
- package/src/{ui → client}/components/jant-post-form.ts +57 -8
- package/src/{ui → client}/components/jant-settings-general.ts +2 -2
- package/src/{ui → client}/components/nav-manager-types.ts +3 -0
- package/src/{ui → client}/components/post-form-template.ts +35 -31
- package/src/{ui → client}/components/post-form-types.ts +7 -3
- package/src/{lib → client}/compose-bridge.ts +9 -7
- package/src/client/lazy-slugify.ts +51 -0
- package/src/{lib → client}/media-upload.ts +16 -3
- package/src/{lib → client}/nav-manager-bridge.ts +1 -1
- package/src/client/page-slug-bridge.ts +42 -0
- package/src/{lib → client}/post-form-bridge.ts +2 -2
- package/src/{lib → client}/settings-bridge.ts +3 -3
- package/src/client/tiptap/bubble-menu.ts +205 -0
- package/src/client/tiptap/create-editor.ts +40 -0
- package/src/client/tiptap/exitable-marks.ts +73 -0
- package/src/client/tiptap/extensions.ts +60 -0
- package/src/client/tiptap/image-node.ts +488 -0
- package/src/client/tiptap/link-toolbar.ts +371 -0
- package/src/client/tiptap/more-break.ts +50 -0
- package/src/client/tiptap/paste-image.ts +140 -0
- package/src/client/tiptap/slash-commands.ts +328 -0
- package/src/{types → client/types}/sortablejs.d.ts +1 -1
- package/src/client.ts +24 -17
- package/src/db/migrations/0012_add_tiptap_columns.sql +2 -0
- package/src/db/migrations/0013_replace_featured_with_visibility.sql +8 -0
- package/src/db/schema.ts +6 -1
- package/src/i18n/locales/en.po +641 -215
- package/src/i18n/locales/en.ts +1 -1
- package/src/i18n/locales/zh-Hans.po +642 -204
- package/src/i18n/locales/zh-Hans.ts +1 -1
- package/src/i18n/locales/zh-Hant.po +642 -204
- package/src/i18n/locales/zh-Hant.ts +1 -1
- package/src/lib/__tests__/resolve-config.test.ts +2 -2
- package/src/lib/__tests__/schemas.test.ts +9 -6
- package/src/lib/__tests__/url.test.ts +2 -2
- package/src/lib/__tests__/view.test.ts +9 -9
- package/src/lib/emoji-catalog.ts +146 -0
- package/src/lib/feed.ts +1 -1
- package/src/lib/media-helpers.ts +10 -9
- package/src/lib/render.tsx +4 -3
- package/src/lib/resolve-config.ts +8 -1
- package/src/lib/schemas.ts +2 -3
- package/src/lib/summary.ts +92 -0
- package/src/lib/timeline.ts +2 -0
- package/src/lib/tiptap-render.ts +196 -0
- package/src/lib/upload.ts +97 -9
- package/src/lib/url.ts +7 -23
- package/src/lib/view.ts +33 -19
- package/src/middleware/error-handler.ts +3 -3
- package/src/preset.css +38 -0
- package/src/routes/api/collections.ts +20 -3
- package/src/routes/api/posts.ts +48 -33
- package/src/routes/api/upload.ts +7 -5
- package/src/routes/auth/reset.tsx +5 -4
- package/src/routes/auth/setup.tsx +26 -11
- package/src/routes/auth/signin.tsx +10 -7
- package/src/routes/compose.tsx +20 -11
- package/src/routes/dash/__tests__/settings-avatar.test.ts +43 -8
- package/src/routes/dash/index.tsx +7 -1
- package/src/routes/dash/media.tsx +3 -0
- package/src/routes/dash/pages.tsx +8 -2
- package/src/routes/dash/posts.tsx +6 -2
- package/src/routes/dash/redirects.tsx +15 -9
- package/src/routes/dash/settings.tsx +336 -32
- package/src/routes/feed/__tests__/rss.test.ts +245 -6
- package/src/routes/feed/rss.ts +70 -6
- package/src/routes/pages/__tests__/featured.test.ts +6 -7
- package/src/routes/pages/archive.tsx +11 -7
- package/src/routes/pages/collection.tsx +32 -15
- package/src/routes/pages/collections.tsx +11 -2
- package/src/routes/pages/featured.tsx +1 -1
- package/src/routes/pages/home.tsx +1 -1
- package/src/services/__tests__/post.test.ts +124 -33
- package/src/services/__tests__/settings.test.ts +3 -3
- package/src/services/page.ts +16 -3
- package/src/services/post.ts +96 -37
- package/src/services/search.ts +4 -2
- package/src/services/settings.ts +6 -2
- package/src/styles/components.css +240 -60
- package/src/styles/tokens.css +10 -0
- package/src/styles/ui.css +1157 -81
- package/src/types/bindings.ts +5 -0
- package/src/types/config.ts +23 -1
- package/src/types/constants.ts +3 -0
- package/src/types/entities.ts +9 -2
- package/src/types/operations.ts +9 -3
- package/src/types/props.ts +3 -3
- package/src/types/views.ts +3 -2
- package/src/ui/compose/ComposeDialog.tsx +24 -7
- package/src/ui/dash/PageForm.tsx +2 -0
- package/src/ui/dash/PostList.tsx +5 -5
- package/src/ui/dash/StatusBadge.tsx +13 -5
- package/src/ui/dash/appearance/AdvancedContent.tsx +52 -61
- package/src/ui/dash/appearance/ColorThemeContent.tsx +30 -35
- package/src/ui/dash/appearance/FontThemeContent.tsx +65 -73
- package/src/ui/dash/appearance/NavigationContent.tsx +107 -96
- package/src/ui/dash/media/MediaListContent.tsx +9 -4
- package/src/ui/dash/media/ViewMediaContent.tsx +2 -2
- package/src/ui/dash/pages/PagesContent.tsx +2 -1
- package/src/ui/dash/posts/PostForm.tsx +19 -7
- package/src/ui/dash/settings/AccountContent.tsx +133 -138
- package/src/ui/dash/settings/AvatarContent.tsx +70 -0
- package/src/ui/dash/settings/GeneralContent.tsx +3 -62
- package/src/ui/dash/settings/SettingsRootContent.tsx +236 -0
- package/src/ui/layouts/DashLayout.tsx +157 -75
- package/src/ui/layouts/SiteLayout.tsx +13 -13
- package/src/ui/pages/ArchivePage.tsx +10 -7
- package/src/ui/pages/CollectionPage.tsx +6 -35
- package/src/ui/pages/CollectionsPage.tsx +2 -1
- package/src/ui/pages/FeaturedPage.tsx +2 -1
- package/src/ui/pages/HomePage.tsx +1 -1
- package/src/ui/pages/SearchPage.tsx +1 -1
- package/src/ui/shared/CollectionsSidebar.tsx +228 -3
- package/src/ui/shared/MediaGallery.tsx +179 -41
- package/src/lib/collections-reorder.ts +0 -28
- package/src/routes/dash/appearance.tsx +0 -240
- package/src/routes/dash/collections.tsx +0 -211
- package/src/ui/components/jant-compose-editor.ts +0 -814
- package/src/ui/dash/appearance/AppearanceNav.tsx +0 -60
- package/src/ui/dash/collections/CollectionForm.tsx +0 -166
- package/src/ui/dash/collections/CollectionsListContent.tsx +0 -146
- package/src/ui/dash/collections/IconPickerGrid.tsx +0 -50
- package/src/ui/dash/collections/ViewCollectionContent.tsx +0 -103
- package/src/ui/dash/settings/SettingsNav.tsx +0 -52
- /package/src/{ui → client}/components/__tests__/jant-settings-avatar.test.ts +0 -0
- /package/src/{ui → client}/components/jant-settings-avatar.ts +0 -0
- /package/src/{ui → client}/components/settings-types.ts +0 -0
- /package/src/{lib → client}/image-processor.ts +0 -0
- /package/src/{lib → client}/toast.ts +0 -0
|
@@ -38,7 +38,8 @@ export interface ComposeLabels {
|
|
|
38
38
|
attachedTextHint: string;
|
|
39
39
|
done: string;
|
|
40
40
|
media: string;
|
|
41
|
-
|
|
41
|
+
rate: string;
|
|
42
|
+
emoji: string;
|
|
42
43
|
title: string;
|
|
43
44
|
collection: string;
|
|
44
45
|
searchCollections: string;
|
|
@@ -51,6 +52,7 @@ export interface ComposeLabels {
|
|
|
51
52
|
addMore: string;
|
|
52
53
|
uploading: string;
|
|
53
54
|
published: string;
|
|
55
|
+
retryAll: string;
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
export interface ComposeSubmitDetail {
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Handles create/edit collection form interactions:
|
|
5
5
|
* - Maintains form state for title, slug, description, sort order, and icon
|
|
6
|
-
* -
|
|
6
|
+
* - Notion-style inline icon trigger with anchored popover (Icons + Emojis tabs)
|
|
7
|
+
* - Color presets that instantly recolor all icon previews
|
|
8
|
+
* - Default "library" icon in create mode
|
|
7
9
|
* - Dispatches `jant:collection-submit` for the bridge to POST to the server
|
|
8
10
|
*
|
|
9
11
|
* Light DOM only — BaseCoat and Tailwind classes apply directly.
|
|
@@ -11,10 +13,10 @@
|
|
|
11
13
|
|
|
12
14
|
import { LitElement, html, nothing } from "lit";
|
|
13
15
|
import type { PropertyValueMap } from "lit";
|
|
14
|
-
import { classMap } from "lit/directives/class-map.js";
|
|
15
16
|
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
|
16
17
|
import {
|
|
17
18
|
DEFAULT_ICON_COLOR,
|
|
19
|
+
DEFAULT_ICON_NAME,
|
|
18
20
|
ICON_COLOR_PRESETS,
|
|
19
21
|
createIconValue,
|
|
20
22
|
parseCollectionIcon,
|
|
@@ -22,6 +24,8 @@ import {
|
|
|
22
24
|
getIconSvg,
|
|
23
25
|
} from "../../lib/icons.js";
|
|
24
26
|
import { ICON_CATALOG } from "../../lib/icon-catalog.js";
|
|
27
|
+
import { EMOJI_CATALOG } from "../../lib/emoji-catalog.js";
|
|
28
|
+
import { slugify } from "../lazy-slugify.js";
|
|
25
29
|
import type {
|
|
26
30
|
CollectionFormInitial,
|
|
27
31
|
CollectionFormLabels,
|
|
@@ -33,14 +37,10 @@ type CatalogCategory = {
|
|
|
33
37
|
icons: Array<{ name: string; svg: string }>;
|
|
34
38
|
};
|
|
35
39
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
.replace(/[^\w\s-]/g, "")
|
|
41
|
-
.replace(/[\s_-]+/g, "-")
|
|
42
|
-
.replace(/^-+|-+$/g, "");
|
|
43
|
-
}
|
|
40
|
+
type EmojiCategory = {
|
|
41
|
+
name: string;
|
|
42
|
+
emojis: string[];
|
|
43
|
+
};
|
|
44
44
|
|
|
45
45
|
export class JantCollectionForm extends LitElement {
|
|
46
46
|
static properties = {
|
|
@@ -57,7 +57,10 @@ export class JantCollectionForm extends LitElement {
|
|
|
57
57
|
_iconName: { state: true },
|
|
58
58
|
_iconSvg: { state: true },
|
|
59
59
|
_iconColor: { state: true },
|
|
60
|
+
_iconEmoji: { state: true },
|
|
60
61
|
_iconSearch: { state: true },
|
|
62
|
+
_pickerOpen: { state: true },
|
|
63
|
+
_pickerTab: { state: true },
|
|
61
64
|
_loading: { state: true },
|
|
62
65
|
};
|
|
63
66
|
|
|
@@ -74,10 +77,27 @@ export class JantCollectionForm extends LitElement {
|
|
|
74
77
|
declare _iconName: string;
|
|
75
78
|
declare _iconSvg: string;
|
|
76
79
|
declare _iconColor: string;
|
|
80
|
+
declare _iconEmoji: string;
|
|
77
81
|
declare _iconSearch: string;
|
|
82
|
+
declare _pickerOpen: boolean;
|
|
83
|
+
declare _pickerTab: "icons" | "emojis";
|
|
78
84
|
declare _loading: boolean;
|
|
79
85
|
|
|
80
86
|
#initialized = false;
|
|
87
|
+
#closePickerHandler = (e: Event) => {
|
|
88
|
+
const target = e.target as HTMLElement | null;
|
|
89
|
+
if (!target) return;
|
|
90
|
+
const pickerEl = this.querySelector<HTMLElement>("[data-icon-picker]");
|
|
91
|
+
const triggerEl = this.querySelector<HTMLElement>("[data-icon-trigger]");
|
|
92
|
+
if (
|
|
93
|
+
pickerEl &&
|
|
94
|
+
!pickerEl.contains(target) &&
|
|
95
|
+
triggerEl &&
|
|
96
|
+
!triggerEl.contains(target)
|
|
97
|
+
) {
|
|
98
|
+
this._pickerOpen = false;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
81
101
|
|
|
82
102
|
createRenderRoot() {
|
|
83
103
|
this.innerHTML = "";
|
|
@@ -105,7 +125,10 @@ export class JantCollectionForm extends LitElement {
|
|
|
105
125
|
this._iconName = "";
|
|
106
126
|
this._iconSvg = "";
|
|
107
127
|
this._iconColor = DEFAULT_ICON_COLOR;
|
|
128
|
+
this._iconEmoji = "";
|
|
108
129
|
this._iconSearch = "";
|
|
130
|
+
this._pickerOpen = false;
|
|
131
|
+
this._pickerTab = "icons";
|
|
109
132
|
this._loading = false;
|
|
110
133
|
}
|
|
111
134
|
|
|
@@ -126,6 +149,16 @@ export class JantCollectionForm extends LitElement {
|
|
|
126
149
|
return this._loading;
|
|
127
150
|
}
|
|
128
151
|
|
|
152
|
+
connectedCallback() {
|
|
153
|
+
super.connectedCallback();
|
|
154
|
+
document.addEventListener("click", this.#closePickerHandler, true);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
disconnectedCallback() {
|
|
158
|
+
super.disconnectedCallback();
|
|
159
|
+
document.removeEventListener("click", this.#closePickerHandler, true);
|
|
160
|
+
}
|
|
161
|
+
|
|
129
162
|
#applyInitialData() {
|
|
130
163
|
if (!this.initial) return;
|
|
131
164
|
this.#initialized = true;
|
|
@@ -134,29 +167,44 @@ export class JantCollectionForm extends LitElement {
|
|
|
134
167
|
this._description = this.initial.description ?? "";
|
|
135
168
|
this._sortOrder = this.initial.sortOrder ?? "newest";
|
|
136
169
|
|
|
137
|
-
const
|
|
170
|
+
const rawIcon = this.initial.icon ?? "";
|
|
171
|
+
const parsed = parseCollectionIcon(rawIcon);
|
|
138
172
|
if (parsed) {
|
|
139
173
|
this._iconName = parsed.name;
|
|
140
174
|
this._iconSvg = parsed.svg;
|
|
141
175
|
this._iconColor = parsed.color || DEFAULT_ICON_COLOR;
|
|
176
|
+
this._iconEmoji = "";
|
|
177
|
+
} else if (rawIcon && !rawIcon.startsWith("{")) {
|
|
178
|
+
// Legacy emoji value
|
|
179
|
+
this._iconEmoji = rawIcon;
|
|
180
|
+
this._iconName = "";
|
|
181
|
+
this._iconSvg = "";
|
|
182
|
+
this._iconColor = DEFAULT_ICON_COLOR;
|
|
142
183
|
} else {
|
|
143
184
|
this._iconName = "";
|
|
144
185
|
this._iconSvg = "";
|
|
145
186
|
this._iconColor = DEFAULT_ICON_COLOR;
|
|
187
|
+
this._iconEmoji = "";
|
|
188
|
+
// Default icon in create mode
|
|
189
|
+
if (!this.isEdit) {
|
|
190
|
+
this.#applyDefaultIcon();
|
|
191
|
+
}
|
|
146
192
|
}
|
|
147
193
|
}
|
|
148
194
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
return this.querySelector<HTMLDialogElement>("#collection-icon-dialog");
|
|
195
|
+
#applyDefaultIcon() {
|
|
196
|
+
const svg = getIconSvg(DEFAULT_ICON_NAME);
|
|
197
|
+
if (svg) {
|
|
198
|
+
this._iconName = DEFAULT_ICON_NAME;
|
|
199
|
+
this._iconSvg = svg;
|
|
200
|
+
this._iconColor = DEFAULT_ICON_COLOR;
|
|
201
|
+
}
|
|
157
202
|
}
|
|
158
203
|
|
|
159
204
|
get #iconValue(): string {
|
|
205
|
+
if (this._iconEmoji) {
|
|
206
|
+
return this._iconEmoji;
|
|
207
|
+
}
|
|
160
208
|
if (this._iconName && this._iconSvg) {
|
|
161
209
|
return createIconValue(
|
|
162
210
|
this._iconName,
|
|
@@ -185,12 +233,47 @@ export class JantCollectionForm extends LitElement {
|
|
|
185
233
|
return result;
|
|
186
234
|
}
|
|
187
235
|
|
|
188
|
-
#
|
|
189
|
-
this
|
|
236
|
+
#filteredEmojiCatalog(): EmojiCategory[] {
|
|
237
|
+
const q = this._iconSearch.trim().toLowerCase();
|
|
238
|
+
const result: EmojiCategory[] = [];
|
|
239
|
+
for (const [category, emojis] of Object.entries(EMOJI_CATALOG)) {
|
|
240
|
+
if (q && !category.includes(q)) continue;
|
|
241
|
+
result.push({ name: category, emojis });
|
|
242
|
+
}
|
|
243
|
+
return result;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
#togglePicker(e: Event) {
|
|
247
|
+
e.stopPropagation();
|
|
248
|
+
this._pickerOpen = !this._pickerOpen;
|
|
249
|
+
this._iconSearch = "";
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
#selectIcon(name: string, svg: string) {
|
|
253
|
+
this._iconName = name;
|
|
254
|
+
this._iconSvg = svg;
|
|
255
|
+
this._iconEmoji = "";
|
|
256
|
+
if (!this._iconColor) {
|
|
257
|
+
this._iconColor = DEFAULT_ICON_COLOR;
|
|
258
|
+
}
|
|
259
|
+
this._iconSearch = "";
|
|
260
|
+
this._pickerOpen = false;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
#selectEmoji(emoji: string) {
|
|
264
|
+
this._iconEmoji = emoji;
|
|
265
|
+
this._iconName = "";
|
|
266
|
+
this._iconSvg = "";
|
|
267
|
+
this._iconSearch = "";
|
|
268
|
+
this._pickerOpen = false;
|
|
190
269
|
}
|
|
191
270
|
|
|
192
|
-
#
|
|
193
|
-
this
|
|
271
|
+
#removeIcon() {
|
|
272
|
+
this._iconName = "";
|
|
273
|
+
this._iconSvg = "";
|
|
274
|
+
this._iconColor = DEFAULT_ICON_COLOR;
|
|
275
|
+
this._iconEmoji = "";
|
|
276
|
+
this._pickerOpen = false;
|
|
194
277
|
}
|
|
195
278
|
|
|
196
279
|
#handleSubmit(e: Event) {
|
|
@@ -221,48 +304,47 @@ export class JantCollectionForm extends LitElement {
|
|
|
221
304
|
);
|
|
222
305
|
}
|
|
223
306
|
|
|
224
|
-
#
|
|
307
|
+
#renderTriggerIcon() {
|
|
308
|
+
if (this._iconEmoji) {
|
|
309
|
+
return html`<span class="text-lg leading-none">${this._iconEmoji}</span>`;
|
|
310
|
+
}
|
|
225
311
|
if (this._iconSvg) {
|
|
226
312
|
const htmlString = renderCollectionIcon(this.#iconValue, {
|
|
227
|
-
size:
|
|
313
|
+
size: 20,
|
|
228
314
|
fallback: false,
|
|
229
315
|
});
|
|
230
316
|
return html`<span
|
|
231
|
-
class="w-
|
|
317
|
+
class="w-5 h-5 flex items-center justify-center"
|
|
232
318
|
style=${`color:${this._iconColor}`}
|
|
233
319
|
>
|
|
234
320
|
${unsafeHTML(htmlString)}
|
|
235
321
|
</span>`;
|
|
236
322
|
}
|
|
237
|
-
|
|
238
|
-
const htmlString = renderCollectionIcon(this.initial.icon, {
|
|
239
|
-
size: 24,
|
|
240
|
-
fallback: false,
|
|
241
|
-
});
|
|
242
|
-
if (htmlString) {
|
|
243
|
-
return html`<span class="w-6 h-6 flex items-center justify-center">
|
|
244
|
-
${unsafeHTML(htmlString)}
|
|
245
|
-
</span>`;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
return html`<span class="text-muted-foreground text-lg">?</span>`;
|
|
323
|
+
return html`<span class="text-muted-foreground text-base">+</span>`;
|
|
249
324
|
}
|
|
250
325
|
|
|
251
|
-
#
|
|
252
|
-
if (!this._iconSvg) return nothing;
|
|
326
|
+
#renderInlineIconTrigger() {
|
|
253
327
|
return html`
|
|
254
|
-
<
|
|
328
|
+
<button
|
|
329
|
+
type="button"
|
|
330
|
+
data-icon-trigger
|
|
331
|
+
class="absolute left-2 top-1/2 -translate-y-1/2 flex items-center justify-center w-8 h-8 rounded-md hover:bg-accent transition-colors z-10"
|
|
332
|
+
@click=${(e: Event) => this.#togglePicker(e)}
|
|
333
|
+
>
|
|
334
|
+
${this.#renderTriggerIcon()}
|
|
335
|
+
</button>
|
|
336
|
+
`;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
#renderPickerColorPresets() {
|
|
340
|
+
return html`
|
|
341
|
+
<div class="flex items-center gap-1.5 px-3 pb-2">
|
|
255
342
|
${ICON_COLOR_PRESETS.map((preset) => {
|
|
256
343
|
const isActive = this._iconColor === preset.value;
|
|
257
344
|
return html`
|
|
258
345
|
<button
|
|
259
346
|
type="button"
|
|
260
|
-
class=${
|
|
261
|
-
"w-6 h-6 rounded-full border-2 transition-transform hover:scale-110": true,
|
|
262
|
-
"ring-2": isActive,
|
|
263
|
-
"ring-offset-1": isActive,
|
|
264
|
-
"ring-primary": isActive,
|
|
265
|
-
})}
|
|
347
|
+
class=${`w-5 h-5 rounded-full border-2 transition-transform hover:scale-110${isActive ? " ring-2 ring-offset-1 ring-primary" : ""}`}
|
|
266
348
|
style=${`background-color:${preset.value}; border-color: transparent`}
|
|
267
349
|
title=${preset.name}
|
|
268
350
|
@click=${() => {
|
|
@@ -275,97 +357,157 @@ export class JantCollectionForm extends LitElement {
|
|
|
275
357
|
`;
|
|
276
358
|
}
|
|
277
359
|
|
|
278
|
-
#
|
|
360
|
+
#renderIconsGrid() {
|
|
279
361
|
const categories = this.#filteredCatalog();
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
362
|
+
if (categories.length === 0) {
|
|
363
|
+
return html`<p class="text-sm text-muted-foreground px-3 py-2">
|
|
364
|
+
No icons found
|
|
365
|
+
</p>`;
|
|
366
|
+
}
|
|
367
|
+
return categories.map(
|
|
368
|
+
(category) => html`
|
|
369
|
+
<div class="flex flex-col gap-1.5 mb-3" data-category=${category.name}>
|
|
370
|
+
<h3
|
|
371
|
+
class="text-xs font-medium text-muted-foreground uppercase tracking-wider px-3"
|
|
372
|
+
>
|
|
373
|
+
${category.name}
|
|
374
|
+
</h3>
|
|
375
|
+
<div class="grid grid-cols-8 gap-0.5 px-2">
|
|
376
|
+
${category.icons.map(
|
|
377
|
+
(icon) => html`
|
|
378
|
+
<button
|
|
379
|
+
type="button"
|
|
380
|
+
class=${`flex items-center justify-center w-8 h-8 rounded-md hover:bg-accent transition-colors${this._iconName === icon.name && this._iconSvg === icon.svg && !this._iconEmoji ? " ring-2 ring-primary" : ""}`}
|
|
381
|
+
data-icon-name=${icon.name}
|
|
382
|
+
style=${`color:${this._iconColor}`}
|
|
383
|
+
@click=${() => this.#selectIcon(icon.name, icon.svg)}
|
|
384
|
+
>
|
|
385
|
+
<span class="w-4 h-4 flex items-center justify-center">
|
|
386
|
+
${unsafeHTML(
|
|
387
|
+
icon.svg
|
|
388
|
+
.replace(/width="24"/, 'width="16"')
|
|
389
|
+
.replace(/height="24"/, 'height="16"'),
|
|
390
|
+
)}
|
|
391
|
+
</span>
|
|
392
|
+
</button>
|
|
393
|
+
`,
|
|
394
|
+
)}
|
|
308
395
|
</div>
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
this._iconName = icon.name;
|
|
342
|
-
this._iconSvg = icon.svg;
|
|
343
|
-
if (!this._iconColor) {
|
|
344
|
-
this._iconColor = DEFAULT_ICON_COLOR;
|
|
345
|
-
}
|
|
346
|
-
this._iconSearch = "";
|
|
347
|
-
this.#closeDialog();
|
|
348
|
-
}}
|
|
349
|
-
>
|
|
350
|
-
<span
|
|
351
|
-
class="w-5 h-5 flex items-center justify-center"
|
|
352
|
-
>
|
|
353
|
-
${unsafeHTML(
|
|
354
|
-
icon.svg
|
|
355
|
-
.replace(/width="24"/, 'width="20"')
|
|
356
|
-
.replace(/height="24"/, 'height="20"'),
|
|
357
|
-
)}
|
|
358
|
-
</span>
|
|
359
|
-
</button>
|
|
360
|
-
`,
|
|
361
|
-
)}
|
|
362
|
-
</div>
|
|
363
|
-
</div>
|
|
364
|
-
`,
|
|
365
|
-
)}
|
|
396
|
+
</div>
|
|
397
|
+
`,
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
#renderEmojisGrid() {
|
|
402
|
+
const categories = this.#filteredEmojiCatalog();
|
|
403
|
+
if (categories.length === 0) {
|
|
404
|
+
return html`<p class="text-sm text-muted-foreground px-3 py-2">
|
|
405
|
+
No emojis found
|
|
406
|
+
</p>`;
|
|
407
|
+
}
|
|
408
|
+
return categories.map(
|
|
409
|
+
(category) => html`
|
|
410
|
+
<div class="flex flex-col gap-1.5 mb-3" data-category=${category.name}>
|
|
411
|
+
<h3
|
|
412
|
+
class="text-xs font-medium text-muted-foreground uppercase tracking-wider px-3"
|
|
413
|
+
>
|
|
414
|
+
${category.name}
|
|
415
|
+
</h3>
|
|
416
|
+
<div class="grid grid-cols-8 gap-0.5 px-2">
|
|
417
|
+
${category.emojis.map(
|
|
418
|
+
(emoji) => html`
|
|
419
|
+
<button
|
|
420
|
+
type="button"
|
|
421
|
+
class=${`flex items-center justify-center w-8 h-8 rounded-md hover:bg-accent transition-colors text-lg${this._iconEmoji === emoji ? " ring-2 ring-primary" : ""}`}
|
|
422
|
+
@click=${() => this.#selectEmoji(emoji)}
|
|
423
|
+
>
|
|
424
|
+
${emoji}
|
|
425
|
+
</button>
|
|
426
|
+
`,
|
|
427
|
+
)}
|
|
366
428
|
</div>
|
|
367
429
|
</div>
|
|
368
|
-
|
|
430
|
+
`,
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
#renderIconPopover() {
|
|
435
|
+
if (!this._pickerOpen) return nothing;
|
|
436
|
+
|
|
437
|
+
const isIconsTab = this._pickerTab === "icons";
|
|
438
|
+
const searchPlaceholder = isIconsTab
|
|
439
|
+
? this.labels.searchIconsPlaceholder
|
|
440
|
+
: this.labels.searchEmojisPlaceholder;
|
|
441
|
+
const hasIcon = this._iconSvg || this._iconEmoji;
|
|
442
|
+
|
|
443
|
+
return html`
|
|
444
|
+
<div
|
|
445
|
+
data-icon-picker
|
|
446
|
+
class="absolute left-0 top-full mt-1 z-50 w-80 rounded-lg border border-border bg-background shadow-lg"
|
|
447
|
+
>
|
|
448
|
+
<!-- Tabs -->
|
|
449
|
+
<div class="flex border-b border-border">
|
|
450
|
+
<button
|
|
451
|
+
type="button"
|
|
452
|
+
class=${`flex-1 px-3 py-2 text-sm font-medium transition-colors ${isIconsTab ? "border-b-2 border-primary text-foreground" : "text-muted-foreground hover:text-foreground"}`}
|
|
453
|
+
@click=${() => {
|
|
454
|
+
this._pickerTab = "icons";
|
|
455
|
+
this._iconSearch = "";
|
|
456
|
+
}}
|
|
457
|
+
>
|
|
458
|
+
${this.labels.iconsTab}
|
|
459
|
+
</button>
|
|
460
|
+
<button
|
|
461
|
+
type="button"
|
|
462
|
+
class=${`flex-1 px-3 py-2 text-sm font-medium transition-colors ${!isIconsTab ? "border-b-2 border-primary text-foreground" : "text-muted-foreground hover:text-foreground"}`}
|
|
463
|
+
@click=${() => {
|
|
464
|
+
this._pickerTab = "emojis";
|
|
465
|
+
this._iconSearch = "";
|
|
466
|
+
}}
|
|
467
|
+
>
|
|
468
|
+
${this.labels.emojisTab}
|
|
469
|
+
</button>
|
|
470
|
+
</div>
|
|
471
|
+
|
|
472
|
+
<!-- Color presets (icons tab only) -->
|
|
473
|
+
${isIconsTab
|
|
474
|
+
? html`<div class="pt-2">${this.#renderPickerColorPresets()}</div>`
|
|
475
|
+
: nothing}
|
|
476
|
+
|
|
477
|
+
<!-- Search -->
|
|
478
|
+
<div class="px-3 py-2">
|
|
479
|
+
<input
|
|
480
|
+
type="search"
|
|
481
|
+
class="input text-sm w-full"
|
|
482
|
+
placeholder=${searchPlaceholder}
|
|
483
|
+
.value=${this._iconSearch}
|
|
484
|
+
@input=${(event: Event) => {
|
|
485
|
+
const target = event.target as HTMLInputElement;
|
|
486
|
+
this._iconSearch = target.value;
|
|
487
|
+
}}
|
|
488
|
+
/>
|
|
489
|
+
</div>
|
|
490
|
+
|
|
491
|
+
<!-- Grid -->
|
|
492
|
+
<div class="overflow-y-auto max-h-56">
|
|
493
|
+
${isIconsTab ? this.#renderIconsGrid() : this.#renderEmojisGrid()}
|
|
494
|
+
</div>
|
|
495
|
+
|
|
496
|
+
<!-- Remove button -->
|
|
497
|
+
${hasIcon
|
|
498
|
+
? html`
|
|
499
|
+
<div class="border-t border-border px-3 py-2">
|
|
500
|
+
<button
|
|
501
|
+
type="button"
|
|
502
|
+
class="btn-ghost text-sm w-full"
|
|
503
|
+
@click=${() => this.#removeIcon()}
|
|
504
|
+
>
|
|
505
|
+
${this.labels.removeIcon}
|
|
506
|
+
</button>
|
|
507
|
+
</div>
|
|
508
|
+
`
|
|
509
|
+
: nothing}
|
|
510
|
+
</div>
|
|
369
511
|
`;
|
|
370
512
|
}
|
|
371
513
|
|
|
@@ -377,20 +519,31 @@ export class JantCollectionForm extends LitElement {
|
|
|
377
519
|
>
|
|
378
520
|
<div class="field">
|
|
379
521
|
<label class="label">${this.labels.titleLabel}</label>
|
|
380
|
-
<
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
522
|
+
<div class="relative">
|
|
523
|
+
${this.#renderInlineIconTrigger()}
|
|
524
|
+
<input
|
|
525
|
+
type="text"
|
|
526
|
+
class="input pl-12"
|
|
527
|
+
required
|
|
528
|
+
.value=${this._title}
|
|
529
|
+
placeholder=${this.isEdit
|
|
530
|
+
? nothing
|
|
531
|
+
: this.labels.titlePlaceholder}
|
|
532
|
+
@input=${(event: Event) => {
|
|
533
|
+
const target = event.target as HTMLInputElement;
|
|
534
|
+
this._title = target.value;
|
|
535
|
+
if (!this.isEdit) {
|
|
536
|
+
const currentTitle = target.value;
|
|
537
|
+
slugify(currentTitle).then((slug) => {
|
|
538
|
+
if (this._title === currentTitle) {
|
|
539
|
+
this._slug = slug;
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
}}
|
|
544
|
+
/>
|
|
545
|
+
${this.#renderIconPopover()}
|
|
546
|
+
</div>
|
|
394
547
|
</div>
|
|
395
548
|
|
|
396
549
|
<div class="field">
|
|
@@ -430,38 +583,6 @@ export class JantCollectionForm extends LitElement {
|
|
|
430
583
|
></textarea>
|
|
431
584
|
</div>
|
|
432
585
|
|
|
433
|
-
<div class="field">
|
|
434
|
-
<label class="label">${this.labels.iconLabel}</label>
|
|
435
|
-
<div class="flex items-center gap-3">
|
|
436
|
-
<div
|
|
437
|
-
class="flex items-center justify-center w-10 h-10 rounded-md border border-border"
|
|
438
|
-
>
|
|
439
|
-
${this.#renderIconPreview()}
|
|
440
|
-
</div>
|
|
441
|
-
<button
|
|
442
|
-
type="button"
|
|
443
|
-
class="btn-outline text-sm"
|
|
444
|
-
@click=${() => this.#openDialog()}
|
|
445
|
-
>
|
|
446
|
-
${this.labels.chooseIcon}
|
|
447
|
-
</button>
|
|
448
|
-
${this._iconSvg
|
|
449
|
-
? html`<button
|
|
450
|
-
type="button"
|
|
451
|
-
class="btn-ghost text-sm"
|
|
452
|
-
@click=${() => {
|
|
453
|
-
this._iconName = "";
|
|
454
|
-
this._iconSvg = "";
|
|
455
|
-
this._iconColor = DEFAULT_ICON_COLOR;
|
|
456
|
-
}}
|
|
457
|
-
>
|
|
458
|
-
${this.labels.removeIcon}
|
|
459
|
-
</button>`
|
|
460
|
-
: nothing}
|
|
461
|
-
</div>
|
|
462
|
-
${this.#renderColorPresets()}
|
|
463
|
-
</div>
|
|
464
|
-
|
|
465
586
|
<div class="field">
|
|
466
587
|
<label class="label">${this.labels.sortOrderLabel}</label>
|
|
467
588
|
<select
|
|
@@ -503,8 +624,6 @@ export class JantCollectionForm extends LitElement {
|
|
|
503
624
|
</a>
|
|
504
625
|
</div>
|
|
505
626
|
</form>
|
|
506
|
-
|
|
507
|
-
${this.#renderIconDialog()}
|
|
508
627
|
`;
|
|
509
628
|
}
|
|
510
629
|
}
|