complete right panel

This commit is contained in:
adan.ulloa 2020-01-09 10:15:08 -06:00 committed by Adan Ulloa
commit 75e217c03e
9 changed files with 269 additions and 69 deletions

View file

@ -4,7 +4,9 @@
import UIShell from './UIShell.svelte'; import UIShell from './UIShell.svelte';
import FormTest from './FormTest.svelte'; import FormTest from './FormTest.svelte';
import SettingsAdjust20 from 'carbon-icons-svelte/lib/SettingsAdjust20'; import SettingsAdjust20 from 'carbon-icons-svelte/lib/SettingsAdjust20';
// import Binoculars20 from 'carbon-icons-svelte/lib/Binoculars20'
import { leftPanelActions, leftPanelTypes } from './constants'; import { leftPanelActions, leftPanelTypes } from './constants';
import searchStore from './searchStore';
const navMenu = [ const navMenu = [
{ {
@ -61,6 +63,7 @@
*** actions 'search' and 'switcher' doesnt need to specify type and icon because those are predefined actions in the component. *** actions 'search' and 'switcher' doesnt need to specify type and icon because those are predefined actions in the component.
*/ */
const rightPanel = [ const rightPanel = [
{ {
action: leftPanelActions.search.actionString, action: leftPanelActions.search.actionString,
@ -189,13 +192,35 @@
}, },
isVisible: true isVisible: true
} }
// {
// action: 'customsearch',
// type: leftPanelTypes.search,
// icon: [
// {
// class: undefined,
// skeleton: false,
// render: Binoculars20,
// title: 'binoculars',
// tabIndex: 0,
// focusable: false,
// style: undefined
// }
// ],
// isVisible: true
// }
]; ];
function searchInStore(event) {
if (event.detail.action === leftPanelActions.search.actionString) {
searchStore.search(event.detail.textInput);
}
}
</script> </script>
{#if story === 'with-nav'} {#if story === 'with-nav'}
<UIShell {...$$props} {navMenu} /> <UIShell {...$$props} {navMenu} />
{:else if story === 'with-actions'} {:else if story === 'with-actions'}
<UIShell {...$$props} {rightPanel} /> <UIShell {...$$props} {rightPanel} on:inputSearch={searchInStore} />
{:else} {:else}
<UIShell {...$$props} /> <UIShell {...$$props} />
{/if} {/if}

View file

@ -27,6 +27,6 @@
</UIShellNavWrapper> </UIShellNavWrapper>
{/if} {/if}
{#if rightPanel} {#if rightPanel}
<UIShellRightPanel {rightPanel} /> <UIShellRightPanel {rightPanel} on:inputSearch />
{/if} {/if}
</header> </header>

View file

@ -1,14 +1,14 @@
<!-- <script> <script>
export let action = undefined; // export let action = undefined;
export let type = undefined; export let type = undefined;
export let icon = undefined; export let icon = undefined;
// export let content = undefined; // export let content = undefined;
import { cx } from '../../../lib'; import { cx } from '../../../lib';
import Icon from '../../Icon/Icon.svelte'; import Icon from '../../Icon/Icon.svelte';
import { leftPanelActions } from '../constants'; // import { leftPanelActions } from '../constants';
</script> </script>
<button aria-label={type} class={cx('--header__action')} type="button"> <button aria-label={type} class={cx('--header__action')} type="button">
<Icon {...icon} render={icon[0].render} /> <Icon {...icon} render={icon[0].render} />
</button> --> </button>

View file

@ -1,11 +1,10 @@
<script> <script>
export let action = undefined; export let action = undefined;
import { cx } from '../../../lib';
import ActionLink from './ActionLink.svelte'; import ActionLink from './ActionLink.svelte';
import ActionComponent from './ActionComponent.svelte'; import ActionComponent from './ActionComponent.svelte';
import ActionSearch from './ActionSearch.svelte'; import ActionSearch from './ActionSearch.svelte';
import { leftPanelTypes, actionSearchWhiteList } from '../constants'; import { leftPanelTypes } from '../constants';
let typeComponent = undefined; let typeComponent = undefined;
let componentIsActive = false; let componentIsActive = false;
@ -47,9 +46,6 @@
} }
function checkForClicksTypeSearch(target, component, actionString) { function checkForClicksTypeSearch(target, component, actionString) {
let parentFirstLine;
let parentSecondLine;
if (component && target) { if (component && target) {
try { try {
if ( if (
@ -158,38 +154,45 @@
</style> </style>
{#if action.type === leftPanelTypes.component} {#if action.type === leftPanelTypes.component}
<div bind:this={typeComponent}> {#if action.isVisible}
<ActionComponent <div bind:this={typeComponent}>
action={action.action} <ActionComponent
icon={action.icon ? action.icon : undefined} action={action.action}
content={action.content} icon={action.icon ? action.icon : undefined}
bind:componentIsActive /> content={action.content}
</div> bind:componentIsActive />
</div>
{/if}
{:else if action.type === leftPanelTypes.search} {:else if action.type === leftPanelTypes.search}
<div {#if action.isVisible}
bind:this={typeSearch} <div
class="search-wrapper" bind:this={typeSearch}
class:search-wrapper-hidden={!searchIsActive} class="search-wrapper"
class:search-focus={isSearchFocus || searchIsActive} class:search-wrapper-hidden={!searchIsActive}
role="search"> class:search-focus={isSearchFocus || searchIsActive}
<ActionSearch role="search">
action={action.action} <ActionSearch
icon={action.icon ? action.icon : undefined} action={action.action}
content={action.content} icon={action.icon ? action.icon : undefined}
bind:searchIsActive content={action.content}
focusInputSearch={() => { bind:searchIsActive
isSearchFocus = true; focusInputSearch={() => {
}} isSearchFocus = true;
focusOutInputSearch={() => { }}
isSearchFocus = false; focusOutInputSearch={() => {
}} /> isSearchFocus = false;
</div> }}
on:inputSearch />
</div>
{/if}
{:else if action.type === leftPanelTypes.link || action.type === leftPanelTypes.links} {:else if action.type === leftPanelTypes.link || action.type === leftPanelTypes.links}
<div bind:this={typeLink}> {#if action.isVisible}
<ActionLink <div bind:this={typeLink}>
action={action.action} <ActionLink
icon={action.icon ? action.icon : undefined} action={action.action}
content={action.content} icon={action.icon ? action.icon : undefined}
bind:linkIsActive /> content={action.content}
</div> bind:linkIsActive />
</div>
{/if}
{/if} {/if}

View file

@ -10,8 +10,6 @@
import { leftPanelActions } from '../constants'; import { leftPanelActions } from '../constants';
import { slide } from 'svelte/transition'; import { slide } from 'svelte/transition';
let skeleton = icon ? icon.skeleton : false;
if (!icon) { if (!icon) {
const actionsArray = Object.entries(leftPanelActions); const actionsArray = Object.entries(leftPanelActions);
@ -27,23 +25,14 @@
} }
</script> </script>
{#if action === leftPanelActions.switcher.actionString} <button
<button aria-label={type}
aria-label={type} class={cx('--header__action', linkIsActive && '--header__action--active')}
class={cx('--header__action', linkIsActive && '--header__action--active')} type="button">
type="button"> <Icon {...icon} render={icon.render} />
<Icon {...icon} render={icon.render} /> </button>
</button>
{:else}
<button
aria-label={type}
class={cx('--header__action', linkIsActive && '--header__action--active')}
type="button">
<Icon {...icon} render={icon.render} />
</button>
{/if}
{#if linkIsActive} {#if linkIsActive}
<div class="bx--header-panel bx--header-panel--expanded" transition:slide="{{duration: 200}}"> <div class="bx--header-panel bx--header-panel--expanded" transition:slide={{ duration: 200 }}>
<ul class="bx--switcher__item"> <ul class="bx--switcher__item">
<li class="bx--switcher__item"> <li class="bx--switcher__item">
<a class="bx--switcher__item-link bx--switcher__item-link--selected" href="/">Link</a> <a class="bx--switcher__item-link bx--switcher__item-link--selected" href="/">Link</a>

View file

@ -1,17 +1,18 @@
<script> <script>
export let action = undefined; export let action = undefined;
export let icon = undefined; export let icon = undefined;
// export let content = undefined;
export let searchIsActive = undefined; export let searchIsActive = undefined;
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher } from 'svelte';
import { cx } from '../../../lib'; import { cx } from '../../../lib';
import Icon from '../../Icon/Icon.svelte'; import Icon from '../../Icon/Icon.svelte';
import { leftPanelActions, closeIcon } from '../constants'; import { leftPanelActions, closeIcon } from '../constants';
import searchStore from '../searchStore';
import ActionSearchResult from './ActionSearchResult.svelte'
// let searchIconProps = undefined;
let searchTabIndex = 0; let searchTabIndex = 0;
let closeTabIndex = -1; let closeTabIndex = -1;
let inputSearchField = undefined;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
if (!icon) { if (!icon) {
@ -28,6 +29,26 @@
} }
} }
function dispatchInputs(event) {
const params = {
action: action,
textInput: event.target.value
};
dispatch('inputSearch', params);
}
function clearSearch() {
searchStore.clear();
}
$: if (!searchIsActive) {
try {
inputSearchField.value = '';
searchStore.clear();
} catch {}
}
$: if (searchIsActive) { $: if (searchIsActive) {
searchTabIndex = -1; searchTabIndex = -1;
closeTabIndex = 0; closeTabIndex = 0;
@ -35,6 +56,8 @@
searchTabIndex = 0; searchTabIndex = 0;
closeTabIndex = -1; closeTabIndex = -1;
} }
$: showResults = $searchStore ? true : false;
</script> </script>
<style> <style>
@ -86,6 +109,7 @@
padding: 0; padding: 0;
flex-shrink: 0; flex-shrink: 0;
opacity: 1; opacity: 1;
display: block;
transition: background-color 0.11s cubic-bezier(0.2, 0, 0.38, 0.9), 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); opacity 0.11s cubic-bezier(0.2, 0, 0.38, 0.9);
} }
@ -96,6 +120,20 @@
.btn-clear-hidden { .btn-clear-hidden {
opacity: 0; opacity: 0;
display: none;
}
.search-list {
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);
} }
</style> </style>
@ -115,6 +153,8 @@
<Icon {...icon} render={icon.render} /> <Icon {...icon} render={icon.render} />
</button> </button>
<input <input
bind:this={inputSearchField}
id="input-search-field"
type="text" type="text"
autocomplete="off" autocomplete="off"
tabindex={closeTabIndex} tabindex={closeTabIndex}
@ -122,7 +162,8 @@
class:input-hidden={!searchIsActive} class:input-hidden={!searchIsActive}
placeholder="Search" placeholder="Search"
on:focus={() => dispatch('focusInputSearch')} on:focus={() => dispatch('focusInputSearch')}
on:focusout={() => dispatch('focusOutInputSearch')} /> on:focusout={() => dispatch('focusOutInputSearch')}
on:input={dispatchInputs} />
<button <button
id="right-panel-close-search" id="right-panel-close-search"
tabindex={closeTabIndex} tabindex={closeTabIndex}
@ -130,7 +171,15 @@
class:btn-clear={true} class:btn-clear={true}
class:btn-clear-hidden={!searchIsActive} class:btn-clear-hidden={!searchIsActive}
type="button" type="button"
aria-label="Clear search"> aria-label="Clear search"
on:click={clearSearch}>
<Icon {...closeIcon} render={closeIcon.render} /> <Icon {...closeIcon} render={closeIcon.render} />
</button> </button>
</div> </div>
{#if showResults}
<ul aria-labelledby="search-label" role="menu" id="search-menu" class="search-list">
{#each $searchStore as searchItem, index}
<ActionSearchResult {searchItem} {index} />
{/each}
</ul>
{/if}

View file

@ -0,0 +1,72 @@
<script>
export let searchItem = undefined;
export let index = undefined;
let onHover = false;
</script>
<style>
.search-list-item {
padding: 6px 1rem;
cursor: pointer;
font-size: 0.875rem;
font-weight: 600;
line-height: 1.125rem;
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;
}
.search-list-item-menu {
text-transform: capitalize;
}
.search-list-item-description {
font-size: 0.75rem;
font-weight: 400;
line-height: 1rem;
letter-spacing: 0.32px;
text-transform: lowercase;
color: #c6c6c6;
}
.search-list-item-active {
background-color: #353535;
color: #f4f4f4;
outline: 0;
}
</style>
{#if index === 0}
<li role="none">
<a
tabindex="-1"
id={`menu-item-${index}`}
role="menuitem"
class="search-list-item search-list-item-active"
href={searchItem.href}>
{searchItem.title}&nbsp;
<span class="search-list-item-menu">{searchItem.menu}</span>
<span class="search-list-item-description">{searchItem.description}</span>
</a>
</li>
{:else}
<li role="none" on:mouseover={() => (onHover = true)} on:mouseleave={() => (onHover = false)}>
<a
tabindex="-1"
id={`menu-item-${index}`}
role="menuitem"
class="search-list-item"
class:search-list-item-active={onHover}
href={searchItem.href}>
{searchItem.title}&nbsp;
<span class="search-list-item-menu">{searchItem.menu}</span>
<span class="search-list-item-description">{searchItem.description}</span>
</a>
</li>
{/if}

View file

@ -11,7 +11,11 @@
rightPanel.forEach((item, index) => { rightPanel.forEach((item, index) => {
orderedRightPanel[index] = undefined; orderedRightPanel[index] = undefined;
if (item.action) { if (item.action) {
if (Object.keys(leftPanelActions).indexOf(item.action.charAt(0).toLowerCase() + item.action.slice(1)) === -1) { if (
Object.keys(leftPanelActions).indexOf(
item.action.charAt(0).toLowerCase() + item.action.slice(1)
) === -1
) {
orderedRightPanel[customActions] = rightPanel[index]; orderedRightPanel[customActions] = rightPanel[index];
customActions += 1; customActions += 1;
} }
@ -41,6 +45,6 @@
<div class={cx('--header__global')}> <div class={cx('--header__global')}>
{#each orderedRightPanel as action, index} {#each orderedRightPanel as action, index}
<ActionGeneric {action} /> <ActionGeneric {action} on:inputSearch />
{/each} {/each}
</div> </div>

View file

@ -0,0 +1,58 @@
import { writable } from 'svelte/store';
const data = [
{
href: '#',
title: 'Test title search 1',
menu: 'Test menu 1',
description: 'This is a description for seach #1'
},
{
href: '#',
title: 'Changing text to simulate search',
menu: 'Test menu 2',
description: 'This is a description for seach #2'
},
{
href: '#',
title: 'More testing texts',
menu: 'Test menu 3',
description: 'This is a description for seach #3'
},
{
href: '#',
title: 'We can find here another test text',
menu: 'Test menu 4',
description: 'This is a description for seach #4'
}
];
const globalStore = writable(undefined);
const store = {
subscribe: globalStore.subscribe,
search: searchString => {
if (searchString.length > 1) {
let resultSearch = [];
data.forEach(item => {
if (item.title.toLowerCase().includes(searchString.toLowerCase())) {
resultSearch.push(item);
}
});
if (resultSearch.length > 0) {
globalStore.set(resultSearch);
} else {
globalStore.set(undefined);
}
} else {
globalStore.set(undefined);
}
},
clear: () => {
globalStore.set(undefined);
}
};
export default store;