|
14 | 14 | described in the sections below.
|
15 | 15 | </p>
|
16 | 16 | </header>
|
| 17 | + <!-- Essentials --> |
| 18 | + <section class="space-y-8"> |
| 19 | + <h2 class="h2">Essentials</h2> |
| 20 | + <p>An accessible popover component has the following qualities:</p> |
| 21 | + <ul class="ul"> |
| 22 | + <li> |
| 23 | + <span class="highlight">Dynamic anchor positioning</span>: The popover is positioned next to |
| 24 | + its reference element, remaining anchored to it while avoiding collisions. |
| 25 | + </li> |
| 26 | + <li> |
| 27 | + <span class="highlight">Events</span>: When the reference element is clicked, it toggles the |
| 28 | + popover open or closed. |
| 29 | + </li> |
| 30 | + <li> |
| 31 | + <span class="highlight">Dismissal</span>: When the user presses the |
| 32 | + <kbd class="kbd">esc</kbd> key or outside the popover while it is open, it closes. |
| 33 | + </li> |
| 34 | + <li> |
| 35 | + <span class="highlight">Role</span>: The elements are given relevant role and ARIA |
| 36 | + attributes to be accessible to screen readers. |
| 37 | + </li> |
| 38 | + <li> |
| 39 | + <span class="highlight">Focus management</span>: Focus is managed for non-modal or modal |
| 40 | + behavior. |
| 41 | + </li> |
| 42 | + </ul> |
| 43 | + </section> |
17 | 44 | <!-- Example -->
|
18 | 45 | <section class="space-y-8">
|
| 46 | + <h2 class="h2">Example</h2> |
19 | 47 | <Preview>
|
20 | 48 | {#snippet preview()}<Example />{/snippet}
|
21 | 49 | {#snippet code()}<CodeBlock code={ExampleRaw} lang="svelte" />{/snippet}
|
22 | 50 | </Preview>
|
23 | 51 | </section>
|
| 52 | + <!-- Open State --> |
| 53 | + <section class="space-y-8"> |
| 54 | + <h2 class="h2">Open State</h2> |
| 55 | + <CodeBlock code={`let open = $state(false);`} lang="ts" /> |
| 56 | + <p> |
| 57 | + <code class="code">open</code> determines whether or not the popover is currently open on the screen. |
| 58 | + It is used for conditional rendering. |
| 59 | + </p> |
| 60 | + </section> |
| 61 | + <h2 class="h2">Basic Popover</h2> |
| 62 | + <!-- useFloating Hook --> |
| 63 | + <section class="space-y-8"> |
| 64 | + <h3 class="h3">useFloating Hook</h3> |
| 65 | + <p> |
| 66 | + The <a class="anchor" href="/api/use-floating">useFloating</a> hook provides positioning and context |
| 67 | + for our popover. We need to pass it some information: |
| 68 | + </p> |
| 69 | + <CodeBlock code={`const floating = useFloating({ /* ...settings... */ });`} lang="ts" /> |
| 70 | + <ul class="ul"> |
| 71 | + <li> |
| 72 | + <code class="code">open</code>: The open state from our <code class="code">useState()</code> |
| 73 | + Hook above. |
| 74 | + </li> |
| 75 | + <li> |
| 76 | + <code class="code">onOpenChange</code>: A callback function that will be called when the |
| 77 | + popover is opened or closed. We’ll use this to update our <code class="code">open</code> state. |
| 78 | + </li> |
| 79 | + <li> |
| 80 | + <code class="code">middleware</code>: Import and pass middleware to the array that ensure |
| 81 | + the popover remains on the screen, no matter where it ends up being positioned. |
| 82 | + </li> |
| 83 | + <li> |
| 84 | + <code class="code">whileElementsMounted</code>: Ensure the popover remains anchored to the |
| 85 | + reference element by updating the position when necessary, only while both the reference and |
| 86 | + floating elements are mounted for performance. |
| 87 | + </li> |
| 88 | + </ul> |
| 89 | + </section> |
| 90 | + <!-- Interaction Hooks --> |
| 91 | + <section class="space-y-8"> |
| 92 | + <h3 class="h3">Interaction Hooks</h3> |
| 93 | + <p> |
| 94 | + The <a class="anchor" href="/api/use-interactions">useInteractions</a> hooks returns an object |
| 95 | + containing keys of props that enable the popover to be opened, closed, or accessible to screen |
| 96 | + readers. Using the |
| 97 | + <code class="code">context</code> that was returned from the Hook, call the interaction Hooks. |
| 98 | + </p> |
| 99 | + <CodeBlock |
| 100 | + code={` |
| 101 | +const role = useRole(floating.context); |
| 102 | +const click = useClick(floating.context); |
| 103 | +const dismiss = useDismiss(floating.context); |
| 104 | +const interactions = useInteractions([role, click, dismiss]); |
| 105 | + `} |
| 106 | + lang="ts" |
| 107 | + /> |
| 108 | + <ul class="ul"> |
| 109 | + <li> |
| 110 | + <code class="code">useClick()</code>: adds the ability to toggle the popover open or closed |
| 111 | + when the reference element is clicked. |
| 112 | + </li> |
| 113 | + <li> |
| 114 | + <code class="code">useDismiss()</code>: adds the ability to dismiss the popover when the |
| 115 | + user presses the <kbd class="kbd">esc</kbd> key or presses outside of the popover. |
| 116 | + </li> |
| 117 | + <li> |
| 118 | + <code class="code">useRole()</code>: adds the correct ARIA attributes for a |
| 119 | + <code class="code">dialog</code> to the popover and reference elements. |
| 120 | + </li> |
| 121 | + </ul> |
| 122 | + <p> |
| 123 | + Finally, <code class="code">useInteractions()</code> merges all of their props into prop getters |
| 124 | + which can be used for rendering. |
| 125 | + </p> |
| 126 | + </section> |
| 127 | + <!-- Rendering --> |
| 128 | + <section class="space-y-8"> |
| 129 | + <h3 class="h3">Rendering</h3> |
| 130 | + <p>Now we have all the variables and Hooks set up, we can render out our elements.</p> |
| 131 | + <CodeBlock |
| 132 | + lang="html" |
| 133 | + code={` |
| 134 | +<!-- Reference Element --> |
| 135 | +<button |
| 136 | + bind:this={floating.elements.reference} |
| 137 | + {...interactions.getReferenceProps()} |
| 138 | + class="btn-gradient" |
| 139 | +> |
| 140 | + Click Me |
| 141 | +</button>\n |
| 142 | +<!-- Floating Element --> |
| 143 | +{#if open} |
| 144 | + <div |
| 145 | + bind:this={floating.elements.floating} |
| 146 | + style={floating.floatingStyles} |
| 147 | + {...interactions.getFloatingProps()} |
| 148 | + class="floating popover-neutral" |
| 149 | + transition:fade={{ duration: 200 }} |
| 150 | + > |
| 151 | + <p> |
| 152 | + You can press the <kbd class="kbd">esc</kbd> key or click outside to |
| 153 | + <strong>*dismiss*</strong> this floating element. |
| 154 | + </p> |
| 155 | + <FloatingArrow bind:ref={elemArrow} context={floating.context} fill="#575969" /> |
| 156 | + </div> |
| 157 | +{/if} |
| 158 | + `} |
| 159 | + /> |
| 160 | + <ul class="ul"> |
| 161 | + <li> |
| 162 | + <code class="code">{`{...getReferenceProps()}`}</code> / |
| 163 | + <code class="code">{`{...getFloatingProps()}`}</code> |
| 164 | + spreads the props from the interaction Hooks onto the relevant elements. They contain props like |
| 165 | + <code class="code">onClick</code>, <code class="code">aria-expanded</code>, etc. |
| 166 | + </li> |
| 167 | + <!-- TODO: flush out when FloatingManager available. --> |
| 168 | + <li class="opacity-50 line-through"> |
| 169 | + COMING SOON: <code class="code">{`<FloatingFocusManager />`}</code> is a component that manages |
| 170 | + focus of the popover for non-modal or modal behavior. It should directly wrap the floating element |
| 171 | + and only be rendered when the popover is also rendered. |
| 172 | + </li> |
| 173 | + </ul> |
| 174 | + </section> |
| 175 | + |
| 176 | + <!-- TODO: flush out when FloatingManager available. --> |
| 177 | + |
| 178 | + <!-- Modals and Non-Modal Behavior --> |
| 179 | + <section class="space-y-8"> |
| 180 | + <h3 class="h3">Modals and Non-Modal Behavior</h3> |
| 181 | + <div class="alert">Coming Soon.</div> |
| 182 | + </section> |
| 183 | + <!-- Reusable Popover Component --> |
| 184 | + <h2 class="h2">Reusable Popover Component</h2> |
| 185 | + <div class="alert">Coming Soon.</div> |
24 | 186 | </div>
|
0 commit comments