tangled
alpha
login
or
join now
leaflet.pub
/
leaflet
289
fork
atom
a tool for shared writing and social publishing
289
fork
atom
overview
issues
28
pulls
pipelines
initial delete button but no wiring yet
cozylittle.house
2 months ago
264cd2c5
10f4e467
+106
-83
3 changed files
expand all
collapse all
unified
split
components
Blocks
Block.tsx
CodeBlock.tsx
ImageBlock.tsx
+50
-30
components/Blocks/Block.tsx
···
33
33
import { deepEquals } from "src/utils/deepEquals";
34
34
import { isTextBlock } from "src/utils/isTextBlock";
35
35
import { focusPage } from "src/utils/focusPage";
36
36
+
import { DeleteTiny } from "components/Icons/DeleteTiny";
36
37
37
38
export type Block = {
38
39
factID: string;
···
85
86
let selected = useUIState(
86
87
(s) => !!s.selectedBlocks.find((b) => b.value === props.entityID),
87
88
);
89
89
+
let alignment = useEntity(props.value, "block/text-alignment")?.data.value;
90
90
+
91
91
+
let alignmentStyle =
92
92
+
props.type === "button" || props.type === "image"
93
93
+
? "justify-center"
94
94
+
: "justify-start";
95
95
+
96
96
+
if (alignment)
97
97
+
alignmentStyle = {
98
98
+
left: "justify-start",
99
99
+
right: "justify-end",
100
100
+
center: "justify-center",
101
101
+
justify: "justify-start",
102
102
+
}[alignment];
88
103
89
104
let [areYouSure, setAreYouSure] = useState(false);
90
105
useEffect(() => {
···
120
135
blockWrapper relative
121
136
flex flex-row gap-2
122
137
px-3 sm:px-4
138
138
+
z-1 w-full
139
139
+
${alignmentStyle}
123
140
${
124
141
!props.nextBlock
125
142
? "pb-3 sm:pb-4"
···
258
275
) => {
259
276
// BaseBlock renders the actual block content, delete states, controls spacing between block and list markers
260
277
let BlockTypeComponent = BlockTypeComponents[props.type];
261
261
-
let alignment = useEntity(props.value, "block/text-alignment")?.data.value;
262
262
-
263
263
-
let alignmentStyle =
264
264
-
props.type === "button" || props.type === "image"
265
265
-
? "justify-center"
266
266
-
: "justify-start";
267
267
-
268
268
-
if (alignment)
269
269
-
alignmentStyle = {
270
270
-
left: "justify-start",
271
271
-
right: "justify-end",
272
272
-
center: "justify-center",
273
273
-
justify: "justify-start",
274
274
-
}[alignment];
275
278
276
279
if (!BlockTypeComponent) return <div>unknown block</div>;
277
280
return (
278
278
-
<div
279
279
-
className={`blockContentWrapper w-full grow flex gap-2 z-1 ${alignmentStyle}`}
280
280
-
>
281
281
+
<>
281
282
{props.listData && <ListMarker {...props} />}
282
283
{props.areYouSure ? (
283
284
<AreYouSure
···
290
291
) : (
291
292
<BlockTypeComponent {...props} preview={props.preview} />
292
293
)}
293
293
-
</div>
294
294
+
</>
294
295
);
295
296
};
296
297
···
393
394
className?: string;
394
395
hasBackground?: "accent" | "page";
395
396
borderOnHover?: boolean;
397
397
+
hasAlignment?: boolean;
396
398
}) => {
399
399
+
// this is used to wrap non-text blocks in consistent selected styling, spacing, and top level options like delete
397
400
return (
398
398
-
<div
399
399
-
className={`block ${props.className} p-2 sm:p-3 w-full overflow-hidden
401
401
+
<div className={`relative ${props.hasAlignment ? "w-fit" : "w-full"}`}>
402
402
+
<div
403
403
+
className={`nonTextBlock ${props.className} p-2 sm:p-3 overflow-hidden
404
404
+
${props.hasAlignment ? "w-fit" : "w-full"}
400
405
${props.isSelected ? "block-border-selected " : "block-border"}
401
406
${props.borderOnHover && "hover:border-accent-contrast! hover:outline-accent-contrast! focus-within:border-accent-contrast! focus-within:outline-accent-contrast!"}`}
402
402
-
style={{
403
403
-
backgroundColor:
404
404
-
props.hasBackground === "accent"
405
405
-
? "var(--accent-light)"
406
406
-
: props.hasBackground === "page"
407
407
-
? "rgb(var(--bg-page))"
408
408
-
: "transparent",
409
409
-
}}
410
410
-
>
411
411
-
{props.children}
407
407
+
style={{
408
408
+
backgroundColor:
409
409
+
props.hasBackground === "accent"
410
410
+
? "var(--accent-light)"
411
411
+
: props.hasBackground === "page"
412
412
+
? "rgb(var(--bg-page))"
413
413
+
: "transparent",
414
414
+
}}
415
415
+
>
416
416
+
{props.children}
417
417
+
</div>
418
418
+
{props.isSelected && <DeleteBlock />}
419
419
+
</div>
420
420
+
);
421
421
+
};
422
422
+
423
423
+
const DeleteBlock = () => {
424
424
+
return (
425
425
+
<div className="absolute -top-4 right-2">
426
426
+
<button
427
427
+
className="bg-border border-2 border-bg-page rounded-full p-1 text-bg-page"
428
428
+
onClick={() => {}}
429
429
+
>
430
430
+
<DeleteTiny />
431
431
+
</button>
412
432
</div>
413
433
);
414
434
};
+51
-52
components/Blocks/CodeBlock.tsx
···
68
68
}, []);
69
69
return (
70
70
<div className="codeBlock w-full flex flex-col rounded-md gap-0.5 ">
71
71
+
<BlockLayout
72
72
+
isSelected={focusedBlock}
73
73
+
hasBackground="accent"
74
74
+
borderOnHover
75
75
+
className="p-0! min-h-[48px]"
76
76
+
>
77
77
+
{focusedBlock && permissions.write ? (
78
78
+
<BaseTextareaBlock
79
79
+
placeholder="write some code…"
80
80
+
data-editable-block
81
81
+
data-entityid={props.entityID}
82
82
+
id={elementId.block(props.entityID).input}
83
83
+
block={props}
84
84
+
rep={rep}
85
85
+
permissionSet={entity_set.set}
86
86
+
spellCheck={false}
87
87
+
autoCapitalize="none"
88
88
+
autoCorrect="off"
89
89
+
className="codeBlockEditor whitespace-nowrap! overflow-auto! font-mono p-2 sm:p-3"
90
90
+
value={content?.data.value}
91
91
+
onChange={async (e) => {
92
92
+
// Update the entity with the new value
93
93
+
await rep?.mutate.assertFact({
94
94
+
attribute: "block/code",
95
95
+
entity: props.entityID,
96
96
+
data: { type: "string", value: e.target.value },
97
97
+
});
98
98
+
}}
99
99
+
/>
100
100
+
) : !html ? (
101
101
+
<pre
102
102
+
onClick={onClick}
103
103
+
onMouseDown={(e) => e.stopPropagation()}
104
104
+
className="codeBlockRendered overflow-auto! font-mono p-2 sm:p-3 w-full h-full"
105
105
+
>
106
106
+
{content?.data.value === "" || content?.data.value === undefined ? (
107
107
+
<div className="text-tertiary italic">write some code…</div>
108
108
+
) : (
109
109
+
content?.data.value
110
110
+
)}
111
111
+
</pre>
112
112
+
) : (
113
113
+
<div
114
114
+
onMouseDown={(e) => e.stopPropagation()}
115
115
+
onClick={onClick}
116
116
+
data-lang={lang}
117
117
+
className="contents"
118
118
+
dangerouslySetInnerHTML={{ __html: html || "" }}
119
119
+
/>
120
120
+
)}
121
121
+
</BlockLayout>
71
122
{permissions.write && (
72
123
<div className="text-sm text-tertiary flex justify-between">
73
124
<div className="flex gap-1">
···
119
170
</select>
120
171
</div>
121
172
)}
122
122
-
123
123
-
<BlockLayout
124
124
-
isSelected={focusedBlock}
125
125
-
hasBackground="accent"
126
126
-
borderOnHover
127
127
-
className="p-0! min-h-[48px]"
128
128
-
>
129
129
-
{focusedBlock && permissions.write ? (
130
130
-
<BaseTextareaBlock
131
131
-
placeholder="write some code…"
132
132
-
data-editable-block
133
133
-
data-entityid={props.entityID}
134
134
-
id={elementId.block(props.entityID).input}
135
135
-
block={props}
136
136
-
rep={rep}
137
137
-
permissionSet={entity_set.set}
138
138
-
spellCheck={false}
139
139
-
autoCapitalize="none"
140
140
-
autoCorrect="off"
141
141
-
className="codeBlockEditor whitespace-nowrap! overflow-auto! font-mono p-2 sm:p-3"
142
142
-
value={content?.data.value}
143
143
-
onChange={async (e) => {
144
144
-
// Update the entity with the new value
145
145
-
await rep?.mutate.assertFact({
146
146
-
attribute: "block/code",
147
147
-
entity: props.entityID,
148
148
-
data: { type: "string", value: e.target.value },
149
149
-
});
150
150
-
}}
151
151
-
/>
152
152
-
) : !html ? (
153
153
-
<pre
154
154
-
onClick={onClick}
155
155
-
onMouseDown={(e) => e.stopPropagation()}
156
156
-
className="codeBlockRendered overflow-auto! font-mono p-2 sm:p-3 w-full h-full"
157
157
-
>
158
158
-
{content?.data.value === "" || content?.data.value === undefined ? (
159
159
-
<div className="text-tertiary italic">write some code…</div>
160
160
-
) : (
161
161
-
content?.data.value
162
162
-
)}
163
163
-
</pre>
164
164
-
) : (
165
165
-
<div
166
166
-
onMouseDown={(e) => e.stopPropagation()}
167
167
-
onClick={onClick}
168
168
-
data-lang={lang}
169
169
-
className="contents"
170
170
-
dangerouslySetInnerHTML={{ __html: html || "" }}
171
171
-
/>
172
172
-
)}
173
173
-
</BlockLayout>
174
173
</div>
175
174
);
176
175
}
+5
-1
components/Blocks/ImageBlock.tsx
···
150
150
`;
151
151
152
152
return (
153
153
-
<BlockLayout isSelected={!!isSelected} className={blockClassName}>
153
153
+
<BlockLayout
154
154
+
hasAlignment
155
155
+
isSelected={!!isSelected}
156
156
+
className={blockClassName}
157
157
+
>
154
158
{isLocalUpload || image.data.local ? (
155
159
<img
156
160
loading="lazy"