|
1 | 1 | <script lang="ts"> |
2 | 2 | interface SwitcherProps { |
3 | 3 | /** |
4 | | - * Human-readable label for the input. |
| 4 | + * Human-readable label |
5 | 5 | */ |
6 | 6 | label: string; |
7 | | - options: string[]; |
8 | 7 | /** |
9 | | - * Machine-readable name for the input. Should be unique across the document. |
| 8 | + * Available options |
10 | 9 | */ |
11 | | - groupName: string; |
| 10 | + options: string[]; |
12 | 11 | /** |
13 | | - * Type size |
| 12 | + * Display size |
14 | 13 | */ |
15 | 14 | size?: 'default' | 'small'; |
16 | 15 | /** |
|
19 | 18 | value: string | null; |
20 | 19 | } |
21 | 20 |
|
22 | | - let { |
23 | | - label, |
24 | | - options, |
25 | | - groupName, |
26 | | - size = 'default', |
27 | | - value = $bindable(null) |
28 | | - }: SwitcherProps = $props(); |
| 21 | + let { label, options, size = 'default', value = $bindable(null) }: SwitcherProps = $props(); |
| 22 | +
|
| 23 | + const groupId = $props.id(); |
| 24 | + const groupName = 'select-' + groupId; |
29 | 25 |
|
30 | 26 | function optionToID(o: string) { |
31 | | - // TODO: This should use $id() when it comes out, so |
32 | | - // input IDs are guaranteed unique across the app |
33 | | - // See: https://github.com/sveltejs/svelte/issues/13108 |
34 | 27 | return `${groupName}-option-${o.replace(/ /g, '-').toLowerCase()}`; |
35 | 28 | } |
36 | 29 | </script> |
|
42 | 35 | <li class:is-selected={o === value}> |
43 | 36 | <label for={optionToID(o)}> |
44 | 37 | {o} |
45 | | - <svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> |
46 | | - <path |
47 | | - d="M19.7054 6.29119C20.0969 6.68077 20.0984 7.31393 19.7088 7.7054L9.75697 17.7054C9.56928 17.894 9.31416 18 9.04809 18C8.78201 18 8.52691 17.8939 8.33925 17.7053L4.2911 13.6365C3.90157 13.245 3.90318 12.6118 4.2947 12.2223C4.68621 11.8327 5.31938 11.8344 5.7089 12.2259L9.04825 15.5823L18.2912 6.2946C18.6808 5.90314 19.3139 5.90161 19.7054 6.29119Z" |
48 | | - fill="currentColor" |
49 | | - /> |
50 | | - </svg> |
51 | 38 | </label> |
52 | 39 | <input id={optionToID(o)} name={groupName} value={o} type="radio" bind:group={value} /> |
53 | 40 | </li> |
|
56 | 43 | </fieldset> |
57 | 44 |
|
58 | 45 | <style lang="scss"> |
| 46 | + @use '../styles/base.scss'; |
| 47 | +
|
59 | 48 | fieldset { |
60 | 49 | border: 0; |
61 | 50 | font-family: var(--swr-sans); |
62 | 51 | } |
63 | 52 |
|
64 | 53 | legend { |
65 | 54 | font-size: var(--fs-small-2); |
66 | | - margin-bottom: 0.25em; |
| 55 | + font-weight: 500; |
| 56 | + margin-bottom: 0.35em; |
67 | 57 | } |
68 | 58 |
|
69 | 59 | ul { |
70 | 60 | width: 100%; |
71 | 61 | display: flex; |
72 | | - flex-direction: row; |
73 | | - border-radius: var(--br-small); |
| 62 | + flex-direction: column; |
74 | 63 | overflow: hidden; |
75 | 64 | padding: 0; |
76 | 65 | margin: 0; |
77 | | - overflow: hidden; |
78 | 66 | border: 1px solid currentColor; |
| 67 | + border-radius: var(--br-small); |
| 68 | +
|
| 69 | + @media (min-width: base.$break-phone) { |
| 70 | + flex-flow: row; |
| 71 | + } |
79 | 72 |
|
80 | 73 | &:focus-within, |
81 | 74 | &:active { |
82 | | - outline: 3px solid rgba(white, 0.5); |
| 75 | + outline: 2px solid var(--blue-light-2); |
83 | 76 | } |
84 | 77 | } |
85 | 78 | li { |
86 | 79 | display: contents; |
87 | 80 | &:last-child label { |
88 | 81 | border-right: 0; |
| 82 | + border-bottom: 0; |
89 | 83 | } |
90 | 84 | } |
91 | 85 | input { |
|
94 | 88 | } |
95 | 89 | .small label { |
96 | 90 | font-size: var(--fs-small-1); |
| 91 | + height: 2.25em; |
| 92 | + padding: 0 0.65em; |
97 | 93 | } |
98 | 94 | label { |
99 | 95 | font-size: var(--fs-base); |
100 | | - height: 2.5em; |
101 | 96 | line-height: 1; |
| 97 | + white-space: nowrap; |
| 98 | + padding: 0 1em; |
102 | 99 | cursor: pointer; |
103 | 100 | margin: 0; |
104 | | - flex-basis: 0; |
105 | | - flex-grow: 1; |
106 | 101 | align-items: center; |
107 | 102 | display: flex; |
108 | | - justify-content: center; |
109 | 103 | color: currentColor; |
110 | 104 | position: relative; |
111 | 105 | transition: var(--fast); |
112 | | - text-underline-offset: 0.1em; |
113 | | - border-right: 1px solid currentColor; |
| 106 | + text-underline-offset: 0.2em; |
| 107 | + border-bottom: 1px solid currentColor; |
| 108 | + height: 2.25em; |
| 109 | + @media (min-width: base.$break-phone) { |
| 110 | + justify-content: center; |
| 111 | + padding: 0 1em; |
| 112 | + flex-basis: 0; |
| 113 | + flex-grow: 1; |
| 114 | + border-right: 1px solid currentColor; |
| 115 | + border-bottom: 0; |
| 116 | + } |
| 117 | + @media (min-width: base.$break-tablet) { |
| 118 | + height: 2.5em; |
| 119 | + } |
114 | 120 | &:hover, |
115 | 121 | &:focus-visible { |
116 | 122 | text-decoration: underline; |
117 | 123 | } |
118 | | - svg { |
119 | | - position: absolute; |
120 | | - left: 0.65em; |
121 | | - width: 1em; |
122 | | - height: auto; |
123 | | - opacity: 0; |
124 | | - transition: var(--fast); |
125 | | - display: block; |
126 | | - } |
127 | 124 | .is-selected & { |
128 | | - background: white; |
129 | | - color: black; |
130 | | - svg { |
131 | | - opacity: 1; |
| 125 | + background: rgb(247, 247, 247); |
| 126 | + font-weight: 700; |
| 127 | + box-shadow: inset 5px 0px 0 0 var(--violet-dark-5); |
| 128 | + @media (min-width: base.$break-phone) { |
| 129 | + box-shadow: inset 0 -3px 0 0 var(--violet-dark-5); |
132 | 130 | } |
133 | 131 | } |
134 | 132 | } |
|
0 commit comments