@wikex/admin-kit 0.2.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/CHANGELOG.md +17 -0
- package/README.md +167 -0
- package/RELEASING.md +59 -0
- package/package.json +55 -0
- package/src/TextStyleRuntime.tsx +90 -0
- package/src/admin/BeforeLogin.tsx +19 -0
- package/src/admin/Brand.tsx +74 -0
- package/src/admin/Header.tsx +87 -0
- package/src/admin/LogoutButton.tsx +41 -0
- package/src/admin/Nav.tsx +35 -0
- package/src/admin/NavClient.tsx +288 -0
- package/src/admin/ThemeToggle.tsx +31 -0
- package/src/admin/ViewSite.tsx +11 -0
- package/src/admin/useAdminBrand.ts +81 -0
- package/src/adminExperience.ts +117 -0
- package/src/index.ts +11 -0
- package/src/live-preview/GlobalInlineInspector.tsx +334 -0
- package/src/live-preview/LivePreviewEditor.tsx +2088 -0
- package/src/live-preview/TextStyleInspector.tsx +289 -0
- package/src/live-preview/index.ts +23 -0
- package/src/live-preview/runtimeConfig.ts +52 -0
- package/src/plugin.ts +70 -0
- package/src/project.ts +82 -0
- package/src/rich-text/client.tsx +202 -0
- package/src/rich-text/index.ts +10 -0
- package/src/starter/client.tsx +22 -0
- package/src/starter.ts +95 -0
- package/src/styles/_wikex-fonts.scss +45 -0
- package/src/styles/admin.scss +3032 -0
- package/src/textStyles.ts +161 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
export const TEXT_FONT_OPTIONS = [
|
|
2
|
+
{ css: '', label: 'Mặc định', value: 'default' },
|
|
3
|
+
{ css: 'var(--font-manrope), sans-serif', label: 'Manrope', value: 'manrope' },
|
|
4
|
+
{ css: 'var(--font-inter), sans-serif', label: 'Inter', value: 'inter' },
|
|
5
|
+
{ css: 'var(--font-roboto), sans-serif', label: 'Roboto', value: 'roboto' },
|
|
6
|
+
{ css: 'var(--font-montserrat), sans-serif', label: 'Montserrat', value: 'montserrat' },
|
|
7
|
+
{ css: 'var(--font-lora), serif', label: 'Lora', value: 'lora' },
|
|
8
|
+
{
|
|
9
|
+
css: 'var(--font-playfair-display), serif',
|
|
10
|
+
label: 'Playfair Display',
|
|
11
|
+
value: 'playfair-display',
|
|
12
|
+
},
|
|
13
|
+
] as const
|
|
14
|
+
|
|
15
|
+
export const TEXT_LINE_HEIGHT_OPTIONS = [
|
|
16
|
+
{ css: '', label: 'Mặc định', value: 'default' },
|
|
17
|
+
{ css: '0.9', label: 'Rất chặt · 0.9', value: '0.9' },
|
|
18
|
+
{ css: '1', label: 'Chặt · 1.0', value: '1' },
|
|
19
|
+
{ css: '1.15', label: 'Gọn · 1.15', value: '1.15' },
|
|
20
|
+
{ css: '1.35', label: 'Tiêu chuẩn · 1.35', value: '1.35' },
|
|
21
|
+
{ css: '1.5', label: 'Thoáng · 1.5', value: '1.5' },
|
|
22
|
+
{ css: '1.7', label: 'Rộng · 1.7', value: '1.7' },
|
|
23
|
+
] as const
|
|
24
|
+
|
|
25
|
+
export type TextFontFamily = (typeof TEXT_FONT_OPTIONS)[number]['value']
|
|
26
|
+
export type TextLineHeight = (typeof TEXT_LINE_HEIGHT_OPTIONS)[number]['value']
|
|
27
|
+
|
|
28
|
+
export type TextStyleValue = {
|
|
29
|
+
fontFamily?: TextFontFamily
|
|
30
|
+
lineHeight?: TextLineHeight
|
|
31
|
+
/** Percentage of the containing block, written by the canvas resize handles. */
|
|
32
|
+
width?: number
|
|
33
|
+
/** Percentage offset from the containing block's left content edge. */
|
|
34
|
+
widthOffset?: number
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type TextStyleMap = Record<string, TextStyleValue>
|
|
38
|
+
|
|
39
|
+
export const EDITABLE_INTERACTIVE_SELECTOR = [
|
|
40
|
+
'a',
|
|
41
|
+
'button',
|
|
42
|
+
'input:not([type="hidden"])',
|
|
43
|
+
'select',
|
|
44
|
+
'textarea',
|
|
45
|
+
'[role="button"]',
|
|
46
|
+
'[role="link"]',
|
|
47
|
+
'[tabindex]:not([tabindex="-1"])',
|
|
48
|
+
].join(',')
|
|
49
|
+
|
|
50
|
+
export const EDITABLE_PREVIEW_SELECTOR = [
|
|
51
|
+
'h1',
|
|
52
|
+
'h2',
|
|
53
|
+
'h3',
|
|
54
|
+
'h4',
|
|
55
|
+
'h5',
|
|
56
|
+
'h6',
|
|
57
|
+
'p',
|
|
58
|
+
'li',
|
|
59
|
+
'span',
|
|
60
|
+
'strong',
|
|
61
|
+
EDITABLE_INTERACTIVE_SELECTOR,
|
|
62
|
+
].join(',')
|
|
63
|
+
|
|
64
|
+
export const isSyntheticElementStyleKey = (value: string) => value.startsWith('@element:')
|
|
65
|
+
|
|
66
|
+
export const getEditableElementStyleKey = (element: HTMLElement) => {
|
|
67
|
+
const scopedRoot = element.closest<HTMLElement>('[data-wikex-scope]')
|
|
68
|
+
const root = scopedRoot ?? element.ownerDocument.body
|
|
69
|
+
const scope = scopedRoot?.dataset.wikexScope || 'body'
|
|
70
|
+
const segments: string[] = []
|
|
71
|
+
let current: HTMLElement | null = element
|
|
72
|
+
|
|
73
|
+
while (current && current !== root) {
|
|
74
|
+
const parentElement: HTMLElement | null = current.parentElement
|
|
75
|
+
if (!parentElement) break
|
|
76
|
+
|
|
77
|
+
const tagName = current.tagName.toLowerCase()
|
|
78
|
+
const sameTagSiblings = Array.from(parentElement.children).filter(
|
|
79
|
+
(sibling) => sibling.tagName === current?.tagName,
|
|
80
|
+
)
|
|
81
|
+
const index = sameTagSiblings.indexOf(current) + 1
|
|
82
|
+
segments.push(`${tagName}:nth-of-type(${Math.max(index, 1)})`)
|
|
83
|
+
current = parentElement
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return `@element:${scope}:${segments.reverse().join('>')}`
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const fontValues = new Set<string>(TEXT_FONT_OPTIONS.map((option) => option.value))
|
|
90
|
+
const lineHeightValues = new Set<string>(TEXT_LINE_HEIGHT_OPTIONS.map((option) => option.value))
|
|
91
|
+
|
|
92
|
+
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
93
|
+
Boolean(value) && typeof value === 'object' && !Array.isArray(value)
|
|
94
|
+
|
|
95
|
+
export const normalizeTextStyleValue = (value: unknown): TextStyleValue => {
|
|
96
|
+
if (!isRecord(value)) return {}
|
|
97
|
+
|
|
98
|
+
const fontFamily =
|
|
99
|
+
typeof value.fontFamily === 'string' && fontValues.has(value.fontFamily)
|
|
100
|
+
? (value.fontFamily as TextFontFamily)
|
|
101
|
+
: undefined
|
|
102
|
+
const lineHeight =
|
|
103
|
+
typeof value.lineHeight === 'string' && lineHeightValues.has(value.lineHeight)
|
|
104
|
+
? (value.lineHeight as TextLineHeight)
|
|
105
|
+
: undefined
|
|
106
|
+
const legacyWidth =
|
|
107
|
+
typeof value.width === 'string' && /^\d+(?:\.\d+)?%$/.test(value.width)
|
|
108
|
+
? Number.parseFloat(value.width)
|
|
109
|
+
: undefined
|
|
110
|
+
const widthValue = typeof value.width === 'number' ? value.width : legacyWidth
|
|
111
|
+
const width =
|
|
112
|
+
typeof widthValue === 'number' && Number.isFinite(widthValue)
|
|
113
|
+
? Math.min(100, Math.max(5, widthValue))
|
|
114
|
+
: undefined
|
|
115
|
+
const widthOffset =
|
|
116
|
+
typeof value.widthOffset === 'number' && Number.isFinite(value.widthOffset)
|
|
117
|
+
? Math.min(width ? 100 - width : 95, Math.max(0, value.widthOffset))
|
|
118
|
+
: undefined
|
|
119
|
+
|
|
120
|
+
return { fontFamily, lineHeight, width, widthOffset }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export const normalizeTextStyleMap = (value: unknown): TextStyleMap => {
|
|
124
|
+
if (!isRecord(value)) return {}
|
|
125
|
+
|
|
126
|
+
return Object.fromEntries(
|
|
127
|
+
Object.entries(value).map(([fieldPath, fieldStyle]) => [
|
|
128
|
+
fieldPath,
|
|
129
|
+
normalizeTextStyleValue(fieldStyle),
|
|
130
|
+
]),
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export const getTextStyleCSS = (
|
|
135
|
+
value: TextStyleValue,
|
|
136
|
+
): {
|
|
137
|
+
boxSizing?: 'border-box'
|
|
138
|
+
fontFamily?: string
|
|
139
|
+
lineHeight?: string
|
|
140
|
+
marginLeft?: string
|
|
141
|
+
marginRight?: string
|
|
142
|
+
maxWidth?: string
|
|
143
|
+
width?: string
|
|
144
|
+
} => {
|
|
145
|
+
const fontFamily = TEXT_FONT_OPTIONS.find((option) => option.value === value.fontFamily)?.css
|
|
146
|
+
const lineHeight = TEXT_LINE_HEIGHT_OPTIONS.find(
|
|
147
|
+
(option) => option.value === value.lineHeight,
|
|
148
|
+
)?.css
|
|
149
|
+
const width = typeof value.width === 'number' ? `${value.width}%` : undefined
|
|
150
|
+
const widthOffset = typeof value.widthOffset === 'number' ? `${value.widthOffset}%` : undefined
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
boxSizing: width ? 'border-box' : undefined,
|
|
154
|
+
fontFamily: fontFamily || undefined,
|
|
155
|
+
lineHeight: lineHeight || undefined,
|
|
156
|
+
marginLeft: width ? widthOffset || '0%' : undefined,
|
|
157
|
+
marginRight: width ? 'auto' : undefined,
|
|
158
|
+
maxWidth: width ? 'none' : undefined,
|
|
159
|
+
width,
|
|
160
|
+
}
|
|
161
|
+
}
|