|
2 | 2 | import { onMount, onDestroy, type Snippet } from 'svelte'; |
3 | 3 | import { BROWSER } from 'esm-env'; |
4 | 4 | import { page } from '$app/state'; |
5 | | - import { goto, pushState } from '$app/navigation'; |
| 5 | + import { pushState, beforeNavigate } from '$app/navigation'; |
6 | 6 | import CodeMirror, { |
7 | 7 | type ChangeCodeMirrorEvent, |
8 | 8 | type SelectCodeMirrorEvent, |
|
37 | 37 | getCellId: (rowIndex: number, cellIndex: number) => string | undefined; |
38 | 38 | isFocusedCell: (rowIndex: number, cellIndex: number) => boolean; |
39 | 39 | isFocusedRow: (rowIndex: number) => boolean; |
40 | | - gotoAfterCollapse: (url: string | URL) => void; |
41 | 40 | }; |
42 | 41 |
|
43 | 42 | interface Props { |
|
72 | 71 | onclickSubmit: (event: MouseEvent) => void; |
73 | 72 | onclickClear: (event: MouseEvent) => void; |
74 | 73 | onclickClose: (event: MouseEvent) => void; |
75 | | - gotoAfterCollapse?: (url: string | URL) => void; |
76 | 74 | } |
77 | 75 | ] |
78 | 76 | >; |
|
84 | 82 | getCellId: (cellIndex: number) => string; |
85 | 83 | isFocusedCell: (cellIndex: number) => boolean; |
86 | 84 | rowIndex: number; |
87 | | - gotoAfterCollapse?: (url: string | URL) => void; |
88 | 85 | } |
89 | 86 | ] |
90 | 87 | >; |
|
157 | 154 | let expanded = $state(false); |
158 | 155 | let activeRowIndex: number = $state(0); |
159 | 156 | let activeColIndex: number = $state(-1); |
160 | | - let gotoAfterCollapseUrl: string | URL | undefined = $state(); |
| 157 | + let interceptedLinkElement: HTMLLinkElement | undefined = $state(undefined); |
| 158 | + let submitLinkElement: HTMLAnchorElement | undefined = $state(undefined); |
| 159 | +
|
161 | 160 | let prevValue: string = value; |
162 | 161 |
|
163 | 162 | let allowArrowKeyCursorHandling: { vertical: boolean; horizontal: boolean } = $state({ |
|
195 | 194 | } |
196 | 195 | }); |
197 | 196 |
|
| 197 | + beforeNavigate((navigation) => { |
| 198 | + if (navigation.to) { |
| 199 | + hideExpandedSearch(); |
| 200 | + } |
| 201 | + }); |
| 202 | +
|
198 | 203 | const extensionsWithDefaults = $derived([ |
199 | 204 | keymap.of(standardKeymap), // Needed for atomic ranges to work. Maybe we can use a subset? |
200 | 205 | preventEnterKeyHandling(), |
|
303 | 308 | } |
304 | 309 |
|
305 | 310 | function handleChangeCodeMirror(event: ChangeCodeMirrorEvent) { |
306 | | - if (!dialog?.open && value !== event.value) { |
| 311 | + if (!dialog?.open && value.trim() !== event.value.trim()) { |
307 | 312 | showExpandedSearch(); |
308 | 313 | } |
309 | 314 | value = event.value; |
|
370 | 375 | }); |
371 | 376 | } |
372 | 377 |
|
373 | | - export async function submit(form: HTMLFormElement) { |
374 | | - hideExpandedSearch(); |
375 | | - const formData = new FormData(form); |
376 | | - const formParams = new URLSearchParams(formData as unknown as Record<string, string>); |
377 | | - const actionUrl = new URL(form.action); |
378 | | - gotoAfterCollapse(`${actionUrl.href}?${formParams.toString()}`); |
379 | | - } |
380 | | -
|
381 | | - async function gotoAfterCollapse(url: string | URL) { |
382 | | - gotoAfterCollapseUrl = url; |
383 | | - history.back(); |
384 | | - } |
385 | | -
|
386 | 378 | async function handlePopState(event: PopStateEvent) { |
| 379 | + interceptedLinkElement?.click(); |
387 | 380 | if (dialog?.open) { |
388 | 381 | hideExpandedSearch(); |
389 | | - } |
390 | | - if (gotoAfterCollapseUrl) { |
391 | | - const url = gotoAfterCollapseUrl; |
392 | | - gotoAfterCollapseUrl = undefined; |
393 | | - await goto(url); |
394 | | - collapsedEditorView?.focus(); |
395 | | - } else if ( |
396 | | - page.state.expandedSuperSearch && |
397 | | - event.state['sveltekit:states']?.expandedSuperSearch // a little bit hacky way to ensure the dialog doesn't flicker when navigating forward |
398 | | - ) { |
399 | | - showExpandedSearch({ focusRow: 0, preventPushState: true }); |
| 382 | + } else if (event.state['sveltekit:states']?.expandedSuperSearch) { |
| 383 | + showExpandedSearch({ focusRow: 0, preventPushState: true }); // a little bit hacky way to ensure the dialog doesn't flicker when navigating forward |
400 | 384 | } |
401 | 385 | } |
402 | 386 |
|
|
463 | 447 | : collapsedEditorView?.dom?.closest('form'); |
464 | 448 |
|
465 | 449 | if (formElement && formElement instanceof HTMLFormElement) { |
466 | | - if (!shallowRouting) { |
467 | | - hideExpandedSearch(); |
| 450 | + if (expanded) { |
| 451 | + if (shallowRouting) { |
| 452 | + const formAction = formElement.getAttribute('action'); |
| 453 | + const formParams = new URLSearchParams( |
| 454 | + new FormData(formElement) as unknown as Record<string, string> |
| 455 | + ); |
| 456 | + if (formAction && formParams) { |
| 457 | + submitLinkElement?.setAttribute( |
| 458 | + 'href', |
| 459 | + `${!formAction.startsWith('/') ? '/' : ''}${formAction}?${formParams.toString()}` // A workaround for fixing back/forward navigation when submitting from expanded dialog (link clicks works together with history.back() but not goto() for some reason...) |
| 460 | + ); |
| 461 | + submitLinkElement?.click(); |
| 462 | + } else { |
| 463 | + formElement.requestSubmit(); |
| 464 | + } |
| 465 | + hideExpandedSearch(); |
| 466 | + } |
| 467 | + } else { |
| 468 | + formElement.requestSubmit(); |
468 | 469 | } |
469 | | - formElement.requestSubmit(); |
470 | 470 | } |
471 | 471 | } |
472 | 472 |
|
|
767 | 767 | } |
768 | 768 |
|
769 | 769 | function interceptExpandedLinks(event: MouseEvent) { |
770 | | - const target = event.target as HTMLElement; |
771 | | - const linkElement = target.tagName === 'A' ? target : target.closest('a'); |
| 770 | + if (interceptedLinkElement) { |
| 771 | + interceptedLinkElement = undefined; // already intercepted so do nothing |
| 772 | + return; |
| 773 | + } else { |
| 774 | + const target = event.target as HTMLElement; |
| 775 | + const linkElement = target.tagName === 'A' ? target : target.closest('a'); |
772 | 776 |
|
773 | | - if (linkElement) { |
774 | | - const href = linkElement.getAttribute('href'); |
775 | | - if (href) { |
| 777 | + if (linkElement && linkElement.getAttribute('href')) { |
776 | 778 | event.preventDefault(); |
777 | | - gotoAfterCollapse(href); |
| 779 | + interceptedLinkElement = linkElement as HTMLLinkElement; |
| 780 | + history.back(); // go back before triggering click again in popstate |
778 | 781 | } |
779 | 782 | } |
780 | 783 | } |
|
975 | 978 | bind:this={comboboxElement} |
976 | 979 | onclick={interceptExpandedLinks} |
977 | 980 | > |
| 981 | + <!-- The hidden submit link is used as a workaround to achieve correct history navigations when submitting --> |
| 982 | + <a |
| 983 | + href="/" |
| 984 | + class="supersearch-hidden-submit-link" |
| 985 | + aria-hidden="true" |
| 986 | + bind:this={submitLinkElement} |
| 987 | + ></a> |
978 | 988 | {@render inputRow?.({ |
979 | 989 | expanded: true, |
980 | 990 | inputField: expandedInputSnippet, |
|
983 | 993 | isFocusedRow: () => activeRowIndex === 0, |
984 | 994 | onclickSubmit: handleClickSubmit, |
985 | 995 | onclickClear: handleReset, |
986 | | - onclickClose: handleClickClose, |
987 | | - gotoAfterCollapse |
| 996 | + onclickClose: handleClickClose |
988 | 997 | })} |
989 | 998 | </div> |
990 | 999 | <!-- svelte-ignore a11y_click_events_have_key_events --> |
|
997 | 1006 | getCellId: (rowIndex: number, colIndex: number) => `${id}-item-${rowIndex}x${colIndex}`, |
998 | 1007 | isFocusedCell: (rowIndex: number, colIndex: number) => |
999 | 1008 | rowIndex === activeRowIndex && colIndex === activeColIndex, |
1000 | | - isFocusedRow: (rowIndex: number) => rowIndex === activeRowIndex, |
1001 | | - gotoAfterCollapse |
| 1009 | + isFocusedRow: (rowIndex: number) => rowIndex === activeRowIndex |
1002 | 1010 | })} |
1003 | 1011 | </div> |
1004 | 1012 | </div> |
|
1017 | 1025 | .supersearch-suggestions { |
1018 | 1026 | overflow: hidden; |
1019 | 1027 | } |
| 1028 | +
|
| 1029 | + .supersearch-hidden-submit-link { |
| 1030 | + display: none; |
| 1031 | + } |
1020 | 1032 | </style> |
0 commit comments