a tool for shared writing and social publishing
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

better focus persistence on undo/redo

+116 -21
+90 -20
components/Blocks/BlockCommands.tsx
··· 106 106 name: "Text", 107 107 icon: <ParagraphSmall />, 108 108 type: "text", 109 - onSelect: async (rep, props) => { 109 + onSelect: async (rep, props, um) => { 110 110 props.entityID && clearCommandSearchText(props.entityID); 111 111 let entity = await createBlockWithType(rep, props, "text"); 112 112 clearCommandSearchText(entity); 113 + um.add({ 114 + undo: () => { 115 + keepFocus(entity); 116 + }, 117 + redo: () => { 118 + keepFocus(entity); 119 + }, 120 + }); 121 + 113 122 keepFocus(entity); 114 123 }, 115 124 }, ··· 117 126 name: "Title", 118 127 icon: <Header1Small />, 119 128 type: "text", 120 - onSelect: async (rep, props) => { 121 - props.entityID && clearCommandSearchText(props.entityID); 129 + onSelect: async (rep, props, um) => { 122 130 let entity = await createBlockWithType(rep, props, "heading"); 123 131 await rep.mutate.assertFact({ 124 132 entity, 125 133 attribute: "block/heading-level", 126 134 data: { type: "number", value: 1 }, 127 135 }); 136 + clearCommandSearchText(entity); 137 + um.add({ 138 + undo: () => { 139 + keepFocus(entity); 140 + }, 141 + redo: () => { 142 + keepFocus(entity); 143 + }, 144 + }); 128 145 129 146 keepFocus(entity); 130 147 }, ··· 133 150 name: "Header", 134 151 icon: <Header2Small />, 135 152 type: "text", 136 - onSelect: async (rep, props) => { 137 - props.entityID && clearCommandSearchText(props.entityID); 153 + onSelect: async (rep, props, um) => { 138 154 let entity = await createBlockWithType(rep, props, "heading"); 139 - rep.mutate.assertFact({ 155 + await rep.mutate.assertFact({ 140 156 entity, 141 157 attribute: "block/heading-level", 142 158 data: { type: "number", value: 2 }, 143 159 }); 160 + um.add({ 161 + undo: () => { 162 + keepFocus(entity); 163 + }, 164 + redo: () => { 165 + keepFocus(entity); 166 + }, 167 + }); 144 168 clearCommandSearchText(entity); 145 169 keepFocus(entity); 146 170 }, ··· 149 173 name: "Subheader", 150 174 icon: <Header3Small />, 151 175 type: "text", 152 - onSelect: async (rep, props) => { 153 - props.entityID && clearCommandSearchText(props.entityID); 176 + onSelect: async (rep, props, um) => { 154 177 let entity = await createBlockWithType(rep, props, "heading"); 155 - rep.mutate.assertFact({ 178 + await rep.mutate.assertFact({ 156 179 entity, 157 180 attribute: "block/heading-level", 158 181 data: { type: "number", value: 3 }, 159 182 }); 183 + um.add({ 184 + undo: () => { 185 + keepFocus(entity); 186 + }, 187 + redo: () => { 188 + keepFocus(entity); 189 + }, 190 + }); 160 191 clearCommandSearchText(entity); 161 192 keepFocus(entity); 162 193 }, ··· 166 197 name: "External Link", 167 198 icon: <LinkSmall />, 168 199 type: "block", 169 - onSelect: async (rep, props) => { 170 - createBlockWithType(rep, props, "link"); 200 + onSelect: async (rep, props, um) => { 201 + props.entityID && clearCommandSearchText(props.entityID); 202 + await createBlockWithType(rep, props, "link"); 203 + um.add({ 204 + undo: () => { 205 + props.entityID && keepFocus(props.entityID); 206 + }, 207 + redo: () => {}, 208 + }); 171 209 }, 172 210 }, 173 211 { 174 212 name: "Embed Website", 175 213 icon: <BlockEmbedSmall />, 176 214 type: "block", 177 - onSelect: async (rep, props) => { 178 - createBlockWithType(rep, props, "embed"); 215 + onSelect: async (rep, props, um) => { 216 + props.entityID && clearCommandSearchText(props.entityID); 217 + await createBlockWithType(rep, props, "embed"); 218 + um.add({ 219 + undo: () => { 220 + props.entityID && keepFocus(props.entityID); 221 + }, 222 + redo: () => {}, 223 + }); 179 224 }, 180 225 }, 181 226 { 182 227 name: "Image", 183 228 icon: <BlockImageSmall />, 184 229 type: "block", 185 - onSelect: async (rep, props) => { 230 + onSelect: async (rep, props, um) => { 231 + props.entityID && clearCommandSearchText(props.entityID); 186 232 let entity = await createBlockWithType(rep, props, "image"); 187 233 setTimeout(() => { 188 234 let el = document.getElementById(elementId.block(entity).input); 189 235 el?.focus(); 190 236 }, 100); 237 + um.add({ 238 + undo: () => { 239 + keepFocus(entity); 240 + }, 241 + redo: () => { 242 + let el = document.getElementById(elementId.block(entity).input); 243 + el?.focus(); 244 + }, 245 + }); 191 246 }, 192 247 }, 193 248 { 194 249 name: "Button", 195 250 icon: <BlockButtonSmall />, 196 251 type: "block", 197 - onSelect: async (rep, props) => { 198 - createBlockWithType(rep, props, "button"); 252 + onSelect: async (rep, props, um) => { 253 + props.entityID && clearCommandSearchText(props.entityID); 254 + await createBlockWithType(rep, props, "button"); 255 + um.add({ 256 + undo: () => { 257 + props.entityID && keepFocus(props.entityID); 258 + }, 259 + redo: () => {}, 260 + }); 199 261 }, 200 262 }, 201 263 { ··· 203 265 icon: <BlockMailboxSmall />, 204 266 type: "block", 205 267 onSelect: async (rep, props) => { 206 - let entity; 207 - createBlockWithType(rep, props, "mailbox"); 268 + props.entityID && clearCommandSearchText(props.entityID); 269 + await createBlockWithType(rep, props, "mailbox"); 208 270 }, 209 271 }, 210 272 ··· 214 276 name: "RSVP", 215 277 icon: <RSVPSmall />, 216 278 type: "event", 217 - onSelect: (rep, props) => createBlockWithType(rep, props, "rsvp"), 279 + onSelect: (rep, props) => { 280 + props.entityID && clearCommandSearchText(props.entityID); 281 + return createBlockWithType(rep, props, "rsvp"); 282 + }, 218 283 }, 219 284 { 220 285 name: "Date and Time", 221 286 icon: <BlockCalendarSmall />, 222 287 type: "event", 223 - onSelect: (rep, props) => createBlockWithType(rep, props, "datetime"), 288 + onSelect: (rep, props) => { 289 + props.entityID && clearCommandSearchText(props.entityID); 290 + return createBlockWithType(rep, props, "datetime"); 291 + }, 224 292 }, 225 293 226 294 // PAGE TYPES ··· 230 298 icon: <BlockDocPageSmall />, 231 299 type: "page", 232 300 onSelect: async (rep, props, um) => { 301 + props.entityID && clearCommandSearchText(props.entityID); 233 302 let entity = await createBlockWithType(rep, props, "card"); 234 303 235 304 let newPage = v7(); ··· 268 337 icon: <BlockCanvasPageSmall />, 269 338 type: "page", 270 339 onSelect: async (rep, props, um) => { 340 + props.entityID && clearCommandSearchText(props.entityID); 271 341 let entity = await createBlockWithType(rep, props, "card"); 272 342 273 343 let newPage = v7();
+4
components/Buttons.tsx
··· 6 6 CardThemeProvider, 7 7 NestedCardThemeProvider, 8 8 } from "./ThemeManager/ThemeProvider"; 9 + import { useReplicache } from "src/replicache"; 9 10 10 11 type ButtonProps = Omit<JSX.IntrinsicElements["button"], "content">; 11 12 export const ButtonPrimary = forwardRef< ··· 153 154 open?: boolean; 154 155 delayDuration?: number; 155 156 }) => { 157 + let { undoManager } = useReplicache(); 156 158 return ( 157 159 // toolbar button does not control the highlight theme setter 158 160 // if toolbar button is updated, be sure to update there as well ··· 165 167 className={props.className} 166 168 onMouseDown={(e) => { 167 169 e.preventDefault(); 170 + undoManager.startGroup(); 168 171 props.onMouseDown && props.onMouseDown(e); 172 + undoManager.endGroup(); 169 173 }} 170 174 > 171 175 {props.children}
+22 -1
components/Toolbar/TextBlockTypeToolbar.tsx
··· 170 170 }; 171 171 172 172 export function keepFocus(entityID: string) { 173 - setTimeout(() => {}, 1000); 173 + let existingEditor = useEditorStates.getState().editorStates[entityID]; 174 + 175 + let selection = existingEditor?.editor.selection; 176 + 177 + setTimeout(() => { 178 + let existingEditor = useEditorStates.getState().editorStates[entityID]; 179 + 180 + if (!existingEditor) return; 181 + 182 + existingEditor.view?.focus(); 183 + 184 + setEditorState(entityID, { 185 + editor: existingEditor.editor.apply( 186 + existingEditor.editor.tr.setSelection( 187 + TextSelection.create( 188 + existingEditor.editor.doc, 189 + selection?.anchor || 1, 190 + ), 191 + ), 192 + ), 193 + }); 194 + }, 50); 174 195 } 175 196 176 197 export function TextBlockTypeButton(props: {