|
3 | 3 | import { onMount } from 'svelte';
|
4 | 4 | import IconSearch from '~icons/effit/search';
|
5 | 5 | import IconChevronLeft from '~icons/tabler/chevron-left';
|
6 |
| - import IconCircleXFilled from '~icons/tabler/circle-x-filled'; |
| 6 | + import IconX from '~icons/tabler/x'; |
7 | 7 | import { afterNavigate, beforeNavigate, goto } from '$app/navigation';
|
8 | 8 | import { page } from '$app/stores';
|
9 | 9 | import { Icon } from '$lib/components';
|
10 |
| - import { css, cx } from '$styled-system/css'; |
| 10 | + import { TextInput } from '$lib/components/v2/forms'; |
| 11 | + import { css } from '$styled-system/css'; |
11 | 12 | import { center } from '$styled-system/patterns';
|
12 | 13 | import type { SystemStyleObject } from '$styled-system/types';
|
13 | 14 |
|
14 | 15 | export let style: SystemStyleObject | undefined = undefined;
|
15 | 16 |
|
16 | 17 | let value = ($page.url.pathname === '/search' && $page.url.searchParams.get('q')) || '';
|
17 | 18 |
|
18 |
| - let open = false; |
| 19 | + export let open = false; |
19 | 20 |
|
20 | 21 | afterNavigate(({ from, to }) => {
|
21 | 22 | if (!from || !to) return;
|
|
34 | 35 | });
|
35 | 36 |
|
36 | 37 | onMount(() => {
|
37 |
| - if (window.innerWidth >= 800) { |
38 |
| - open = true; |
39 |
| - } |
| 38 | + const toggleSearchBar = () => { |
| 39 | + open = window.innerWidth >= 800; |
| 40 | + }; |
| 41 | +
|
| 42 | + toggleSearchBar(); |
| 43 | +
|
| 44 | + window.addEventListener('resize', toggleSearchBar); |
| 45 | +
|
| 46 | + return () => { |
| 47 | + window.removeEventListener('resize', toggleSearchBar); |
| 48 | + }; |
40 | 49 | });
|
41 | 50 |
|
42 | 51 | beforeNavigate(() => {
|
|
48 | 57 |
|
49 | 58 | {#if open}
|
50 | 59 | <div
|
51 |
| - class={css({ |
52 |
| - flexGrow: '1', |
53 |
| - smDown: { |
54 |
| - position: 'absolute', |
55 |
| - left: '0', |
56 |
| - right: '0', |
57 |
| - top: '0', |
58 |
| - zIndex: '50', |
59 |
| - display: 'flex', |
60 |
| - alignItems: 'center', |
61 |
| - gap: '8px', |
62 |
| - borderBottomWidth: '1px', |
63 |
| - borderBottomColor: 'gray.100', |
64 |
| - paddingX: '20px', |
65 |
| - backgroundColor: 'gray.5', |
66 |
| - height: '56px', |
| 60 | + class={css( |
| 61 | + { |
| 62 | + smDown: { |
| 63 | + position: 'absolute', |
| 64 | + inset: '0', |
| 65 | + zIndex: '50', |
| 66 | + display: 'flex', |
| 67 | + alignItems: 'center', |
| 68 | + gap: '8px', |
| 69 | + backgroundColor: 'gray.5', |
| 70 | + height: 'full', |
| 71 | + }, |
67 | 72 | },
|
68 |
| - })} |
| 73 | + style, |
| 74 | + )} |
69 | 75 | >
|
70 | 76 | <button class={css({ hideFrom: 'sm' })} type="button" on:click={() => (open = false)}>
|
71 | 77 | <Icon icon={IconChevronLeft} size={24} />
|
72 | 78 | </button>
|
73 | 79 | <form
|
74 |
| - class={css( |
75 |
| - { |
76 |
| - position: 'relative', |
77 |
| - smDown: { maxWidth: 'full' }, |
78 |
| - }, |
79 |
| - style, |
80 |
| - )} |
81 | 80 | on:submit|preventDefault={async () => {
|
82 | 81 | await goto(qs.stringifyUrl({ url: '/search', query: { q: value } }));
|
83 | 82 | }}
|
84 | 83 | >
|
85 |
| - <input |
86 |
| - class={cx( |
87 |
| - 'peer', |
88 |
| - css({ |
89 |
| - borderRadius: '6px', |
90 |
| - paddingLeft: '16px', |
91 |
| - paddingRight: '68px', |
92 |
| - paddingY: '10px', |
93 |
| - fontSize: '15px', |
94 |
| - backgroundColor: 'gray.50', |
95 |
| - width: 'full', |
96 |
| - height: { base: '45px', sm: '43px' }, |
97 |
| - transition: 'common', |
98 |
| - _focusWithin: { |
99 |
| - ringWidth: '1px', |
100 |
| - ringColor: 'teal.500', |
101 |
| - backgroundColor: 'gray.5', |
102 |
| - }, |
103 |
| - }), |
104 |
| - )} |
105 |
| - placeholder="검색어를 입력하세요" |
106 |
| - type="search" |
107 |
| - bind:value |
108 |
| - /> |
109 |
| - |
110 |
| - <button |
111 |
| - class={center({ |
112 |
| - position: 'absolute', |
113 |
| - insetY: '0', |
114 |
| - right: '48px', |
115 |
| - color: 'gray.400', |
116 |
| - hideFrom: 'sm', |
117 |
| - })} |
118 |
| - type="button" |
119 |
| - on:click={() => (value = '')} |
120 |
| - > |
121 |
| - <Icon icon={IconCircleXFilled} size={20} /> |
122 |
| - </button> |
123 |
| - |
124 |
| - <button |
125 |
| - class={center({ |
126 |
| - position: 'absolute', |
127 |
| - insetY: '0', |
128 |
| - right: '16px', |
129 |
| - transition: 'common', |
130 |
| - color: { base: 'gray.500', _peerFocus: 'teal.500' }, |
131 |
| - })} |
132 |
| - type="submit" |
133 |
| - > |
134 |
| - <Icon icon={IconSearch} size={20} /> |
135 |
| - </button> |
| 84 | + <TextInput placeholder="검색어를 입력하세요" size="sm" type="search" bind:value> |
| 85 | + <button slot="left-icon" class={center({ transition: 'common', color: 'gray.500' })} type="submit"> |
| 86 | + <Icon icon={IconSearch} size={20} /> |
| 87 | + </button> |
| 88 | + <button |
| 89 | + slot="right-icon" |
| 90 | + class={center({ color: 'gray.600', display: { _disabled: 'none' } })} |
| 91 | + aria-label="지우기" |
| 92 | + disabled={value.length === 0} |
| 93 | + type="button" |
| 94 | + on:click={() => (value = '')} |
| 95 | + > |
| 96 | + <Icon icon={IconX} size={20} /> |
| 97 | + </button> |
| 98 | + </TextInput> |
136 | 99 | </form>
|
137 | 100 | </div>
|
138 | 101 | {:else}
|
139 |
| - <button class={center({ marginRight: '8px', size: '34px' })} type="button" on:click={() => (open = true)}> |
| 102 | + <button |
| 103 | + class={css(style)} |
| 104 | + type="button" |
| 105 | + on:click={async () => { |
| 106 | + open = true; |
| 107 | + }} |
| 108 | + > |
140 | 109 | <Icon style={css.raw({ color: 'gray.900' })} icon={IconSearch} size={24} />
|
141 | 110 | </button>
|
142 | 111 | {/if}
|
0 commit comments