@thangph2146/lexical-editor 0.0.6 → 0.0.9
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/editor-x/editor.cjs +604 -497
- package/dist/editor-x/editor.cjs.map +1 -1
- package/dist/editor-x/editor.css +159 -69
- package/dist/editor-x/editor.css.map +1 -1
- package/dist/editor-x/editor.js +344 -237
- package/dist/editor-x/editor.js.map +1 -1
- package/dist/index.cjs +610 -503
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +159 -69
- package/dist/index.css.map +1 -1
- package/dist/index.js +344 -237
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/src/editor-ui/content-editable.tsx +30 -42
- package/src/editor-x/plugins.tsx +8 -5
- package/src/plugins/images-plugin.tsx +3 -2
- package/src/plugins/layout-plugin.tsx +93 -62
- package/src/themes/_mixins.scss +12 -7
- package/src/themes/_variables.scss +2 -1
- package/src/themes/core/_reset.scss +10 -6
- package/src/themes/plugins/_color-picker.scss +1 -0
- package/src/themes/plugins/_layout.scss +3 -7
- package/src/themes/plugins/_list-color.scss +2 -0
- package/src/themes/plugins/_menus-and-pickers.scss +2 -0
- package/src/themes/plugins/_toolbar.scss +7 -7
- package/src/themes/ui-components/_button.scss +3 -3
- package/src/themes/ui-components/_flex.scss +2 -0
- package/src/themes/ui-components/_number-input.scss +81 -0
- package/src/themes/ui-components/_text-utilities.scss +1 -1
- package/src/themes/ui-components.scss +1 -0
- package/src/ui/flex.tsx +9 -2
- package/src/ui/number-input.tsx +104 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thangph2146/lexical-editor",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"description": "Rich Text Editor library based on Lexical for React/Next.js",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
"dev": "tsup --watch",
|
|
21
21
|
"lint": "eslint",
|
|
22
22
|
"format": "prettier --write \"**/*.{ts,tsx}\"",
|
|
23
|
-
"typecheck": "tsc --noEmit"
|
|
23
|
+
"typecheck": "tsc --noEmit",
|
|
24
|
+
"prepublishOnly": "pnpm build && pnpm typecheck"
|
|
24
25
|
},
|
|
25
26
|
"dependencies": {
|
|
26
27
|
"@lexical/code": "^0.38.2",
|
|
@@ -1,42 +1,30 @@
|
|
|
1
|
-
import { JSX } from "react"
|
|
2
|
-
import { ContentEditable as LexicalContentEditable } from "@lexical/react/LexicalContentEditable"
|
|
3
|
-
import { cn } from "../lib/utils"
|
|
4
|
-
|
|
5
|
-
type Props = {
|
|
6
|
-
placeholder
|
|
7
|
-
className?: string
|
|
8
|
-
placeholderClassName?: string
|
|
9
|
-
placeholderDefaults?: boolean // apply default positioning/padding for placeholder
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function ContentEditable({
|
|
13
|
-
placeholder,
|
|
14
|
-
className,
|
|
15
|
-
placeholderClassName,
|
|
16
|
-
placeholderDefaults = true,
|
|
17
|
-
}: Props): JSX.Element {
|
|
18
|
-
const isReadOnlyOrReview = className?.includes("--readonly") || className?.includes("--review")
|
|
19
|
-
|
|
20
|
-
return (
|
|
21
|
-
<LexicalContentEditable
|
|
22
|
-
className={cn(
|
|
23
|
-
"ContentEditable__root relative block focus:outline-none",
|
|
24
|
-
!isReadOnlyOrReview && "min-h-72 px-8 py-4",
|
|
25
|
-
className
|
|
26
|
-
)}
|
|
27
|
-
aria-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
className={cn(
|
|
32
|
-
placeholderClassName,
|
|
33
|
-
"text-muted-foreground pointer-events-none select-none",
|
|
34
|
-
placeholderDefaults && !isReadOnlyOrReview && "absolute top-0 left-0 overflow-hidden px-8 py-[18px] text-ellipsis"
|
|
35
|
-
)}
|
|
36
|
-
>
|
|
37
|
-
{placeholder}
|
|
38
|
-
</div>
|
|
39
|
-
}
|
|
40
|
-
/>
|
|
41
|
-
)
|
|
42
|
-
}
|
|
1
|
+
import { JSX } from "react"
|
|
2
|
+
import { ContentEditable as LexicalContentEditable } from "@lexical/react/LexicalContentEditable"
|
|
3
|
+
import { cn } from "../lib/utils"
|
|
4
|
+
|
|
5
|
+
type Props = {
|
|
6
|
+
placeholder?: string
|
|
7
|
+
className?: string
|
|
8
|
+
placeholderClassName?: string
|
|
9
|
+
placeholderDefaults?: boolean // apply default positioning/padding for placeholder
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function ContentEditable({
|
|
13
|
+
placeholder,
|
|
14
|
+
className,
|
|
15
|
+
placeholderClassName,
|
|
16
|
+
placeholderDefaults = true,
|
|
17
|
+
}: Props): JSX.Element {
|
|
18
|
+
const isReadOnlyOrReview = className?.includes("--readonly") || className?.includes("--review")
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<LexicalContentEditable
|
|
22
|
+
className={cn(
|
|
23
|
+
"ContentEditable__root relative block focus:outline-none",
|
|
24
|
+
!isReadOnlyOrReview && "min-h-72 px-8 py-4",
|
|
25
|
+
className
|
|
26
|
+
)}
|
|
27
|
+
aria-label={"Editor nội dung"}
|
|
28
|
+
/>
|
|
29
|
+
)
|
|
30
|
+
}
|
package/src/editor-x/plugins.tsx
CHANGED
|
@@ -110,6 +110,7 @@ import { TABLE } from "../transformers/markdown-table-transformer"
|
|
|
110
110
|
import { TWEET } from "../transformers/markdown-tweet-transformer"
|
|
111
111
|
import { UNORDERED_LIST } from "../transformers/markdown-list-transformer"
|
|
112
112
|
import { Separator } from "../ui/separator"
|
|
113
|
+
import { Flex } from "../ui/flex"
|
|
113
114
|
|
|
114
115
|
export function Plugins({
|
|
115
116
|
readOnly = false,
|
|
@@ -227,11 +228,13 @@ export function Plugins({
|
|
|
227
228
|
<div className={cn("editor-relative-full")}>
|
|
228
229
|
<div className={cn("editor-relative-full")} ref={onRef}>
|
|
229
230
|
<ContentEditable
|
|
230
|
-
placeholder=
|
|
231
|
+
placeholder=""
|
|
231
232
|
className={cn(
|
|
232
233
|
"editor-content-editable",
|
|
233
234
|
readOnly && "editor-content-editable--readonly"
|
|
234
235
|
)}
|
|
236
|
+
placeholderClassName="editor-placeholder"
|
|
237
|
+
placeholderDefaults={false}
|
|
235
238
|
/>
|
|
236
239
|
</div>
|
|
237
240
|
</div>
|
|
@@ -349,11 +352,11 @@ export function Plugins({
|
|
|
349
352
|
{!readOnly && (
|
|
350
353
|
<ActionsPlugin>
|
|
351
354
|
<div className="editor-actions-bar">
|
|
352
|
-
<
|
|
355
|
+
<Flex align="center" className="editor-flex-shrink-0">
|
|
353
356
|
<CounterCharacterPlugin charset="UTF-16" />
|
|
354
|
-
</
|
|
357
|
+
</Flex>
|
|
355
358
|
<div className="editor-flex-1" />
|
|
356
|
-
<
|
|
359
|
+
<Flex align="center" justify="end" gap={1} wrap="nowrap" className="editor-flex-shrink-0">
|
|
357
360
|
<SpeechToTextPlugin />
|
|
358
361
|
<ShareContentPlugin />
|
|
359
362
|
<ImportExportPlugin />
|
|
@@ -376,7 +379,7 @@ export function Plugins({
|
|
|
376
379
|
<EditModeTogglePlugin />
|
|
377
380
|
<ClearEditorActionPlugin />
|
|
378
381
|
<TreeViewPlugin />
|
|
379
|
-
</
|
|
382
|
+
</Flex>
|
|
380
383
|
</div>
|
|
381
384
|
</ActionsPlugin>
|
|
382
385
|
)}
|
|
@@ -41,6 +41,7 @@ import { Button } from "../ui/button"
|
|
|
41
41
|
import { DialogFooter } from "../ui/dialog"
|
|
42
42
|
import { Input } from "../ui/input"
|
|
43
43
|
import { Label } from "../ui/label"
|
|
44
|
+
import { Flex } from "../ui/flex"
|
|
44
45
|
import {
|
|
45
46
|
Tabs,
|
|
46
47
|
TabsContent,
|
|
@@ -337,9 +338,9 @@ export function InsertImageUploadsDialogBody({
|
|
|
337
338
|
<div className="editor-form-item">
|
|
338
339
|
<Label>Chọn hình ảnh từ thư viện</Label>
|
|
339
340
|
{isLoading ? (
|
|
340
|
-
<
|
|
341
|
+
<Flex align="center" justify="center" className="editor-py-8">
|
|
341
342
|
<Loader2 className="editor-loader" />
|
|
342
|
-
</
|
|
343
|
+
</Flex>
|
|
343
344
|
) : !folderTree || (folderTree.subfolders.length === 0 && folderTree.images.length === 0) ? (
|
|
344
345
|
<div className="editor-empty-state">
|
|
345
346
|
<TypographySpanSmallMuted>Chưa có hình ảnh nào được upload</TypographySpanSmallMuted>
|
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
LayoutItemNode,
|
|
44
44
|
} from "../nodes/layout-item-node"
|
|
45
45
|
import { Button } from "../ui/button"
|
|
46
|
+
import { NumberInput } from "../ui/number-input"
|
|
46
47
|
import {
|
|
47
48
|
Select,
|
|
48
49
|
SelectContent,
|
|
@@ -62,7 +63,6 @@ import {
|
|
|
62
63
|
ColorPickerPresets,
|
|
63
64
|
ColorPickerTrigger,
|
|
64
65
|
} from "../editor-ui/color-picker"
|
|
65
|
-
import { Input } from "../ui/input"
|
|
66
66
|
import { Flex } from "../ui/flex"
|
|
67
67
|
import { useEditorModal } from "../editor-hooks/use-modal"
|
|
68
68
|
import { logger } from "../lib/logger"
|
|
@@ -82,14 +82,16 @@ type InsertLayoutPayload =
|
|
|
82
82
|
| {
|
|
83
83
|
template: string
|
|
84
84
|
itemBackgroundColor?: string
|
|
85
|
-
|
|
85
|
+
itemPaddingXPx?: number
|
|
86
|
+
itemPaddingYPx?: number
|
|
86
87
|
itemBorderRadiusPx?: number
|
|
87
88
|
}
|
|
88
89
|
|
|
89
90
|
type LayoutDialogValues = {
|
|
90
91
|
template: string
|
|
91
92
|
itemBackgroundColor: string
|
|
92
|
-
|
|
93
|
+
itemPaddingXPx: number
|
|
94
|
+
itemPaddingYPx: number
|
|
93
95
|
itemBorderRadiusPx: number
|
|
94
96
|
}
|
|
95
97
|
|
|
@@ -116,13 +118,15 @@ export function InsertLayoutDialog({
|
|
|
116
118
|
const [backgroundColor, setBackgroundColor] = useState(
|
|
117
119
|
initialValues?.itemBackgroundColor ?? "#ffffff"
|
|
118
120
|
)
|
|
119
|
-
const [
|
|
121
|
+
const [paddingXPx, setPaddingXPx] = useState(initialValues?.itemPaddingXPx ?? 12)
|
|
122
|
+
const [paddingYPx, setPaddingYPx] = useState(initialValues?.itemPaddingYPx ?? 12)
|
|
120
123
|
const [borderRadiusPx, setBorderRadiusPx] = useState(
|
|
121
124
|
initialValues?.itemBorderRadiusPx ?? 8
|
|
122
125
|
)
|
|
123
126
|
const layoutRef = useRef(layout)
|
|
124
127
|
const backgroundColorRef = useRef(backgroundColor)
|
|
125
|
-
const
|
|
128
|
+
const paddingXPxRef = useRef(paddingXPx)
|
|
129
|
+
const paddingYPxRef = useRef(paddingYPx)
|
|
126
130
|
const borderRadiusPxRef = useRef(borderRadiusPx)
|
|
127
131
|
|
|
128
132
|
useEffect(() => {
|
|
@@ -132,8 +136,11 @@ export function InsertLayoutDialog({
|
|
|
132
136
|
backgroundColorRef.current = backgroundColor
|
|
133
137
|
}, [backgroundColor])
|
|
134
138
|
useEffect(() => {
|
|
135
|
-
|
|
136
|
-
}, [
|
|
139
|
+
paddingXPxRef.current = paddingXPx
|
|
140
|
+
}, [paddingXPx])
|
|
141
|
+
useEffect(() => {
|
|
142
|
+
paddingYPxRef.current = paddingYPx
|
|
143
|
+
}, [paddingYPx])
|
|
137
144
|
useEffect(() => {
|
|
138
145
|
borderRadiusPxRef.current = borderRadiusPx
|
|
139
146
|
}, [borderRadiusPx])
|
|
@@ -143,10 +150,16 @@ export function InsertLayoutDialog({
|
|
|
143
150
|
setBackgroundColor(value)
|
|
144
151
|
}
|
|
145
152
|
|
|
146
|
-
const
|
|
153
|
+
const onPaddingXChange = (next: number) => {
|
|
147
154
|
const value = Math.min(Math.max(next, 0), 64)
|
|
148
|
-
|
|
149
|
-
|
|
155
|
+
paddingXPxRef.current = value
|
|
156
|
+
setPaddingXPx(value)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const onPaddingYChange = (next: number) => {
|
|
160
|
+
const value = Math.min(Math.max(next, 0), 64)
|
|
161
|
+
paddingYPxRef.current = value
|
|
162
|
+
setPaddingYPx(value)
|
|
150
163
|
}
|
|
151
164
|
|
|
152
165
|
const onBorderRadiusChange = (next: number) => {
|
|
@@ -160,7 +173,8 @@ export function InsertLayoutDialog({
|
|
|
160
173
|
const values: LayoutDialogValues = {
|
|
161
174
|
template: layoutRef.current,
|
|
162
175
|
itemBackgroundColor: backgroundColorRef.current,
|
|
163
|
-
|
|
176
|
+
itemPaddingXPx: paddingXPxRef.current,
|
|
177
|
+
itemPaddingYPx: paddingYPxRef.current,
|
|
164
178
|
itemBorderRadiusPx: borderRadiusPxRef.current,
|
|
165
179
|
}
|
|
166
180
|
logger.info("[Layout] Submit dialog values", {
|
|
@@ -177,7 +191,7 @@ export function InsertLayoutDialog({
|
|
|
177
191
|
}
|
|
178
192
|
|
|
179
193
|
return (
|
|
180
|
-
|
|
194
|
+
<Flex direction="column" gap={4}>
|
|
181
195
|
<Select onValueChange={setLayout} value={layout}>
|
|
182
196
|
<SelectTrigger className="editor-input-lg editor-w-full">
|
|
183
197
|
<SelectValue placeholder={buttonLabel} />
|
|
@@ -191,7 +205,7 @@ export function InsertLayoutDialog({
|
|
|
191
205
|
</SelectContent>
|
|
192
206
|
</Select>
|
|
193
207
|
<div className="editor-layout-dialog-grid">
|
|
194
|
-
<
|
|
208
|
+
<Flex direction="column" gap={1.5}>
|
|
195
209
|
<div className="editor-text-xs-muted">Background</div>
|
|
196
210
|
<ColorPicker
|
|
197
211
|
modal
|
|
@@ -227,48 +241,37 @@ export function InsertLayoutDialog({
|
|
|
227
241
|
<ColorPickerPresets />
|
|
228
242
|
</ColorPickerContent>
|
|
229
243
|
</ColorPicker>
|
|
230
|
-
</
|
|
231
|
-
<
|
|
232
|
-
<div className="editor-text-xs-muted">Padding (px)</div>
|
|
233
|
-
<
|
|
234
|
-
type="number"
|
|
244
|
+
</Flex>
|
|
245
|
+
<Flex direction="column" gap={1.5}>
|
|
246
|
+
<div className="editor-text-xs-muted">Padding X (px)</div>
|
|
247
|
+
<NumberInput
|
|
235
248
|
min={0}
|
|
236
249
|
max={64}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
onChange={(event) => {
|
|
240
|
-
const next = Number.parseInt(event.target.value, 10)
|
|
241
|
-
if (Number.isFinite(next)) {
|
|
242
|
-
onPaddingChange(next)
|
|
243
|
-
} else {
|
|
244
|
-
onPaddingChange(0)
|
|
245
|
-
}
|
|
246
|
-
}}
|
|
247
|
-
className="editor-input-lg editor-w-full"
|
|
250
|
+
value={paddingXPx}
|
|
251
|
+
onValueChange={onPaddingXChange}
|
|
248
252
|
/>
|
|
249
|
-
</
|
|
250
|
-
<
|
|
253
|
+
</Flex>
|
|
254
|
+
<Flex direction="column" gap={1.5}>
|
|
255
|
+
<div className="editor-text-xs-muted">Padding Y (px)</div>
|
|
256
|
+
<NumberInput
|
|
257
|
+
min={0}
|
|
258
|
+
max={64}
|
|
259
|
+
value={paddingYPx}
|
|
260
|
+
onValueChange={onPaddingYChange}
|
|
261
|
+
/>
|
|
262
|
+
</Flex>
|
|
263
|
+
<Flex direction="column" gap={1.5}>
|
|
251
264
|
<div className="editor-text-xs-muted">Border radius (px)</div>
|
|
252
|
-
<
|
|
253
|
-
type="number"
|
|
265
|
+
<NumberInput
|
|
254
266
|
min={0}
|
|
255
267
|
max={64}
|
|
256
|
-
step={1}
|
|
257
268
|
value={borderRadiusPx}
|
|
258
|
-
|
|
259
|
-
const next = Number.parseInt(event.target.value, 10)
|
|
260
|
-
if (Number.isFinite(next)) {
|
|
261
|
-
onBorderRadiusChange(next)
|
|
262
|
-
} else {
|
|
263
|
-
onBorderRadiusChange(0)
|
|
264
|
-
}
|
|
265
|
-
}}
|
|
266
|
-
className="editor-input-lg editor-w-full"
|
|
269
|
+
onValueChange={onBorderRadiusChange}
|
|
267
270
|
/>
|
|
268
|
-
</
|
|
271
|
+
</Flex>
|
|
269
272
|
</div>
|
|
270
273
|
<Button onClick={onClick}>{submitLabel}</Button>
|
|
271
|
-
|
|
274
|
+
</Flex>
|
|
272
275
|
)
|
|
273
276
|
}
|
|
274
277
|
|
|
@@ -339,29 +342,42 @@ export function LayoutPlugin(): JSX.Element | null {
|
|
|
339
342
|
return match?.[1]?.trim()
|
|
340
343
|
}
|
|
341
344
|
|
|
342
|
-
const extractNumericStyle = (style: string, property: string): number | undefined => {
|
|
345
|
+
const extractNumericStyle = (style: string, property: string): number[] | undefined => {
|
|
343
346
|
const value = extractStyleValue(style, property)
|
|
344
347
|
if (!value) {
|
|
345
348
|
return undefined
|
|
346
349
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
350
|
+
// Remove !important and split by whitespace
|
|
351
|
+
const values = value.replace(/!important/gi, "").trim().split(/\s+/)
|
|
352
|
+
const parsedValues = values
|
|
353
|
+
.map((v) => {
|
|
354
|
+
const match = v.match(/^(\d+)px/i)
|
|
355
|
+
if (!match?.[1]) return null
|
|
356
|
+
const parsed = Number.parseInt(match[1], 10)
|
|
357
|
+
return Number.isFinite(parsed) ? parsed : null
|
|
358
|
+
})
|
|
359
|
+
.filter((v): v is number => v !== null)
|
|
360
|
+
|
|
361
|
+
if (parsedValues.length === 0) return undefined
|
|
362
|
+
return parsedValues
|
|
353
363
|
}
|
|
354
364
|
|
|
355
365
|
const buildLayoutItemStyle = ({
|
|
356
366
|
itemBackgroundColor,
|
|
357
|
-
|
|
367
|
+
itemPaddingXPx,
|
|
368
|
+
itemPaddingYPx,
|
|
358
369
|
itemBorderRadiusPx,
|
|
359
370
|
}: LayoutDialogValues): string => {
|
|
360
371
|
const itemStyles: string[] = []
|
|
361
372
|
if (itemBackgroundColor.trim()) {
|
|
362
373
|
itemStyles.push(`background-color: ${itemBackgroundColor.trim()}`)
|
|
363
374
|
}
|
|
364
|
-
itemStyles.push(
|
|
375
|
+
itemStyles.push(
|
|
376
|
+
`padding: ${Math.min(Math.max(itemPaddingYPx, 0), 64)}px ${Math.min(
|
|
377
|
+
Math.max(itemPaddingXPx, 0),
|
|
378
|
+
64
|
|
379
|
+
)}px`
|
|
380
|
+
)
|
|
365
381
|
itemStyles.push(
|
|
366
382
|
`border-radius: ${Math.min(Math.max(itemBorderRadiusPx, 0), 64)}px`
|
|
367
383
|
)
|
|
@@ -376,7 +392,10 @@ export function LayoutPlugin(): JSX.Element | null {
|
|
|
376
392
|
}
|
|
377
393
|
|
|
378
394
|
const background = values.itemBackgroundColor.trim()
|
|
379
|
-
const padding = `${Math.min(Math.max(values.
|
|
395
|
+
const padding = `${Math.min(Math.max(values.itemPaddingYPx, 0), 64)}px ${Math.min(
|
|
396
|
+
Math.max(values.itemPaddingXPx, 0),
|
|
397
|
+
64
|
|
398
|
+
)}px`
|
|
380
399
|
const borderRadius = `${Math.min(Math.max(values.itemBorderRadiusPx, 0), 64)}px`
|
|
381
400
|
if (background) {
|
|
382
401
|
element.style.setProperty("background-color", background)
|
|
@@ -438,6 +457,9 @@ export function LayoutPlugin(): JSX.Element | null {
|
|
|
438
457
|
}
|
|
439
458
|
|
|
440
459
|
const style = layoutItem.getStyle()
|
|
460
|
+
const paddingValues = extractNumericStyle(style, "padding")
|
|
461
|
+
const borderRadiusValues = extractNumericStyle(style, "border-radius")
|
|
462
|
+
|
|
441
463
|
payload = {
|
|
442
464
|
containerKey: parentContainer.getKey(),
|
|
443
465
|
layoutItemKey: layoutItem.getKey(),
|
|
@@ -445,8 +467,9 @@ export function LayoutPlugin(): JSX.Element | null {
|
|
|
445
467
|
template: parentContainer.getTemplateColumns(),
|
|
446
468
|
itemBackgroundColor:
|
|
447
469
|
extractStyleValue(style, "background-color") ?? "#ffffff",
|
|
448
|
-
|
|
449
|
-
|
|
470
|
+
itemPaddingXPx: paddingValues?.[1] ?? paddingValues?.[0] ?? 12,
|
|
471
|
+
itemPaddingYPx: paddingValues?.[0] ?? 12,
|
|
472
|
+
itemBorderRadiusPx: borderRadiusValues?.[0] ?? 8,
|
|
450
473
|
},
|
|
451
474
|
}
|
|
452
475
|
logger.debug("[Layout] Resolved payload from target", payload)
|
|
@@ -566,12 +589,19 @@ export function LayoutPlugin(): JSX.Element | null {
|
|
|
566
589
|
typeof payload === "string"
|
|
567
590
|
? undefined
|
|
568
591
|
: payload.itemBackgroundColor?.trim()
|
|
569
|
-
const
|
|
592
|
+
const itemPaddingXPx =
|
|
593
|
+
typeof payload === "string"
|
|
594
|
+
? undefined
|
|
595
|
+
: typeof payload.itemPaddingXPx === "number" &&
|
|
596
|
+
Number.isFinite(payload.itemPaddingXPx)
|
|
597
|
+
? Math.min(Math.max(payload.itemPaddingXPx, 0), 64)
|
|
598
|
+
: undefined
|
|
599
|
+
const itemPaddingYPx =
|
|
570
600
|
typeof payload === "string"
|
|
571
601
|
? undefined
|
|
572
|
-
: typeof payload.
|
|
573
|
-
Number.isFinite(payload.
|
|
574
|
-
? Math.min(Math.max(payload.
|
|
602
|
+
: typeof payload.itemPaddingYPx === "number" &&
|
|
603
|
+
Number.isFinite(payload.itemPaddingYPx)
|
|
604
|
+
? Math.min(Math.max(payload.itemPaddingYPx, 0), 64)
|
|
575
605
|
: undefined
|
|
576
606
|
const itemBorderRadiusPx =
|
|
577
607
|
typeof payload === "string"
|
|
@@ -584,7 +614,8 @@ export function LayoutPlugin(): JSX.Element | null {
|
|
|
584
614
|
const itemStyle = buildLayoutItemStyle({
|
|
585
615
|
template,
|
|
586
616
|
itemBackgroundColor: itemBackgroundColor ?? "#ffffff",
|
|
587
|
-
|
|
617
|
+
itemPaddingXPx: itemPaddingXPx ?? 12,
|
|
618
|
+
itemPaddingYPx: itemPaddingYPx ?? 12,
|
|
588
619
|
itemBorderRadiusPx: itemBorderRadiusPx ?? 8,
|
|
589
620
|
})
|
|
590
621
|
|
package/src/themes/_mixins.scss
CHANGED
|
@@ -26,9 +26,14 @@
|
|
|
26
26
|
justify-content: space-between;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
@mixin flex-row {
|
|
30
|
+
display: flex !important;
|
|
31
|
+
flex-direction: row !important;
|
|
32
|
+
}
|
|
33
|
+
|
|
29
34
|
@mixin flex-col {
|
|
30
|
-
display: flex;
|
|
31
|
-
flex-direction: column;
|
|
35
|
+
display: flex !important;
|
|
36
|
+
flex-direction: column !important;
|
|
32
37
|
}
|
|
33
38
|
|
|
34
39
|
@mixin flex-center-justify {
|
|
@@ -63,19 +68,19 @@
|
|
|
63
68
|
}
|
|
64
69
|
|
|
65
70
|
@mixin shadow-sm {
|
|
66
|
-
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
|
71
|
+
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05) !important;
|
|
67
72
|
}
|
|
68
73
|
|
|
69
74
|
@mixin shadow-md {
|
|
70
|
-
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
|
75
|
+
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06) !important;
|
|
71
76
|
}
|
|
72
77
|
|
|
73
78
|
@mixin shadow-lg {
|
|
74
|
-
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
|
79
|
+
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05) !important;
|
|
75
80
|
}
|
|
76
81
|
|
|
77
82
|
@mixin shadow-xl {
|
|
78
|
-
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
|
83
|
+
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04) !important;
|
|
79
84
|
}
|
|
80
85
|
|
|
81
86
|
@mixin absolute-full {
|
|
@@ -138,7 +143,7 @@
|
|
|
138
143
|
// --- Unified Hover Styles ---
|
|
139
144
|
@mixin editor-hover-base {
|
|
140
145
|
transform: translateY(-1px);
|
|
141
|
-
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
|
146
|
+
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06) !important;
|
|
142
147
|
}
|
|
143
148
|
|
|
144
149
|
@mixin editor-active-base {
|
|
@@ -172,7 +172,8 @@ $editor-popover-max-height: 300px !default;
|
|
|
172
172
|
$editor-format-select-width: 72px !default;
|
|
173
173
|
$editor-floating-toolbar-max-width: 560px !default;
|
|
174
174
|
$editor-color-picker-height: 160px !default;
|
|
175
|
-
|
|
175
|
+
$editor-color-picker-width: 380px !default;
|
|
176
|
+
$editor-scroll-area-max-height: 350px !default;
|
|
176
177
|
$editor-link-editor-max-width: 400px !default;
|
|
177
178
|
$editor-component-picker-width: 250px !default;
|
|
178
179
|
$editor-mentions-width: 200px !default;
|
|
@@ -11,6 +11,15 @@
|
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
// Global resets
|
|
15
|
+
*, *::before, *::after {
|
|
16
|
+
box-sizing: border-box;
|
|
17
|
+
|
|
18
|
+
@supports (color: color-mix(in lab, red, red)) {
|
|
19
|
+
outline-color: color-mix(in oklab, var(--ring) 50%, transparent);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
14
23
|
// Lexical Editor Root
|
|
15
24
|
.lexical-editor-root {
|
|
16
25
|
font-family: $editor-font-family !important;
|
|
@@ -24,12 +33,7 @@
|
|
|
24
33
|
text-align: left !important;
|
|
25
34
|
box-sizing: border-box !important;
|
|
26
35
|
|
|
27
|
-
|
|
28
|
-
*, *::before, *::after {
|
|
29
|
-
box-sizing: border-box;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
p, ul, ol, li, span, strong, em, b, i, blockquote, h1, h2, h3, h4, h5, h6 {
|
|
36
|
+
p, div, ul, ol, li, span, strong, em, b, i, blockquote, h1, h2, h3, h4, h5, h6 {
|
|
33
37
|
margin: 0 !important;
|
|
34
38
|
padding: 0;
|
|
35
39
|
border: 0 !important;
|
|
@@ -4,17 +4,13 @@
|
|
|
4
4
|
.editor-layout-dialog-grid {
|
|
5
5
|
display: grid;
|
|
6
6
|
grid-template-columns: repeat(1, minmax(0, 1fr));
|
|
7
|
-
gap: $editor-gap-
|
|
7
|
+
gap: $editor-gap-4;
|
|
8
8
|
|
|
9
|
-
@media (min-width:
|
|
10
|
-
grid-template-columns: repeat(
|
|
9
|
+
@media (min-width: 480px) {
|
|
10
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
.editor-layout-dialog-group {
|
|
15
|
-
@include flex-col;
|
|
16
|
-
gap: $editor-gap-1-5;
|
|
17
|
-
}
|
|
18
14
|
|
|
19
15
|
.editor-layout-color-trigger {
|
|
20
16
|
height: $editor-control-size-xl; // h-11
|
|
@@ -121,6 +121,8 @@
|
|
|
121
121
|
overflow-x: auto;
|
|
122
122
|
flex-wrap: nowrap;
|
|
123
123
|
border-top: $editor-border-width solid var(--border);
|
|
124
|
+
border-bottom-left-radius: 8px;
|
|
125
|
+
border-bottom-right-radius: 8px;
|
|
124
126
|
padding: $editor-gap-1-5 $editor-gap-3;
|
|
125
127
|
@include backdrop-blur($editor-dialog-overlay-blur * 2, color-mix(in srgb, var(--background), transparent 50%));
|
|
126
128
|
|
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
@include flex-center;
|
|
9
9
|
flex-wrap: wrap;
|
|
10
10
|
gap: $editor-gap-1;
|
|
11
|
-
border-bottom: $editor-border-width solid var(--border);
|
|
12
|
-
border-top-left-radius: $editor-border-radius;
|
|
13
|
-
border-top-right-radius: $editor-border-radius;
|
|
11
|
+
border-bottom: $editor-border-width solid var(--border) !important;
|
|
12
|
+
border-top-left-radius: $editor-border-radius !important;
|
|
13
|
+
border-top-right-radius: $editor-border-radius !important;
|
|
14
14
|
@include backdrop-blur($editor-dialog-overlay-blur, rgba(255, 255, 255, 0.8));
|
|
15
|
-
padding: $editor-gap-
|
|
15
|
+
padding: $editor-gap-2 !important;
|
|
16
16
|
@include shadow-sm;
|
|
17
17
|
width: 100%;
|
|
18
18
|
overflow-x: visible;
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
.editor-toolbar-group {
|
|
24
24
|
@include flex-center;
|
|
25
25
|
gap: $editor-gap-1;
|
|
26
|
-
padding: $editor-gap-1;
|
|
26
|
+
padding: $editor-gap-1 !important;
|
|
27
27
|
@include rounded-md;
|
|
28
28
|
}
|
|
29
29
|
|
|
@@ -31,6 +31,6 @@
|
|
|
31
31
|
|
|
32
32
|
.editor-toolbar-separator {
|
|
33
33
|
height: $editor-icon-size-lg !important; // h-6 (standard separator height)
|
|
34
|
-
margin-left: $editor-gap-1;
|
|
35
|
-
margin-right: $editor-gap-1;
|
|
34
|
+
margin-left: $editor-gap-1 !important;
|
|
35
|
+
margin-right: $editor-gap-1 !important;
|
|
36
36
|
}
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
position: relative;
|
|
18
18
|
padding: $editor-gap-2 $editor-gap-4;
|
|
19
19
|
box-sizing: border-box; // Ensure consistent sizing
|
|
20
|
+
@include shadow-sm;
|
|
20
21
|
@include editor-button-interactive;
|
|
21
22
|
|
|
22
23
|
&:disabled {
|
|
@@ -53,7 +54,6 @@
|
|
|
53
54
|
background-color: var(--primary);
|
|
54
55
|
color: var(--primary-foreground);
|
|
55
56
|
border-color: var(--primary);
|
|
56
|
-
@include shadow-sm;
|
|
57
57
|
|
|
58
58
|
&:hover {
|
|
59
59
|
background-color: color-mix(in srgb, var(--primary), black 10%);
|
|
@@ -65,7 +65,6 @@
|
|
|
65
65
|
background-color: var(--destructive);
|
|
66
66
|
color: var(--destructive-foreground);
|
|
67
67
|
border-color: var(--destructive);
|
|
68
|
-
@include shadow-sm;
|
|
69
68
|
|
|
70
69
|
&:hover {
|
|
71
70
|
background-color: color-mix(in srgb, var(--destructive), black 10%);
|
|
@@ -114,11 +113,12 @@
|
|
|
114
113
|
border: none;
|
|
115
114
|
padding: 0;
|
|
116
115
|
height: auto;
|
|
116
|
+
box-shadow: none !important;
|
|
117
117
|
|
|
118
118
|
&:hover {
|
|
119
119
|
text-decoration-line: underline;
|
|
120
120
|
transform: none;
|
|
121
|
-
box-shadow: none;
|
|
121
|
+
box-shadow: none !important;
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
124
|
|