@sikka/hawa 0.0.229 → 0.0.231
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/styles.css +65 -1
- package/es/elements/ArrowCarousel.d.ts +13 -0
- package/es/elements/FloatingComment - ContentEditable.d.ts +6 -0
- package/es/elements/FloatingComment.d.ts +6 -0
- package/es/elements/index.d.ts +2 -0
- package/es/index.es.js +1 -1
- package/lib/elements/ArrowCarousel.d.ts +13 -0
- package/lib/elements/FloatingComment - ContentEditable.d.ts +6 -0
- package/lib/elements/FloatingComment.d.ts +6 -0
- package/lib/elements/index.d.ts +2 -0
- package/lib/index.js +1 -1
- package/package.json +3 -2
- package/src/elements/ArrowCarousel.tsx +86 -0
- package/src/elements/FloatingComment - ContentEditable.tsx +514 -0
- package/src/elements/FloatingComment.tsx +369 -0
- package/src/elements/index.ts +2 -0
- package/src/styles.css +65 -1
- package/src/tailwind.css +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sikka/hawa",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.231",
|
|
4
4
|
"description": "SaaS Oriented UI Kit",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"module": "es/index.es.js",
|
|
@@ -48,10 +48,10 @@
|
|
|
48
48
|
"@storybook/react": "^6.5.8",
|
|
49
49
|
"@storybook/storybook-deployer": "^2.8.11",
|
|
50
50
|
"@storybook/theming": "^6.5.8",
|
|
51
|
+
"@swc/core": "^1.3.53",
|
|
51
52
|
"@types/react": "^18.0.25",
|
|
52
53
|
"@types/react-dom": "^18.0.9",
|
|
53
54
|
"babel-loader": "^8.2.2",
|
|
54
|
-
"@swc/core": "^1.3.53",
|
|
55
55
|
"clsx": "^1.2.1",
|
|
56
56
|
"color": "^4.2.3",
|
|
57
57
|
"crypto-js": "^4.1.1",
|
|
@@ -111,6 +111,7 @@
|
|
|
111
111
|
"react-hook-form": "^7.28.0",
|
|
112
112
|
"react-icons": "^4.6.0",
|
|
113
113
|
"react-select": "^5.3.2",
|
|
114
|
+
"rich-textarea": "^0.21.1",
|
|
114
115
|
"rollup-plugin-typescript2": "^0.34.1",
|
|
115
116
|
"start": "^5.1.0"
|
|
116
117
|
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
|
|
3
|
+
import { FaArrowLeft, FaArrowRight } from "react-icons/fa"
|
|
4
|
+
|
|
5
|
+
type Item = {
|
|
6
|
+
label?: string
|
|
7
|
+
icon?: JSX.Element
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
type ComponentTypes = {
|
|
11
|
+
items: Item[]
|
|
12
|
+
index?: number
|
|
13
|
+
arrowSize?: number
|
|
14
|
+
labelSize?: "small" | "medium" | "big"
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const Arrow = (props: {
|
|
18
|
+
icon: any
|
|
19
|
+
size: number
|
|
20
|
+
onClick?: () => void
|
|
21
|
+
disabled?: boolean
|
|
22
|
+
}) => {
|
|
23
|
+
return (
|
|
24
|
+
<props.icon
|
|
25
|
+
className={
|
|
26
|
+
props.disabled || false ? "text-gray-300" : "hover:text-gray-500"
|
|
27
|
+
}
|
|
28
|
+
size={props.size}
|
|
29
|
+
onClick={props.onClick || (() => {})}
|
|
30
|
+
/>
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const ArrowCarousel: React.FunctionComponent<ComponentTypes> = (
|
|
35
|
+
props
|
|
36
|
+
) => {
|
|
37
|
+
const [index, setIndex] = React.useState(props.index || 0)
|
|
38
|
+
|
|
39
|
+
React.useEffect(() => {
|
|
40
|
+
console.log(`INDEX CHANGED TO: ${index}`)
|
|
41
|
+
}, [index])
|
|
42
|
+
|
|
43
|
+
const sizes = {
|
|
44
|
+
small: ["", -8],
|
|
45
|
+
medium: ["2", -11],
|
|
46
|
+
big: ["3", -16],
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<div className="align-center box-boorder relative flex h-min w-min flex-row items-center justify-center rounded bg-white p-4 py-6 shadow-md">
|
|
51
|
+
<Arrow
|
|
52
|
+
icon={FaArrowLeft}
|
|
53
|
+
size={props.arrowSize || 48}
|
|
54
|
+
disabled={index == 0}
|
|
55
|
+
onClick={() => {
|
|
56
|
+
if (index != 0) setIndex(index - 1)
|
|
57
|
+
}}
|
|
58
|
+
/>
|
|
59
|
+
|
|
60
|
+
<div
|
|
61
|
+
className={`relative box-border flex h-min flex-col items-center justify-center p-5`}
|
|
62
|
+
>
|
|
63
|
+
<div>{props.items[index].icon}</div>
|
|
64
|
+
<div
|
|
65
|
+
className={`absolute bottom-0 text-${
|
|
66
|
+
sizes[props.labelSize || "small"][0]
|
|
67
|
+
}xl `}
|
|
68
|
+
style={{
|
|
69
|
+
marginBottom: sizes[props.labelSize || "small"][1],
|
|
70
|
+
}}
|
|
71
|
+
>
|
|
72
|
+
{props.items[index].label}
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
<Arrow
|
|
77
|
+
icon={FaArrowRight}
|
|
78
|
+
size={props.arrowSize || 48}
|
|
79
|
+
disabled={index == props.items.length - 1}
|
|
80
|
+
onClick={() => {
|
|
81
|
+
if (index != props.items.length - 1) setIndex(index + 1)
|
|
82
|
+
}}
|
|
83
|
+
/>
|
|
84
|
+
</div>
|
|
85
|
+
)
|
|
86
|
+
}
|
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
import React, { useRef, useState, useEffect } from "react"
|
|
2
|
+
import { RichTextarea } from "rich-textarea"
|
|
3
|
+
import clsx from "clsx"
|
|
4
|
+
|
|
5
|
+
const Property = (props) => {
|
|
6
|
+
return (
|
|
7
|
+
<div
|
|
8
|
+
className="border-box mr-[5px] flex h-[32px] w-[32px] items-center justify-center rounded bg-gray-400 p-2"
|
|
9
|
+
onMouseDown={props.onMouseDown}
|
|
10
|
+
>
|
|
11
|
+
{props.name}
|
|
12
|
+
</div>
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
type ComponentTypes = {
|
|
17
|
+
foo?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// TODO: Handle copy pasting by assigning to local storage ?
|
|
21
|
+
|
|
22
|
+
const styleClasses = {
|
|
23
|
+
bold: "font-bold",
|
|
24
|
+
italic: "italic",
|
|
25
|
+
under: "underline",
|
|
26
|
+
strike: "line-through",
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const FloatingComment: React.FunctionComponent<ComponentTypes> = (
|
|
30
|
+
props
|
|
31
|
+
) => {
|
|
32
|
+
const [text, setText] = useState({
|
|
33
|
+
content: "",
|
|
34
|
+
stylings: [], // A styling is an object with 2 indices specifying a substring of text, and the applied effect
|
|
35
|
+
revert: [0, 0],
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const field = useRef(null)
|
|
39
|
+
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
let [start, end] = text.revert
|
|
42
|
+
|
|
43
|
+
if (start == 0 && end == 0) return
|
|
44
|
+
|
|
45
|
+
console.log(start)
|
|
46
|
+
console.log(end)
|
|
47
|
+
|
|
48
|
+
// TODO: To get the child node based on the index, get lengths of each child node
|
|
49
|
+
// add to each child node the sum of all previous child nodes
|
|
50
|
+
// Create ranges for 0 - n, n - n1, n - n2, ... where nx is the length of a child node
|
|
51
|
+
// Add 1 to the index
|
|
52
|
+
// Iterate through each range, and check if the index is greater than the start of the range, and less than or equal the end of the range
|
|
53
|
+
let startNode = null
|
|
54
|
+
let endNode = null
|
|
55
|
+
|
|
56
|
+
let total = 0
|
|
57
|
+
let nodes = Array.from(field.current.childNodes)
|
|
58
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
59
|
+
let node: any = nodes[i]
|
|
60
|
+
let sum = node.textContent.length + total
|
|
61
|
+
|
|
62
|
+
if (start > total && start <= sum) {
|
|
63
|
+
startNode = nodes[i]
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (end > total && end <= sum) {
|
|
67
|
+
endNode = nodes[i]
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
total += node.textContent.length
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// console.log(
|
|
74
|
+
// Array.from(field.current.childNodes).map(
|
|
75
|
+
// (item) => item.textContent.length
|
|
76
|
+
// )
|
|
77
|
+
// )
|
|
78
|
+
|
|
79
|
+
console.log(startNode)
|
|
80
|
+
console.log(endNode)
|
|
81
|
+
|
|
82
|
+
var range = document.createRange()
|
|
83
|
+
var sel = window.getSelection()
|
|
84
|
+
|
|
85
|
+
range.setStart(startNode.firstChild, start)
|
|
86
|
+
range.setEnd(endNode.firstChild, end)
|
|
87
|
+
range.collapse(true)
|
|
88
|
+
|
|
89
|
+
sel.removeAllRanges()
|
|
90
|
+
sel.addRange(range)
|
|
91
|
+
}, [text.revert])
|
|
92
|
+
|
|
93
|
+
// utility functions
|
|
94
|
+
const getRange = (start, end) => {
|
|
95
|
+
let result = []
|
|
96
|
+
for (let i = start; i <= end; i++) {
|
|
97
|
+
result.push(i)
|
|
98
|
+
}
|
|
99
|
+
return result
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const intersection = (setA, setB) => {
|
|
103
|
+
const _intersection = new Set()
|
|
104
|
+
for (const elem of setB) {
|
|
105
|
+
if (setA.has(elem)) {
|
|
106
|
+
_intersection.add(elem)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return _intersection
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const getMinimum = (array) => {
|
|
113
|
+
return array.sort((a, b) => a - b)[0]
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const getMaximum = (array) => {
|
|
117
|
+
return array.sort((a, b) => b - a)[0]
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// -1 - types dont match
|
|
121
|
+
// 0 - s1 is surrounded or on the edge of the s2
|
|
122
|
+
// 1 - s1 intersects with s2
|
|
123
|
+
// 2 - s1 does not intersect with s2
|
|
124
|
+
const getCorrelation = (styling1, styling2) => {
|
|
125
|
+
if (styling1.type != styling2.type) return -1
|
|
126
|
+
|
|
127
|
+
if (
|
|
128
|
+
styling2.start <= styling1.start &&
|
|
129
|
+
styling2.finish >= styling1.finish
|
|
130
|
+
) {
|
|
131
|
+
return 0
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
let indices1 = new Set(getRange(styling1.start - 1, styling1.finish - 1))
|
|
135
|
+
let indices2 = new Set(getRange(styling2.start - 1, styling2.finish - 1))
|
|
136
|
+
|
|
137
|
+
let result = intersection(indices1, indices2)
|
|
138
|
+
|
|
139
|
+
return result.size == 0 ? 2 : 1
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Correlation handler
|
|
143
|
+
const stylingSplice = (correlations, stylings, current, type) => {
|
|
144
|
+
// Only one surround correlation is possible at one time, so use .find to fetch it
|
|
145
|
+
|
|
146
|
+
let [_, index, styling] = correlations.find(([c, _, __]) => c == 0)
|
|
147
|
+
|
|
148
|
+
// Remove correlated styling
|
|
149
|
+
stylings = stylings.filter((_, _index) => _index != index)
|
|
150
|
+
|
|
151
|
+
// Get splices
|
|
152
|
+
let added = [
|
|
153
|
+
{
|
|
154
|
+
type: type,
|
|
155
|
+
start: getMinimum([styling.start, current.start]),
|
|
156
|
+
finish: getMaximum([styling.start, current.start]),
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
type: type,
|
|
160
|
+
start: getMinimum([styling.finish, current.finish]),
|
|
161
|
+
finish: getMaximum([styling.finish, current.finish]),
|
|
162
|
+
},
|
|
163
|
+
]
|
|
164
|
+
|
|
165
|
+
// Remove empty splices (edge cases)
|
|
166
|
+
added = added.filter((item) => item.start != item.finish)
|
|
167
|
+
|
|
168
|
+
// Add to current stylings
|
|
169
|
+
stylings = [...stylings, ...added]
|
|
170
|
+
|
|
171
|
+
return stylings
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Correlation handler
|
|
175
|
+
const stylingIntersect = (correlations, stylings, current, type) => {
|
|
176
|
+
// Filter out all intersected stylings
|
|
177
|
+
let intersections = correlations
|
|
178
|
+
.filter(([c, _, __]) => c == 1)
|
|
179
|
+
.map(([_, index, styling]) => {
|
|
180
|
+
return [index, styling]
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
// Add current styling with no index for the sake for endpoint indices
|
|
184
|
+
intersections.push([-1, current])
|
|
185
|
+
|
|
186
|
+
// Get minimum intersection start index
|
|
187
|
+
let start = intersections
|
|
188
|
+
.map(([_, styling]) => styling.start)
|
|
189
|
+
.sort((a, b) => a - b)[0]
|
|
190
|
+
|
|
191
|
+
// Get maximum intersection start index
|
|
192
|
+
let finish = intersections
|
|
193
|
+
.map(([_, styling]) => styling.finish)
|
|
194
|
+
.sort((a, b) => b - a)[0]
|
|
195
|
+
|
|
196
|
+
// Get indices of all intersection
|
|
197
|
+
let indices = intersections.map((e) => e[0])
|
|
198
|
+
|
|
199
|
+
// Remove all from resulting styling array
|
|
200
|
+
stylings = stylings.filter((_, index) => !indices.includes(index))
|
|
201
|
+
|
|
202
|
+
// Add widest styling which encompasses all intersections
|
|
203
|
+
stylings.push({
|
|
204
|
+
type: type,
|
|
205
|
+
start: start,
|
|
206
|
+
finish: finish,
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
return stylings
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const perform = (id) => {
|
|
213
|
+
let stylings = text.stylings.slice()
|
|
214
|
+
let [selectionStart, selectionEnd] = getFieldSelection()
|
|
215
|
+
|
|
216
|
+
if (selectionStart == selectionEnd) return
|
|
217
|
+
|
|
218
|
+
let current = {
|
|
219
|
+
type: id,
|
|
220
|
+
start: selectionStart,
|
|
221
|
+
finish: selectionEnd,
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
let correlations = []
|
|
225
|
+
|
|
226
|
+
// Check the correlation between this requested styling and all other stylings
|
|
227
|
+
for (let i = 0; i < stylings.length; i++) {
|
|
228
|
+
let styling = stylings[i]
|
|
229
|
+
let correlation = getCorrelation(current, styling)
|
|
230
|
+
|
|
231
|
+
if (correlation != -1) correlations.push([correlation, i, styling])
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
let result
|
|
235
|
+
|
|
236
|
+
if (correlations.find(([c, _, __]) => c == 1)) {
|
|
237
|
+
result = stylingIntersect(correlations, stylings, current, id)
|
|
238
|
+
} else if (correlations.find(([c, _, __]) => c == 0)) {
|
|
239
|
+
result = stylingSplice(correlations, stylings, current, id)
|
|
240
|
+
} else if (
|
|
241
|
+
correlations.find(([c, _, __]) => c == 2) ||
|
|
242
|
+
correlations.length == 0
|
|
243
|
+
) {
|
|
244
|
+
result = [...stylings, current]
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
setText({
|
|
248
|
+
...text,
|
|
249
|
+
stylings: result,
|
|
250
|
+
})
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Get stylings encompassing an index within it's range
|
|
254
|
+
const getIntersectStylings = (index, startOffset = 0, finishOffset = 0) => {
|
|
255
|
+
// Find all stylings with encompassing range
|
|
256
|
+
let matches = text.stylings.filter(
|
|
257
|
+
({ start, finish }) =>
|
|
258
|
+
index >= start + startOffset && index < finish + finishOffset
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
return matches
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Get stylings after an index
|
|
265
|
+
const getSucceedStylings = (index) => {
|
|
266
|
+
// Find all stylings after the index
|
|
267
|
+
let matches = text.stylings.filter(({ start, finish }) => start >= index)
|
|
268
|
+
|
|
269
|
+
return matches
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const getStylingIndex = (styling) => {
|
|
273
|
+
return text.stylings.findIndex(
|
|
274
|
+
(item) =>
|
|
275
|
+
item.start == styling.start &&
|
|
276
|
+
item.finish == styling.finish &&
|
|
277
|
+
item.type == styling.type
|
|
278
|
+
)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const onChange = (value) => {
|
|
282
|
+
let difference = value.length - text.content.length
|
|
283
|
+
|
|
284
|
+
let selection = field.current.selectionStart - difference
|
|
285
|
+
|
|
286
|
+
let stylings = text.stylings.slice()
|
|
287
|
+
let succeeding = getSucceedStylings(selection)
|
|
288
|
+
let changes = []
|
|
289
|
+
|
|
290
|
+
for (let succeed of succeeding) {
|
|
291
|
+
let index = getStylingIndex(succeed)
|
|
292
|
+
let styling = stylings[index]
|
|
293
|
+
|
|
294
|
+
changes.push([
|
|
295
|
+
index,
|
|
296
|
+
styling.start + difference,
|
|
297
|
+
styling.finish + difference,
|
|
298
|
+
])
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
let intersecting = getIntersectStylings(selection, 1, 1)
|
|
302
|
+
|
|
303
|
+
for (let intersect of intersecting) {
|
|
304
|
+
let index = getStylingIndex(intersect)
|
|
305
|
+
let styling = stylings[index]
|
|
306
|
+
|
|
307
|
+
changes.push([index, styling.start, styling.finish + difference])
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
let spliced = []
|
|
311
|
+
for (let [index, start, finish] of changes) {
|
|
312
|
+
stylings[index] = {
|
|
313
|
+
...stylings[index],
|
|
314
|
+
start: start,
|
|
315
|
+
finish: finish,
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (start >= finish) spliced.push(index)
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
stylings = stylings.filter((_, index) => !spliced.includes(index))
|
|
322
|
+
|
|
323
|
+
setText({ ...text, content: value, stylings: stylings })
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const getContent = () => {
|
|
327
|
+
let content = text.content
|
|
328
|
+
|
|
329
|
+
// Get all styling indices
|
|
330
|
+
let indices = text.stylings
|
|
331
|
+
.map(({ start, finish }) => [start, finish])
|
|
332
|
+
.flat()
|
|
333
|
+
|
|
334
|
+
// Sort ascendingly
|
|
335
|
+
indices = indices.sort((a, b) => a - b)
|
|
336
|
+
|
|
337
|
+
// Remove duplicates
|
|
338
|
+
indices = indices.filter(
|
|
339
|
+
(element, index) => indices.indexOf(element) == index
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
// Add first index if not present
|
|
343
|
+
if (indices[0] != 0) indices.unshift(0)
|
|
344
|
+
|
|
345
|
+
// Add last index if not present
|
|
346
|
+
let last = text.content.length
|
|
347
|
+
if (indices[indices.length - 1] != last) indices.push(last)
|
|
348
|
+
|
|
349
|
+
let result = []
|
|
350
|
+
|
|
351
|
+
for (let i = 0; i < indices.length - 1; i++) {
|
|
352
|
+
result.push([indices[i], content.substring(indices[i], indices[i + 1])])
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return result
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const getFieldSelection = () => {
|
|
359
|
+
if (document.activeElement != field.current) return [0, 0]
|
|
360
|
+
|
|
361
|
+
let selection = window.getSelection()
|
|
362
|
+
let start
|
|
363
|
+
let end
|
|
364
|
+
|
|
365
|
+
if (selection.rangeCount) {
|
|
366
|
+
let range = selection.getRangeAt(0)
|
|
367
|
+
start = range.startOffset
|
|
368
|
+
end = range.endOffset
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return [start, end]
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return (
|
|
375
|
+
<div className="align-center box-border flex h-min w-[400px] flex-col items-center justify-center rounded bg-gray-300 shadow-md">
|
|
376
|
+
<div className={clsx("flex w-full flex-row justify-start p-2")}>
|
|
377
|
+
<Property
|
|
378
|
+
name="B"
|
|
379
|
+
onMouseDown={(event) => {
|
|
380
|
+
event.preventDefault() // This does not take focus away from field which allows the function to retrieve the current selection data
|
|
381
|
+
perform("bold")
|
|
382
|
+
}}
|
|
383
|
+
/>
|
|
384
|
+
<Property
|
|
385
|
+
name="I"
|
|
386
|
+
onClick={() => {
|
|
387
|
+
perform("italic")
|
|
388
|
+
}}
|
|
389
|
+
/>
|
|
390
|
+
<Property
|
|
391
|
+
name="U"
|
|
392
|
+
onClick={() => {
|
|
393
|
+
perform("under")
|
|
394
|
+
}}
|
|
395
|
+
/>
|
|
396
|
+
<Property
|
|
397
|
+
name="S"
|
|
398
|
+
onClick={() => {
|
|
399
|
+
perform("strike")
|
|
400
|
+
}}
|
|
401
|
+
/>
|
|
402
|
+
</div>
|
|
403
|
+
<div className="h-[1px] w-full bg-slate-600"> </div>
|
|
404
|
+
<div className="w-full">
|
|
405
|
+
<div
|
|
406
|
+
ref={field}
|
|
407
|
+
contentEditable="true"
|
|
408
|
+
dangerouslySetInnerHTML={{
|
|
409
|
+
__html: getContent()
|
|
410
|
+
.map((_data, index) => {
|
|
411
|
+
let [start, data] = _data
|
|
412
|
+
|
|
413
|
+
// Get stylings encompassing an index within it's range
|
|
414
|
+
let stylings = getIntersectStylings(start)
|
|
415
|
+
|
|
416
|
+
return `<span class="${stylings
|
|
417
|
+
.map((styling) => styleClasses[styling.type])
|
|
418
|
+
.join("")}">${data}</span>`
|
|
419
|
+
// return `<span
|
|
420
|
+
// class="${stylings
|
|
421
|
+
// .map((styling) => styleClasses[styling.type])
|
|
422
|
+
// .join(" ")}"
|
|
423
|
+
|
|
424
|
+
// >
|
|
425
|
+
// ${data}
|
|
426
|
+
// </span>`
|
|
427
|
+
})
|
|
428
|
+
.join(" "),
|
|
429
|
+
}}
|
|
430
|
+
className="h-[150px] w-full resize-none border-none p-2 outline-none"
|
|
431
|
+
onInput={(event) => {
|
|
432
|
+
let target: any = event.target
|
|
433
|
+
|
|
434
|
+
let [start, end] = getFieldSelection()
|
|
435
|
+
|
|
436
|
+
// console.log(target)
|
|
437
|
+
// console.log(target.selectionStart)
|
|
438
|
+
// console.log(target.selectionEnd)
|
|
439
|
+
|
|
440
|
+
setText({
|
|
441
|
+
...text,
|
|
442
|
+
content: target.textContent,
|
|
443
|
+
revert: [start, end],
|
|
444
|
+
})
|
|
445
|
+
}}
|
|
446
|
+
></div>
|
|
447
|
+
|
|
448
|
+
{/* <RichTextarea
|
|
449
|
+
ref={field}
|
|
450
|
+
value={text.content}
|
|
451
|
+
style={{ width: "100%" }} // tailwind w-full does not work
|
|
452
|
+
className="order-none h-[150px] resize-none p-2 outline-none"
|
|
453
|
+
onChange={(e) => {
|
|
454
|
+
onChange(e.target.value)
|
|
455
|
+
}}
|
|
456
|
+
>
|
|
457
|
+
{(value) => {
|
|
458
|
+
// Get all styling indices
|
|
459
|
+
let indices = text.stylings
|
|
460
|
+
.map(({ start, finish }) => [start, finish])
|
|
461
|
+
.flat()
|
|
462
|
+
|
|
463
|
+
// Sort ascendingly
|
|
464
|
+
indices = indices.sort((a, b) => a - b)
|
|
465
|
+
|
|
466
|
+
// Remove duplicates
|
|
467
|
+
indices = indices.filter(
|
|
468
|
+
(element, index) => indices.indexOf(element) == index
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
// Add first index if not present
|
|
472
|
+
if (indices[0] != 0) indices.unshift(0)
|
|
473
|
+
|
|
474
|
+
// Add last index if not present
|
|
475
|
+
let last = text.content.length
|
|
476
|
+
if (indices[indices.length - 1] != last) indices.push(last)
|
|
477
|
+
|
|
478
|
+
let result = []
|
|
479
|
+
|
|
480
|
+
for (let i = 0; i < indices.length - 1; i++) {
|
|
481
|
+
result.push([
|
|
482
|
+
indices[i],
|
|
483
|
+
value.substring(indices[i], indices[i + 1]),
|
|
484
|
+
])
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
return result.map((_data, index) => {
|
|
488
|
+
let [start, data] = _data
|
|
489
|
+
|
|
490
|
+
// Get stylings encompassing an index within it's range
|
|
491
|
+
let stylings = getIntersectStylings(start)
|
|
492
|
+
|
|
493
|
+
return (
|
|
494
|
+
<span
|
|
495
|
+
key={index}
|
|
496
|
+
className={`${stylings
|
|
497
|
+
.map((styling) => styleClasses[styling.type])
|
|
498
|
+
.join(" ")}
|
|
499
|
+
`}
|
|
500
|
+
>
|
|
501
|
+
{data}
|
|
502
|
+
</span>
|
|
503
|
+
)
|
|
504
|
+
})
|
|
505
|
+
}}
|
|
506
|
+
</RichTextarea> */}
|
|
507
|
+
</div>
|
|
508
|
+
<div className="h-[1px] w-full bg-slate-600"> </div>
|
|
509
|
+
<button className="my-1 rounded bg-cyan-800 p-2 py-1 text-white">
|
|
510
|
+
Submit
|
|
511
|
+
</button>
|
|
512
|
+
</div>
|
|
513
|
+
)
|
|
514
|
+
}
|