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
27
pulls
pipelines
some styling updates
cozylittle.house
6 days ago
ce10ee81
4a8480f3
+72
-45
6 changed files
expand all
collapse all
unified
split
app
globals.css
components
Blocks
index.tsx
Footnotes
FootnoteEditor.tsx
FootnoteItemLayout.tsx
Toolbar
FootnoteButton.tsx
FootnoteTextToolbar.tsx
+5
-3
app/globals.css
···
496
496
.footnote-ref {
497
497
counter-increment: footnote;
498
498
cursor: pointer;
499
499
-
color: rgb(var(--accent-contrast));
499
499
+
color: rgb(var(--color-tertiary));
500
500
opacity: 0.7;
501
501
}
502
502
.footnote-ref::after {
503
503
content: counter(footnote);
504
504
vertical-align: super;
505
505
+
padding-left: 2px;
505
506
font-size: 75%;
507
507
+
line-height: 0rem;
506
508
}
507
509
.footnote-ref ~ br.ProseMirror-trailingBreak {
508
508
-
display: inline;
509
509
-
width: 4px;
510
510
+
display: inline;
511
511
+
width: 4px;
510
512
}
511
513
.footnote-ref ~ img.ProseMirror-separator {
512
514
display: none;
+16
-3
components/Blocks/index.tsx
···
14
14
import { v7 } from "uuid";
15
15
16
16
import { Block } from "./Block";
17
17
-
import { useEffect } from "react";
17
17
+
import { useEffect, useState } from "react";
18
18
import { addShortcut } from "src/shortcuts";
19
19
import { useHandleDrop } from "./useHandleDrop";
20
20
+
import { useFootnoteContext } from "components/Footnotes/FootnoteContext";
20
21
21
22
export function Blocks(props: { entityID: string }) {
22
23
let rep = useReplicache();
···
93
94
),
94
95
);
95
96
97
97
+
let { footnotes } = useFootnoteContext();
98
98
+
99
99
+
let [areFootnotes, setAreFootnotes] = useState(false);
100
100
+
101
101
+
useEffect(() => {
102
102
+
setAreFootnotes(footnotes.length > 0);
103
103
+
}, [footnotes.length]);
104
104
+
96
105
return (
97
106
<div
98
98
-
className={`blocks w-full flex flex-col outline-hidden h-fit min-h-full`}
107
107
+
className={`blocks w-full flex flex-col outline-hidden ${areFootnotes ? "h-fit" : "min-h-full"}`}
99
108
onClick={async (e) => {
100
109
if (!permissions.write) return;
101
110
if (useUIState.getState().selectedBlocks.length > 1) return;
···
167
176
lastVisibleBlock={lastVisibleBlock || undefined}
168
177
lastRootBlock={lastRootBlock || undefined}
169
178
entityID={props.entityID}
179
179
+
areFootnotes={areFootnotes}
170
180
/>
171
181
</div>
172
182
);
···
226
236
lastRootBlock: Block | undefined;
227
237
lastVisibleBlock: Block | undefined;
228
238
entityID: string;
239
239
+
areFootnotes: boolean;
229
240
}) => {
230
241
let { rep } = useReplicache();
231
242
let entity_set = useEntitySetContext();
···
236
247
});
237
248
238
249
if (!entity_set.permissions.write) return;
250
250
+
if (props.areFootnotes) return;
251
251
+
239
252
return (
240
253
<div
241
241
-
className="blockListClickableBottomArea shrink-0 h-[50vh]"
254
254
+
className="blockListClickableBottomArea grow shrink-0 h-[50vh]"
242
255
onClick={() => {
243
256
let newEntityID = v7();
244
257
if (
+35
-16
components/Footnotes/FootnoteEditor.tsx
···
12
12
useYJSValue,
13
13
trackUndoRedo,
14
14
} from "components/Blocks/TextBlock/mountProsemirror";
15
15
-
import { CloseTiny } from "components/Icons/CloseTiny";
15
15
+
import { DeleteTiny } from "components/Icons/DeleteTiny";
16
16
import { FootnoteItemLayout } from "./FootnoteItemLayout";
17
17
import { useEditorStates } from "src/state/useEditorState";
18
18
import { useUIState } from "src/useUIState";
···
46
46
"Shift-Enter": (state, dispatch) => {
47
47
let hardBreak = schema.nodes.hard_break.create();
48
48
if (dispatch) {
49
49
-
dispatch(
50
50
-
state.tr.replaceSelectionWith(hardBreak).scrollIntoView(),
51
51
-
);
49
49
+
dispatch(state.tr.replaceSelectionWith(hardBreak).scrollIntoView());
52
50
}
53
51
return true;
54
52
},
···
179
177
return { editorStates: rest };
180
178
});
181
179
};
182
182
-
}, [props.footnoteEntityID, value, props.editable, props.autoFocus, rep.undoManager, pageID]);
180
180
+
}, [
181
181
+
props.footnoteEntityID,
182
182
+
value,
183
183
+
props.editable,
184
184
+
props.autoFocus,
185
185
+
rep.undoManager,
186
186
+
pageID,
187
187
+
]);
183
188
184
189
return (
185
190
<FootnoteItemLayout
···
194
199
}}
195
200
trailing={
196
201
props.editable && props.onDelete ? (
197
197
-
<button
198
198
-
className="shrink-0 mt-0.5 text-tertiary hover:text-primary opacity-0 group-hover/footnote:opacity-100 transition-opacity"
199
199
-
onClick={props.onDelete}
200
200
-
title="Delete footnote"
201
201
-
>
202
202
-
<CloseTiny />
203
203
-
</button>
202
202
+
<FootnoteDeleteButton
203
203
+
footnoteEntityID={props.footnoteEntityID}
204
204
+
onDelete={props.onDelete}
205
205
+
/>
204
206
) : undefined
205
207
}
206
208
>
207
207
-
<div
208
208
-
ref={mountRef}
209
209
-
className="outline-hidden"
210
210
-
/>
209
209
+
<div ref={mountRef} className="outline-hidden" />
211
210
</FootnoteItemLayout>
212
211
);
213
212
}
214
213
214
214
+
function FootnoteDeleteButton(props: {
215
215
+
footnoteEntityID: string;
216
216
+
onDelete: () => void;
217
217
+
}) {
218
218
+
let isActive = useUIState(
219
219
+
(s) =>
220
220
+
s.focusedEntity?.entityType === "footnote" &&
221
221
+
s.focusedEntity.entityID === props.footnoteEntityID,
222
222
+
);
223
223
+
224
224
+
return (
225
225
+
<button
226
226
+
className={`shrink-0 mt-0.5 text-tertiary hover:text-accent-contrast transition-opacity ${isActive ? "opacity-100" : "opacity-0"}`}
227
227
+
onClick={props.onDelete}
228
228
+
title="Delete footnote"
229
229
+
>
230
230
+
<DeleteTiny />
231
231
+
</button>
232
232
+
);
233
233
+
}
+4
-2
components/Footnotes/FootnoteItemLayout.tsx
···
10
10
className?: string;
11
11
}) {
12
12
let indexClassName =
13
13
-
"text-accent-contrast font-medium shrink-0 text-xs leading-normal no-underline hover:underline cursor-pointer";
13
13
+
"text-tertiary font-medium shrink-0 text-xs leading-normal no-underline hover:underline cursor-pointer";
14
14
15
15
let indexContent = <>{props.index}.</>;
16
16
···
48
48
className?: string;
49
49
}) {
50
50
return (
51
51
-
<div className={`footnote-section px-3 sm:px-4 pb-2 ${props.className ?? ""}`}>
51
51
+
<div
52
52
+
className={`footnote-section px-3 sm:px-4 pb-2 ${props.className ?? ""}`}
53
53
+
>
52
54
<hr className="border-border-light mb-3" />
53
55
<div className="flex flex-col gap-2">{props.children}</div>
54
56
</div>
+11
-21
components/Toolbar/FootnoteButton.tsx
···
5
5
import { insertFootnote } from "components/Blocks/TextBlock/insertFootnote";
6
6
import { TooltipButton } from "components/Buttons";
7
7
import { Props } from "components/Icons/Props";
8
8
+
import { ToolbarButton } from ".";
8
9
9
10
export function FootnoteButton() {
10
11
let rep = useReplicache();
···
12
13
let focusedBlock = useUIState((s) => s.focusedEntity);
13
14
14
15
return (
15
15
-
<TooltipButton
16
16
-
tooltipContent={<div className="text-center">Footnote</div>}
17
17
-
onMouseDown={async (e) => {
16
16
+
<ToolbarButton
17
17
+
tooltipContent={"Insert Footnote"}
18
18
+
onClick={async (e) => {
18
19
e.preventDefault();
19
20
if (!focusedBlock || focusedBlock.entityType !== "block") return;
20
21
let editorState =
···
29
30
}}
30
31
>
31
32
<FootnoteIcon />
32
32
-
</TooltipButton>
33
33
+
</ToolbarButton>
33
34
);
34
35
}
35
36
···
43
44
xmlns="http://www.w3.org/2000/svg"
44
45
{...props}
45
46
>
46
46
-
<text
47
47
-
x="6"
48
48
-
y="18"
49
49
-
fontSize="14"
50
50
-
fontWeight="bold"
47
47
+
<path
48
48
+
d="M21.3926 4.21564C21.503 4.21564 21.5926 4.30518 21.5926 4.41564V10.0352C21.5926 10.1456 21.503 10.2352 21.3926 10.2352H20.1584C20.0479 10.2352 19.9584 10.1456 19.9584 10.0352V5.74816C19.9584 5.73289 19.946 5.72052 19.9307 5.72052C19.9257 5.72052 19.9208 5.72187 19.9165 5.72444L18.909 6.32717C18.7757 6.40692 18.6063 6.31088 18.6063 6.15553V5.23309C18.6063 5.16341 18.6426 5.09876 18.702 5.06243L20.0397 4.24498C20.0711 4.22579 20.1072 4.21564 20.144 4.21564H21.3926Z"
51
49
fill="currentColor"
52
52
-
fontFamily="serif"
53
53
-
>
54
54
-
f
55
55
-
</text>
56
56
-
<text
57
57
-
x="14"
58
58
-
y="12"
59
59
-
fontSize="9"
50
50
+
/>
51
51
+
<path
52
52
+
d="M13.6152 5C13.8361 5 14.0156 5.17948 14.0156 5.40039V10.4297C14.254 10.2111 14.5346 9.9596 15.1074 9.66016C15.6803 9.34771 16.1974 9.19924 16.9785 9.19922C17.7797 9.19927 18.356 9.39478 18.958 9.75879V10.0352C18.9581 10.6977 19.4957 11.2352 20.1582 11.2354H20.417C20.4857 11.3518 20.5525 11.4718 20.6133 11.5977C20.9909 12.353 21.1797 13.2326 21.1797 14.2354C21.1797 15.2379 21.1195 16.0091 20.6455 16.9775C20.2754 17.7337 19.8632 18.1974 19.2607 18.6299C18.766 18.9849 17.9146 19.2011 17.0684 19.2012C16.3263 19.2012 15.7474 19.0709 15.2266 18.8105C14.7059 18.5502 14.2825 18.231 13.957 17.8535V18.6299C13.957 18.8478 13.7823 19.026 13.5645 19.0303L12.2158 19.0566C11.992 19.0609 11.8076 18.8802 11.8076 18.6562V5.40039C11.8076 5.17954 11.9872 5.0001 12.208 5H13.6152ZM7.05469 9.16113C8.33062 9.1612 9.28775 9.48019 9.92578 10.1182C10.5637 10.7562 10.8828 11.57 10.8828 12.5596V18.6641C10.8828 18.885 10.7033 19.0645 10.4824 19.0645H9.00586C8.77508 19.0642 8.59172 18.869 8.60645 18.6387L8.65625 17.873C8.34379 18.2896 7.97256 18.6087 7.54297 18.8301C7.11324 19.0514 6.5394 19.1621 5.82324 19.1621C5.14633 19.162 4.56014 19.0514 4.06543 18.8301C3.57077 18.5957 3.18651 18.2766 2.91309 17.873C2.65267 17.4563 2.52246 16.9737 2.52246 16.4268C2.52256 15.6066 2.82203 14.9228 3.4209 14.376C4.03285 13.8161 4.92498 13.4711 6.09668 13.3408L8.65625 13.0674V12.3057C8.65625 11.9932 8.5194 11.7132 8.24609 11.4658C7.97268 11.2055 7.53625 11.0753 6.9375 11.0752C6.41668 11.0752 5.95996 11.2054 5.56934 11.4658C5.30375 11.64 5.0992 11.8786 4.95605 12.1816C4.85494 12.3957 4.61483 12.5278 4.39258 12.4463L3.24316 12.0234C3.04644 11.9511 2.9365 11.7382 3.0127 11.543C3.28354 10.8507 3.73231 10.2976 4.3584 9.88379C5.10064 9.40199 5.99995 9.16116 7.05469 9.16113ZM6.31152 15.0596C5.81698 15.1247 5.44557 15.268 5.19824 15.4893C4.95102 15.6975 4.82725 15.9647 4.82715 16.29C4.82715 16.5895 4.94437 16.8509 5.17871 17.0723C5.42601 17.2805 5.75841 17.3847 6.1748 17.3848C6.70866 17.3848 7.15883 17.2871 7.52344 17.0918C7.90102 16.8965 8.18098 16.6155 8.36328 16.251C8.55847 15.8864 8.65625 15.437 8.65625 14.9033C8.65611 14.8309 8.59244 14.7752 8.52051 14.7842L6.31152 15.0596ZM16.6758 11.1533C15.9644 11.1533 15.6758 11.2705 15.2852 11.5049C14.9076 11.7393 14.6016 12.0844 14.3672 12.54C14.1328 12.9827 14.0156 13.5297 14.0156 14.1807C14.0156 14.8317 14.1329 15.3851 14.3672 15.8408C14.6016 16.2835 14.9076 16.623 15.2852 16.8574C15.6756 17.0917 15.9645 17.209 16.6758 17.209C17.3871 17.2089 17.8897 16.9826 18.3584 16.4229C18.8401 15.863 19.081 15.1337 19.0811 14.2354C19.0811 13.3368 18.8402 12.6068 18.3584 12.0469C17.8897 11.4871 17.387 11.1535 16.6758 11.1533Z"
60
53
fill="currentColor"
61
61
-
fontFamily="sans-serif"
62
62
-
>
63
63
-
n
64
64
-
</text>
54
54
+
/>
65
55
</svg>
66
56
);
67
57
}
+1
components/Toolbar/FootnoteTextToolbar.tsx
···
25
25
mark={schema.marks.strong}
26
26
icon={<BoldSmall />}
27
27
/>
28
28
+
28
29
<TextDecorationButton
29
30
tooltipContent={
30
31
<div className="flex flex-col gap-1 justify-center">