mirror of
https://github.com/carbon-design-system/carbon-components-svelte.git
synced 2025-09-18 11:36:36 +00:00
feat(ui-shell): refactor UI Shell search component
This commit is contained in:
parent
b7bf9ea1f0
commit
7d19ee8aa8
11 changed files with 595 additions and 5 deletions
|
@ -1,6 +1,6 @@
|
||||||
# Component Index
|
# Component Index
|
||||||
|
|
||||||
> 154 components exported from carbon-components-svelte@0.23.2.
|
> 155 components exported from carbon-components-svelte@0.23.2.
|
||||||
|
|
||||||
## Components
|
## Components
|
||||||
|
|
||||||
|
@ -57,6 +57,7 @@
|
||||||
- [`HeaderPanelDivider`](#headerpaneldivider)
|
- [`HeaderPanelDivider`](#headerpaneldivider)
|
||||||
- [`HeaderPanelLink`](#headerpanellink)
|
- [`HeaderPanelLink`](#headerpanellink)
|
||||||
- [`HeaderPanelLinks`](#headerpanellinks)
|
- [`HeaderPanelLinks`](#headerpanellinks)
|
||||||
|
- [`HeaderSearch`](#headersearch)
|
||||||
- [`HeaderUtilities`](#headerutilities)
|
- [`HeaderUtilities`](#headerutilities)
|
||||||
- [`Icon`](#icon)
|
- [`Icon`](#icon)
|
||||||
- [`IconSkeleton`](#iconskeleton)
|
- [`IconSkeleton`](#iconskeleton)
|
||||||
|
@ -1609,6 +1610,47 @@ None.
|
||||||
|
|
||||||
None.
|
None.
|
||||||
|
|
||||||
|
## `HeaderSearch`
|
||||||
|
|
||||||
|
### Types
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export interface HeaderSearchResult {
|
||||||
|
href: string;
|
||||||
|
text: string;
|
||||||
|
description?: string;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Props
|
||||||
|
|
||||||
|
| Prop name | Kind | Reactive | Type | Default value | Description |
|
||||||
|
| :------------------ | :--------------- | :------- | :---------------------------------------- | ------------------ | -------------------------------------------------- |
|
||||||
|
| selectedResultIndex | <code>let</code> | Yes | <code>number</code> | <code>-1</code> | Specify the selected result index |
|
||||||
|
| ref | <code>let</code> | Yes | <code>null | HTMLInputElement</code> | <code>null</code> | Obtain a reference to the input HTML element |
|
||||||
|
| active | <code>let</code> | Yes | <code>boolean</code> | <code>false</code> | Set to `true` to activate and focus the search bar |
|
||||||
|
| value | <code>let</code> | Yes | <code>string</code> | <code>""</code> | Specify the search input value |
|
||||||
|
| results | <code>let</code> | No | <code>HeaderSearchResult[]</code> | <code>[]</code> | Render a list of search results |
|
||||||
|
|
||||||
|
### Slots
|
||||||
|
|
||||||
|
| Slot name | Default | Props | Fallback |
|
||||||
|
| :-------- | :------ | :---------------------------- | :------------------------------------------------------------------------------------------------------------ |
|
||||||
|
| -- | Yes | <code>{ result: any } </code> | <code>{result.text}<br /> {#if result.description}<span>– {result.description}</span>{/if}</code> |
|
||||||
|
|
||||||
|
### Events
|
||||||
|
|
||||||
|
| Event name | Type | Detail |
|
||||||
|
| :--------- | :--------- | :---------------------------------------------------------------------------------------------- |
|
||||||
|
| clear | dispatched | <code>any</code> |
|
||||||
|
| search | dispatched | <code>{ value: string; selectedResultIndex: number; selectedResult: HeaderSearchResult }</code> |
|
||||||
|
| change | forwarded | -- |
|
||||||
|
| input | forwarded | -- |
|
||||||
|
| focus | forwarded | -- |
|
||||||
|
| blur | forwarded | -- |
|
||||||
|
| keydown | forwarded | -- |
|
||||||
|
| select | dispatched | -- |
|
||||||
|
|
||||||
## `HeaderUtilities`
|
## `HeaderUtilities`
|
||||||
|
|
||||||
### Props
|
### Props
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"total": 154,
|
"total": 155,
|
||||||
"components": [
|
"components": [
|
||||||
{
|
{
|
||||||
"moduleName": "SkeletonText",
|
"moduleName": "SkeletonText",
|
||||||
|
@ -10258,6 +10258,92 @@
|
||||||
"typedefs": [],
|
"typedefs": [],
|
||||||
"rest_props": { "type": "Element", "name": "button" }
|
"rest_props": { "type": "Element", "name": "button" }
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"moduleName": "HeaderSearch",
|
||||||
|
"filePath": "/src/UIShell/HeaderSearch.svelte",
|
||||||
|
"props": [
|
||||||
|
{
|
||||||
|
"name": "value",
|
||||||
|
"kind": "let",
|
||||||
|
"description": "Specify the search input value",
|
||||||
|
"type": "string",
|
||||||
|
"value": "\"\"",
|
||||||
|
"isFunction": false,
|
||||||
|
"constant": false,
|
||||||
|
"reactive": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "active",
|
||||||
|
"kind": "let",
|
||||||
|
"description": "Set to `true` to activate and focus the search bar",
|
||||||
|
"type": "boolean",
|
||||||
|
"value": "false",
|
||||||
|
"isFunction": false,
|
||||||
|
"constant": false,
|
||||||
|
"reactive": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ref",
|
||||||
|
"kind": "let",
|
||||||
|
"description": "Obtain a reference to the input HTML element",
|
||||||
|
"type": "null | HTMLInputElement",
|
||||||
|
"value": "null",
|
||||||
|
"isFunction": false,
|
||||||
|
"constant": false,
|
||||||
|
"reactive": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "results",
|
||||||
|
"kind": "let",
|
||||||
|
"description": "Render a list of search results",
|
||||||
|
"type": "HeaderSearchResult[]",
|
||||||
|
"value": "[]",
|
||||||
|
"isFunction": false,
|
||||||
|
"constant": false,
|
||||||
|
"reactive": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "selectedResultIndex",
|
||||||
|
"kind": "let",
|
||||||
|
"description": "Specify the selected result index",
|
||||||
|
"type": "number",
|
||||||
|
"value": "-1",
|
||||||
|
"isFunction": false,
|
||||||
|
"constant": false,
|
||||||
|
"reactive": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"slots": [
|
||||||
|
{
|
||||||
|
"name": "__default__",
|
||||||
|
"default": true,
|
||||||
|
"fallback": "{result.text}\n {#if result.description}<span>– {result.description}</span>{/if}",
|
||||||
|
"slot_props": "{ result: any }"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"events": [
|
||||||
|
{ "type": "dispatched", "name": "clear", "detail": "any" },
|
||||||
|
{
|
||||||
|
"type": "dispatched",
|
||||||
|
"name": "search",
|
||||||
|
"detail": "{ value: string; selectedResultIndex: number; selectedResult: HeaderSearchResult }"
|
||||||
|
},
|
||||||
|
{ "type": "forwarded", "name": "change", "element": "input" },
|
||||||
|
{ "type": "forwarded", "name": "input", "element": "input" },
|
||||||
|
{ "type": "forwarded", "name": "focus", "element": "input" },
|
||||||
|
{ "type": "forwarded", "name": "blur", "element": "input" },
|
||||||
|
{ "type": "forwarded", "name": "keydown", "element": "input" },
|
||||||
|
{ "type": "dispatched", "name": "select" }
|
||||||
|
],
|
||||||
|
"typedefs": [
|
||||||
|
{
|
||||||
|
"type": "{ href: string; text: string; description?: string; }",
|
||||||
|
"name": "HeaderSearchResult",
|
||||||
|
"ts": "interface HeaderSearchResult { href: string; text: string; description?: string; }"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"rest_props": { "type": "Element", "name": "input" }
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"moduleName": "UnorderedList",
|
"moduleName": "UnorderedList",
|
||||||
"filePath": "/src/UnorderedList/UnorderedList.svelte",
|
"filePath": "/src/UnorderedList/UnorderedList.svelte",
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
components: ["Header",
|
components: ["Header",
|
||||||
"HeaderAction",
|
"HeaderAction",
|
||||||
"HeaderActionLink",
|
"HeaderActionLink",
|
||||||
"HeaderActionSearch",
|
"HeaderSearch",
|
||||||
"HeaderNav",
|
"HeaderNav",
|
||||||
"HeaderNavItem",
|
"HeaderNavItem",
|
||||||
"HeaderNavMenu",
|
"HeaderNavMenu",
|
||||||
|
@ -34,6 +34,10 @@ components: ["Header",
|
||||||
|
|
||||||
<FileSource src="/framed/UIShell/HeaderSwitcher" />
|
<FileSource src="/framed/UIShell/HeaderSwitcher" />
|
||||||
|
|
||||||
|
### Header with global search
|
||||||
|
|
||||||
|
<FileSource src="/framed/UIShell/HeaderSearch" />
|
||||||
|
|
||||||
### Header with utilities
|
### Header with utilities
|
||||||
|
|
||||||
<FileSource src="/framed/UIShell/HeaderUtilities" />
|
<FileSource src="/framed/UIShell/HeaderUtilities" />
|
||||||
|
|
114
docs/src/pages/framed/UIShell/HeaderSearch.svelte
Normal file
114
docs/src/pages/framed/UIShell/HeaderSearch.svelte
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
<script>
|
||||||
|
import {
|
||||||
|
Header,
|
||||||
|
HeaderUtilities,
|
||||||
|
HeaderAction,
|
||||||
|
HeaderSearch,
|
||||||
|
HeaderPanelLinks,
|
||||||
|
HeaderPanelDivider,
|
||||||
|
HeaderPanelLink,
|
||||||
|
SideNav,
|
||||||
|
SideNavItems,
|
||||||
|
SideNavMenu,
|
||||||
|
SideNavMenuItem,
|
||||||
|
SideNavLink,
|
||||||
|
SkipToContent,
|
||||||
|
Content,
|
||||||
|
Grid,
|
||||||
|
Row,
|
||||||
|
Column,
|
||||||
|
} from "carbon-components-svelte";
|
||||||
|
|
||||||
|
let isSideNavOpen = false;
|
||||||
|
let isOpen = false;
|
||||||
|
|
||||||
|
let ref = null;
|
||||||
|
let active = false;
|
||||||
|
let value = "";
|
||||||
|
let selectedResultIndex = 1;
|
||||||
|
|
||||||
|
$: results =
|
||||||
|
value.length > 2
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
href: "/",
|
||||||
|
text: "Result 1",
|
||||||
|
description: "Result description",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: "/",
|
||||||
|
text: "Result 2",
|
||||||
|
description: "Result description",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: "/",
|
||||||
|
text: "Result 3",
|
||||||
|
description: "Result description",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: [];
|
||||||
|
|
||||||
|
$: console.log("ref", ref);
|
||||||
|
$: console.log("active", active);
|
||||||
|
$: console.log("value", value);
|
||||||
|
$: console.log("selectedResultIndex", selectedResultIndex);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Header company="IBM" platformName="Carbon Svelte" bind:isSideNavOpen>
|
||||||
|
<div slot="skip-to-content">
|
||||||
|
<SkipToContent />
|
||||||
|
</div>
|
||||||
|
<HeaderUtilities>
|
||||||
|
<HeaderSearch
|
||||||
|
bind:ref
|
||||||
|
bind:active
|
||||||
|
bind:value
|
||||||
|
on:search="{(e) => {
|
||||||
|
console.log('on:search', e.detail);
|
||||||
|
}}"
|
||||||
|
on:clear="{() => {
|
||||||
|
console.log('on:clear');
|
||||||
|
}}"
|
||||||
|
on:select="{(e) => {
|
||||||
|
console.log('on:select', e.detail);
|
||||||
|
}}"
|
||||||
|
results="{results}"
|
||||||
|
bind:selectedResultIndex
|
||||||
|
/>
|
||||||
|
<HeaderAction bind:isOpen>
|
||||||
|
<HeaderPanelLinks>
|
||||||
|
<HeaderPanelDivider>Switcher subject 1</HeaderPanelDivider>
|
||||||
|
<HeaderPanelLink>Switcher item 1</HeaderPanelLink>
|
||||||
|
<HeaderPanelDivider>Switcher subject 2</HeaderPanelDivider>
|
||||||
|
<HeaderPanelLink>Switcher item 1</HeaderPanelLink>
|
||||||
|
<HeaderPanelLink>Switcher item 2</HeaderPanelLink>
|
||||||
|
<HeaderPanelLink>Switcher item 3</HeaderPanelLink>
|
||||||
|
<HeaderPanelLink>Switcher item 4</HeaderPanelLink>
|
||||||
|
<HeaderPanelLink>Switcher item 5</HeaderPanelLink>
|
||||||
|
</HeaderPanelLinks>
|
||||||
|
</HeaderAction>
|
||||||
|
</HeaderUtilities>
|
||||||
|
</Header>
|
||||||
|
|
||||||
|
<SideNav bind:isOpen="{isSideNavOpen}">
|
||||||
|
<SideNavItems>
|
||||||
|
<SideNavLink text="Link 1" />
|
||||||
|
<SideNavLink text="Link 2" />
|
||||||
|
<SideNavLink text="Link 3" />
|
||||||
|
<SideNavMenu text="Menu">
|
||||||
|
<SideNavMenuItem href="/" text="Link 1" />
|
||||||
|
<SideNavMenuItem href="/" text="Link 2" />
|
||||||
|
<SideNavMenuItem href="/" text="Link 3" />
|
||||||
|
</SideNavMenu>
|
||||||
|
</SideNavItems>
|
||||||
|
</SideNav>
|
||||||
|
|
||||||
|
<Content>
|
||||||
|
<Grid>
|
||||||
|
<Row>
|
||||||
|
<Column>
|
||||||
|
<h1>Welcome</h1>
|
||||||
|
</Column>
|
||||||
|
</Row>
|
||||||
|
</Grid>
|
||||||
|
</Content>
|
|
@ -3,7 +3,6 @@
|
||||||
Header,
|
Header,
|
||||||
HeaderUtilities,
|
HeaderUtilities,
|
||||||
HeaderAction,
|
HeaderAction,
|
||||||
HeaderActionSearch,
|
|
||||||
HeaderGlobalAction,
|
HeaderGlobalAction,
|
||||||
HeaderPanelLinks,
|
HeaderPanelLinks,
|
||||||
HeaderPanelDivider,
|
HeaderPanelDivider,
|
||||||
|
@ -30,7 +29,6 @@
|
||||||
<SkipToContent />
|
<SkipToContent />
|
||||||
</div>
|
</div>
|
||||||
<HeaderUtilities>
|
<HeaderUtilities>
|
||||||
<HeaderActionSearch />
|
|
||||||
<HeaderGlobalAction aria-label="Settings" icon="{SettingsAdjust20}" />
|
<HeaderGlobalAction aria-label="Settings" icon="{SettingsAdjust20}" />
|
||||||
<HeaderAction bind:isOpen>
|
<HeaderAction bind:isOpen>
|
||||||
<HeaderPanelLinks>
|
<HeaderPanelLinks>
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
<script>
|
<script>
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* This component will be removed in version 1.0.0.
|
||||||
|
* Use `HeaderSearch` instead
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @event {{ action: "search"; textInput: string; }} inputSearch
|
* @event {{ action: "search"; textInput: string; }} inputSearch
|
||||||
*/
|
*/
|
||||||
|
|
278
src/UIShell/HeaderSearch.svelte
Normal file
278
src/UIShell/HeaderSearch.svelte
Normal file
|
@ -0,0 +1,278 @@
|
||||||
|
<script>
|
||||||
|
/**
|
||||||
|
* @typedef {{ href: string; text: string; description?: string; }} HeaderSearchResult
|
||||||
|
* @event {any} clear
|
||||||
|
* @event {{ value: string; selectedResultIndex: number; selectedResult: HeaderSearchResult }} search
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Specify the search input value */
|
||||||
|
export let value = "";
|
||||||
|
|
||||||
|
/** Set to `true` to activate and focus the search bar */
|
||||||
|
export let active = false;
|
||||||
|
|
||||||
|
/** Obtain a reference to the input HTML element */
|
||||||
|
export let ref = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a list of search results
|
||||||
|
* @type {HeaderSearchResult[]}
|
||||||
|
*/
|
||||||
|
export let results = [];
|
||||||
|
|
||||||
|
/** Specify the selected result index */
|
||||||
|
export let selectedResultIndex = -1;
|
||||||
|
|
||||||
|
import { createEventDispatcher } from "svelte";
|
||||||
|
import Close20 from "carbon-icons-svelte/lib/Close20/Close20.svelte";
|
||||||
|
import Search20 from "carbon-icons-svelte/lib/Search20/Search20.svelte";
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
|
let refSearch = null;
|
||||||
|
|
||||||
|
$: if (active && ref) ref.focus();
|
||||||
|
$: selectedResult = results[selectedResultIndex];
|
||||||
|
$: selectedId = selectedResult
|
||||||
|
? `search-menuitem-${selectedResultIndex}`
|
||||||
|
: undefined;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
label {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
margin: -1px;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
border: 0;
|
||||||
|
visibility: inherit;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[role="search"] {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
max-width: 28rem;
|
||||||
|
width: 100%;
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
height: 3rem;
|
||||||
|
background-color: #393939;
|
||||||
|
color: #fff;
|
||||||
|
transition: max-width 0.11s cubic-bezier(0.2, 0, 0.38, 0.9),
|
||||||
|
background 0.11s cubic-bezier(0.2, 0, 0.38, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
[role="search"]:not(.active) {
|
||||||
|
max-width: 3rem;
|
||||||
|
background-color: #161616;
|
||||||
|
}
|
||||||
|
|
||||||
|
[role="search"].active {
|
||||||
|
outline: 2px solid #fff;
|
||||||
|
outline-offset: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[role="combobox"] {
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
border-bottom: 1px solid #393939;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
height: 3rem;
|
||||||
|
padding: 0;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.375rem;
|
||||||
|
letter-spacing: 0;
|
||||||
|
color: #fff;
|
||||||
|
caret-color: #fff;
|
||||||
|
background-color: initial;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
transition: opacity 0.11s cubic-bezier(0.2, 0, 0.38, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
input:not(.active) {
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 3rem;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
opacity: 1;
|
||||||
|
transition: background-color 0.11s cubic-bezier(0.2, 0, 0.38, 0.9),
|
||||||
|
opacity 0.11s cubic-bezier(0.2, 0, 0.38, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.disabled {
|
||||||
|
border: none;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[aria-label="Clear search"]:hover {
|
||||||
|
background-color: #4c4c4c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
opacity: 0;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 10000;
|
||||||
|
padding: 1rem 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 3rem;
|
||||||
|
background-color: #161616;
|
||||||
|
border: 1px solid #393939;
|
||||||
|
border-top: none;
|
||||||
|
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
[role="menuitem"] {
|
||||||
|
padding: 6px 1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.29;
|
||||||
|
letter-spacing: 0.16px;
|
||||||
|
transition: all 70ms cubic-bezier(0.2, 0, 0.38, 0.9);
|
||||||
|
display: block;
|
||||||
|
text-decoration: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
color: #c6c6c6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected,
|
||||||
|
[role="menuitem"]:hover {
|
||||||
|
background-color: #353535;
|
||||||
|
color: #f4f4f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
[role="menuitem"] span {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.34;
|
||||||
|
letter-spacing: 0.32px;
|
||||||
|
text-transform: lowercase;
|
||||||
|
color: #c6c6c6;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<svelte:window
|
||||||
|
on:mouseup="{({ target }) => {
|
||||||
|
if (active && !refSearch.contains(target)) active = false;
|
||||||
|
}}"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div bind:this="{refSearch}" role="search" class:active>
|
||||||
|
<label for="search-input" id="search-label">Search</label>
|
||||||
|
<div role="combobox" aria-expanded="{active}">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
aria-label="Search"
|
||||||
|
tabindex="{active ? '-1' : '0'}"
|
||||||
|
class:bx--header__action="{true}"
|
||||||
|
class:disabled="{active}"
|
||||||
|
on:click="{() => {
|
||||||
|
active = true;
|
||||||
|
}}"
|
||||||
|
>
|
||||||
|
<Search20 title="Search" />
|
||||||
|
</button>
|
||||||
|
<input
|
||||||
|
bind:this="{ref}"
|
||||||
|
type="text"
|
||||||
|
autocomplete="off"
|
||||||
|
placeholder="Search..."
|
||||||
|
tabindex="{active ? '0' : '-1'}"
|
||||||
|
class:active
|
||||||
|
{...$$restProps}
|
||||||
|
id="search-input"
|
||||||
|
aria-activedescendant="{selectedId}"
|
||||||
|
bind:value
|
||||||
|
on:change
|
||||||
|
on:input
|
||||||
|
on:focus
|
||||||
|
on:blur
|
||||||
|
on:keydown
|
||||||
|
on:keydown="{({ key }) => {
|
||||||
|
switch (key) {
|
||||||
|
case 'Enter':
|
||||||
|
active = false;
|
||||||
|
value = '';
|
||||||
|
dispatch('select', { value, selectedResultIndex, selectedResult });
|
||||||
|
break;
|
||||||
|
case 'ArrowDown':
|
||||||
|
if (selectedResultIndex === results.length - 1) {
|
||||||
|
selectedResultIndex = 0;
|
||||||
|
} else {
|
||||||
|
selectedResultIndex += 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'ArrowUp':
|
||||||
|
if (selectedResultIndex === 0) {
|
||||||
|
selectedResultIndex = results.length - 1;
|
||||||
|
} else {
|
||||||
|
selectedResultIndex -= 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}}"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
aria-label="Clear search"
|
||||||
|
tabindex="{active ? '0' : '-1'}"
|
||||||
|
class:bx--header__action="{true}"
|
||||||
|
class:hidden="{!active}"
|
||||||
|
on:click="{() => {
|
||||||
|
active = false;
|
||||||
|
value = '';
|
||||||
|
dispatch('clear');
|
||||||
|
}}"
|
||||||
|
>
|
||||||
|
<Close20 title="Close" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if active && results.length > 0}
|
||||||
|
<ul aria-labelledby="search-label" role="menu" id="search-menu">
|
||||||
|
{#each results as result, i}
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
tabindex="-1"
|
||||||
|
id="search-menuitem-{i}"
|
||||||
|
role="menuitem"
|
||||||
|
href="{result.href}"
|
||||||
|
class:selected="{selectedId === `search-menuitem-${i}`}"
|
||||||
|
on:click|preventDefault="{() => {
|
||||||
|
dispatch('select', {
|
||||||
|
value,
|
||||||
|
selectedResultIndex,
|
||||||
|
selectedResult,
|
||||||
|
});
|
||||||
|
}}"
|
||||||
|
>
|
||||||
|
<slot result="{result}">
|
||||||
|
{result.text}
|
||||||
|
{#if result.description}<span>– {result.description}</span>{/if}
|
||||||
|
</slot>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
{/if}
|
||||||
|
</div>
|
|
@ -17,3 +17,4 @@ export { default as SideNavMenuItem } from "./SideNav/SideNavMenuItem.svelte";
|
||||||
export { default as Content } from "./Content.svelte";
|
export { default as Content } from "./Content.svelte";
|
||||||
export { default as SkipToContent } from "./SkipToContent.svelte";
|
export { default as SkipToContent } from "./SkipToContent.svelte";
|
||||||
export { default as HeaderGlobalAction } from "./HeaderGlobalAction.svelte";
|
export { default as HeaderGlobalAction } from "./HeaderGlobalAction.svelte";
|
||||||
|
export { default as HeaderSearch } from "./HeaderSearch.svelte";
|
||||||
|
|
|
@ -134,5 +134,6 @@ export {
|
||||||
Content,
|
Content,
|
||||||
SkipToContent,
|
SkipToContent,
|
||||||
HeaderGlobalAction,
|
HeaderGlobalAction,
|
||||||
|
HeaderSearch,
|
||||||
} from "./UIShell";
|
} from "./UIShell";
|
||||||
export { UnorderedList } from "./UnorderedList";
|
export { UnorderedList } from "./UnorderedList";
|
||||||
|
|
59
types/UIShell/HeaderSearch.d.ts
vendored
Normal file
59
types/UIShell/HeaderSearch.d.ts
vendored
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
/// <reference types="svelte" />
|
||||||
|
|
||||||
|
export interface HeaderSearchResult {
|
||||||
|
href: string;
|
||||||
|
text: string;
|
||||||
|
description?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HeaderSearchProps extends svelte.JSX.HTMLAttributes<HTMLElementTagNameMap["input"]> {
|
||||||
|
/**
|
||||||
|
* Specify the search input value
|
||||||
|
* @default ""
|
||||||
|
*/
|
||||||
|
value?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set to `true` to activate and focus the search bar
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
active?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain a reference to the input HTML element
|
||||||
|
* @default null
|
||||||
|
*/
|
||||||
|
ref?: null | HTMLInputElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a list of search results
|
||||||
|
* @default []
|
||||||
|
*/
|
||||||
|
results?: HeaderSearchResult[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify the selected result index
|
||||||
|
* @default -1
|
||||||
|
*/
|
||||||
|
selectedResultIndex?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class HeaderSearch {
|
||||||
|
$$prop_def: HeaderSearchProps;
|
||||||
|
$$slot_def: {
|
||||||
|
default: { result: any };
|
||||||
|
};
|
||||||
|
|
||||||
|
$on(eventname: "clear", cb: (event: CustomEvent<any>) => void): () => void;
|
||||||
|
$on(
|
||||||
|
eventname: "search",
|
||||||
|
cb: (event: CustomEvent<{ value: string; selectedResultIndex: number; selectedResult: HeaderSearchResult }>) => void
|
||||||
|
): () => void;
|
||||||
|
$on(eventname: "change", cb: (event: WindowEventMap["change"]) => void): () => void;
|
||||||
|
$on(eventname: "input", cb: (event: WindowEventMap["input"]) => void): () => void;
|
||||||
|
$on(eventname: "focus", cb: (event: WindowEventMap["focus"]) => void): () => void;
|
||||||
|
$on(eventname: "blur", cb: (event: WindowEventMap["blur"]) => void): () => void;
|
||||||
|
$on(eventname: "keydown", cb: (event: WindowEventMap["keydown"]) => void): () => void;
|
||||||
|
$on(eventname: "select", cb: (event: CustomEvent<any>) => void): () => void;
|
||||||
|
$on(eventname: string, cb: (event: Event) => void): () => void;
|
||||||
|
}
|
1
types/index.d.ts
vendored
1
types/index.d.ts
vendored
|
@ -151,4 +151,5 @@ export { default as SideNavMenuItem } from "./UIShell/SideNav/SideNavMenuItem";
|
||||||
export { default as Content } from "./UIShell/Content";
|
export { default as Content } from "./UIShell/Content";
|
||||||
export { default as SkipToContent } from "./UIShell/SkipToContent";
|
export { default as SkipToContent } from "./UIShell/SkipToContent";
|
||||||
export { default as HeaderGlobalAction } from "./UIShell/HeaderGlobalAction";
|
export { default as HeaderGlobalAction } from "./UIShell/HeaderGlobalAction";
|
||||||
|
export { default as HeaderSearch } from "./UIShell/HeaderSearch";
|
||||||
export { default as UnorderedList } from "./UnorderedList/UnorderedList";
|
export { default as UnorderedList } from "./UnorderedList/UnorderedList";
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue