uisehll with nav finished

This commit is contained in:
adan.ulloa 2020-01-06 18:41:10 -06:00
commit 390f950ca5
10 changed files with 578 additions and 128 deletions

View file

@ -1,3 +0,0 @@
<script>
</script>

View file

@ -3,8 +3,8 @@
import UIShell from './UIShell.svelte';
import FormTest from './FormTest.svelte';
import Notification20 from 'carbon-icons-svelte/lib/Notification20';
import UserAvatar20 from 'carbon-icons-svelte/lib/UserAvatar20';
import SettingsAdjust20 from 'carbon-icons-svelte/lib/SettingsAdjust20';
import { leftPanelActions, leftPanelTypes } from './constants';
const navMenu = [
{
@ -45,13 +45,15 @@
/*
Format:
action: will be place on the aria-label, use to identify the action from the others.
action: will be place on the aria-label, use to identify the action from the others. Predifined actions: 'search', 'help', 'notification', 'account', 'switcher'
type: search-> will open an inputText to start a search.
component-> when specifying this property, content will expect a .svelte file.
link-> when specifying this property, content will expect for subject objects that will contain href and link.
link-> when specifying this property, content will expect for just 1 href and text
links-> when specifying this property, content will expect for an array of objects with property subject and another array of objects with href and text
icon: you need to specify the properties specified in carbon-icons-svelte components as shown in the example below.
@ -61,29 +63,79 @@
*/
const rightPanel = [
{
action: 'search'
action: leftPanelActions.search.actionString,
type: leftPanelTypes.search,
isVisible: true
},
{
action: 'userlogin',
type: 'component',
{
action: 'customsettings',
type: leftPanelTypes.component,
icon: [
{
class: undefined,
skeleton: false,
render: Notification20,
title: 'Notification20',
render: SettingsAdjust20,
title: 'settings',
tabIndex: 0,
focusable: false,
style: undefined
}
],
content: FormTest
content: FormTest,
isVisible: true
},
{
action: 'switcher',
action: leftPanelActions.help.actionString,
type: leftPanelTypes.link,
content: {
href: '#',
text: leftPanelActions.help.actionString
},
isVisible: true
},
{
action: leftPanelActions.notifications.actionString,
type: leftPanelTypes.links,
content: [
{
subjet: 'Switcher theme 1',
subjet: 'Notification subjet 1',
items: [
{
href: '#',
text: 'Notification item 1'
}
]
},
{
subjet: 'Notification subjet 2',
items: [
{
href: '#',
text: 'Notification item 1'
},
{
href: '#',
text: 'Notification item 2'
},
{
href: '#',
text: 'Notification item 3'
},
{
href: '#',
text: 'Notification item 4'
}
]
}
],
isVisible: true
},
{
action: leftPanelActions.switcher.actionString,
type: leftPanelTypes.links,
content: [
{
subjet: 'Switcher subjet 1',
items: [
{
href: '#',
@ -92,7 +144,7 @@
]
},
{
subjet: 'Switcher theme 2',
subjet: 'Switcher subjet 2',
items: [
{
href: '#',
@ -113,7 +165,7 @@
]
},
{
subjet: 'Switcher theme 3',
subjet: 'Switcher subjet 3',
items: [
{
href: '#',
@ -125,23 +177,17 @@
}
]
}
]
],
isVisible: true
},
{
action: 'userlogin',
type: 'component',
icon: [
{
class: undefined,
skeleton: false,
render: UserAvatar20,
title: 'UserAvatar20',
tabIndex: 0,
focusable: false,
style: undefined
}
],
content: FormTest
action: leftPanelActions.account.actionString,
type: leftPanelTypes.link,
content: {
href: '#',
text: leftPanelActions.account.actionString
},
isVisible: true
}
];
</script>

View file

@ -11,7 +11,7 @@
import UIShellNavItem from './UIShellNav/UIShellNavItem.svelte';
import UIShellRightPanel from './UIShellRightPanel/UIShellRightPanel.svelte';
$: ariaLabel = 'IBM ' + (uiShellAriaLabel || $$props['aria-label'] || platformName);
$: ariaLabel = company + (uiShellAriaLabel || $$props['aria-label'] || platformName);
</script>
<header aria-label={ariaLabel} class={cx('--header')} role="banner">

View file

@ -11,14 +11,14 @@
let listItemSubMenu = undefined;
window.addEventListener('mouseup', ({ target }) => {
if (target !== listItemSubMenu) {
if (expanded) {
console.log('entered first if');
expanded = false;
if (listItemSubMenu) {
if (listItemSubMenu.contains(target) || target === listItemSubMenu) {
expanded = !expanded;
} else {
if (expanded) {
expanded = false;
}
}
} else if (listItemSubMenu.contains(target) || target === listItemSubMenu) {
console.log('entered second if');
expanded = !expanded;
}
});
</script>

View file

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

View file

@ -0,0 +1,195 @@
<script>
export let action = undefined;
import { cx } from '../../../lib';
import ActionLink from './ActionLink.svelte';
import ActionComponent from './ActionComponent.svelte';
import ActionSearch from './ActionSearch.svelte';
import { leftPanelTypes, actionSearchWhiteList } from '../constants';
let typeComponent = undefined;
let componentIsActive = false;
let typeSearch = undefined;
let searchIsActive = false;
let typeLink = undefined;
let linkIsActive = false;
let isSearchFocus = false;
window.addEventListener('mouseup', ({ target }) => {
checkForClicks(target, typeComponent, leftPanelTypes.component);
checkForClicksTypeSearch(target, typeSearch, leftPanelTypes.search);
checkForClicks(target, typeLink, leftPanelTypes.link);
});
function checkForClicks(target, component, actionString) {
try {
if (component && target) {
if (component.contains(target) || target === component) {
if (actionString === leftPanelTypes.component) {
componentIsActive = !componentIsActive;
} else if (actionString === leftPanelTypes.link) {
linkIsActive = !linkIsActive;
}
} else {
if (actionString === leftPanelTypes.component) {
if (componentIsActive) {
componentIsActive = false;
}
} else if (actionString === leftPanelTypes.link) {
if (linkIsActive) {
linkIsActive = false;
}
}
}
}
} catch (error) {}
}
function checkForClicksTypeSearch(target, component, actionString) {
let parentFirstLine;
let parentSecondLine;
if (component && target) {
try {
if (
target.id !== 'right-panel-action-search' &&
target.parentNode.id !== 'right-panel-action-search' &&
target.parentNode.parentNode.id !== 'right-panel-action-search'
) {
if (component.contains(target) || target === component) {
if (actionString === leftPanelTypes.component) {
componentIsActive = !componentIsActive;
} else if (actionString === leftPanelTypes.search) {
searchIsActive = !searchIsActive;
} else if (actionString === leftPanelTypes.link) {
linkIsActive = !linkIsActive;
}
} else {
if (actionString === leftPanelTypes.component) {
if (componentIsActive) {
componentIsActive = false;
}
} else if (actionString === leftPanelTypes.search) {
if (searchIsActive) {
searchIsActive = false;
}
} else if (actionString === leftPanelTypes.link) {
if (linkIsActive) {
linkIsActive = false;
}
}
}
} else {
if (actionString === leftPanelTypes.component) {
if (!componentIsActive && target.id !== 'right-panel-close-search') {
componentIsActive = true;
} else if (
componentIsActive &&
(target.id === 'right-panel-close-search' ||
target.parentNode.id === 'right-panel-close-search')
) {
componentIsActive = false;
}
} else if (actionString === leftPanelTypes.search) {
if (!searchIsActive && target.id !== 'right-panel-close-search') {
searchIsActive = true;
} else if (
searchIsActive &&
(target.id === 'right-panel-close-search' ||
target.parentNode.id === 'right-panel-close-search')
) {
searchIsActive = false;
}
} else if (actionString === leftPanelTypes.link) {
if (!linkIsActive && target.id !== 'right-panel-close-search') {
linkIsActive = true;
} else if (
linkIsActive &&
(target.id === 'right-panel-close-search' ||
target.parentNode.id === 'right-panel-close-search')
) {
linkIsActive = false;
}
}
}
} catch (error) {
if (actionString === leftPanelTypes.component) {
if (componentIsActive) {
componentIsActive = false;
}
} else if (actionString === leftPanelTypes.search) {
if (searchIsActive) {
searchIsActive = false;
}
} else if (actionString === leftPanelTypes.link) {
if (linkIsActive) {
linkIsActive = false;
}
}
}
}
}
</script>
<style>
.search-wrapper {
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);
}
.search-wrapper-hidden {
max-width: 3rem;
background-color: #161616;
}
.search-focus {
outline: 2px solid #fff;
outline-offset: -2px;
}
</style>
{#if action.type === leftPanelTypes.component}
<div bind:this={typeComponent}>
<ActionComponent
action={action.action}
icon={action.icon ? action.icon : undefined}
content={action.content}
bind:componentIsActive />
</div>
{:else if action.type === leftPanelTypes.search}
<div
bind:this={typeSearch}
class="search-wrapper"
class:search-wrapper-hidden={!searchIsActive}
class:search-focus={isSearchFocus || searchIsActive}
role="search">
<ActionSearch
action={action.action}
icon={action.icon ? action.icon : undefined}
content={action.content}
bind:searchIsActive
focusInputSearch={() => {
isSearchFocus = true;
}}
focusOutInputSearch={() => {
isSearchFocus = false;
}} />
</div>
{:else if action.type === leftPanelTypes.link || action.type === leftPanelTypes.links}
<div bind:this={typeLink}>
<ActionLink
action={action.action}
icon={action.icon ? action.icon : undefined}
content={action.content}
bind:linkIsActive />
</div>
{/if}

View file

@ -1,38 +1,68 @@
<script>
export let action = undefined;
// export let type = undefined;
// export let icon = undefined;
export let type = undefined;
export let icon = undefined;
// export let content = undefined;
export let linkIsActive = undefined;
// import { onMount } from 'svelte';
import { cx } from '../../../lib';
import AppSwitcher20 from 'carbon-icons-svelte/lib/AppSwitcher20';
import Icon from '../../Icon/Icon.svelte';
import { leftPanelActions } from '../constants';
import { slide } from 'svelte/transition';
console.log(action);
let skeleton = icon ? icon.skeleton : false;
$: switcherIconProps =
action === 'switcher'
? (switcherIconProps = [
{
class: undefined,
skeleton: false,
render: AppSwitcher20,
title: 'Switcher',
tabIndex: 0,
focusable: false,
style: undefined
if (!icon) {
const actionsArray = Object.entries(leftPanelActions);
for (const definedAction of actionsArray) {
for (const content of definedAction) {
if (typeof content === 'object') {
if (content.actionString === action) {
icon = content.iconProps;
}
])
: (switcherIconProps = []);
}
}
}
}
</script>
{#if action === 'switcher'}
<button aria-label="Notifications" class={cx('--header__action')} type="button">
<Icon {...switcherIconProps} render={switcherIconProps[0].render} />
{#if action === leftPanelActions.switcher.actionString}
<button
aria-label={type}
class={cx('--header__action', linkIsActive && '--header__action--active')}
type="button">
<Icon {...icon} render={icon.render} />
</button>
{:else}
<!-- <button aria-label="Notifications" class={cx('--header__action')} type="button">
<Icon {...icon} render={icon[0].render} />
</button> -->
<button
aria-label={type}
class={cx('--header__action', linkIsActive && '--header__action--active')}
type="button">
<Icon {...icon} render={icon.render} />
</button>
{/if}
{#if linkIsActive}
<div class="bx--header-panel bx--header-panel--expanded" transition:slide="{{duration: 200}}">
<ul class="bx--switcher__item">
<li class="bx--switcher__item">
<a class="bx--switcher__item-link bx--switcher__item-link--selected" href="/">Link</a>
</li>
<li class="bx--switcher__item">
<a class="bx--switcher__item-link" href="/">Link</a>
</li>
<li class="bx--switcher__item">
<a class="bx--switcher__item-link" href="/">Link</a>
</li>
<li class="bx--switcher__item">
<a class="bx--switcher__item-link" href="/">Link</a>
</li>
<li class="bx--switcher__item">
<a class="bx--switcher__item-link" href="/">Link</a>
</li>
<li class="bx--switcher__item">
<a class="bx--switcher__item-link" href="/">Link</a>
</li>
</ul>
</div>
{/if}

View file

@ -1,36 +1,136 @@
<script>
export let action = undefined;
// export let type = undefined;
export let icon = undefined;
// export let content = undefined;
export let searchIsActive = undefined;
// import { onMount } from 'svelte';
import { createEventDispatcher } from 'svelte';
import { cx } from '../../../lib';
import Search20 from 'carbon-icons-svelte/lib/Search20';
import Icon from '../../Icon/Icon.svelte';
import { leftPanelActions, closeIcon } from '../constants';
$: searchIconProps =
action === 'search'
? (searchIconProps = [
{
class: undefined,
skeleton: false,
render: Search20,
title: 'Search',
tabIndex: 0,
focusable: false,
style: undefined
// let searchIconProps = undefined;
let searchTabIndex = 0;
let closeTabIndex = -1;
const dispatch = createEventDispatcher();
if (!icon) {
const actionsArray = Object.entries(leftPanelActions);
for (const definedAction of actionsArray) {
for (const content of definedAction) {
if (typeof content === 'object') {
if (content.actionString === action) {
icon = content.iconProps;
}
])
: (searchIconProps = []);
}
}
}
}
$: if (searchIsActive) {
searchTabIndex = -1;
closeTabIndex = 0;
} else {
searchTabIndex = 0;
closeTabIndex = -1;
}
</script>
{#if action === 'search'}
<button aria-label="Notifications" class={cx('--header__action')} type="button">
<Icon {...searchIconProps} render={searchIconProps[0].render} />
<style>
.search-wrapper {
display: flex;
flex-grow: 1;
border-bottom: 1px solid #393939;
}
.btn-search {
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);
}
.btn-search-disabled {
border: none;
pointer-events: none;
}
.input-search {
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;
width: 100%;
height: 3rem;
padding: 0;
transition: opacity 0.11s cubic-bezier(0.2, 0, 0.38, 0.9);
}
.input-hidden {
opacity: 0;
pointer-events: none;
}
.btn-clear {
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);
}
.btn-clear:hover {
background-color: #4c4c4c;
}
.btn-clear-hidden {
opacity: 0;
}
</style>
<div
id="right-panel-action-search"
class="search-wrapper"
role="combobox"
aria-expanded={searchIsActive}>
<button
tabindex={searchTabIndex}
aria-label={action}
class={cx('--header__action')}
class:btn-search={true}
class:btn-search-disabled={searchIsActive}
on:click={() => dispatch('focusInputSearch')}
type="button">
<Icon {...icon} render={icon.render} />
</button>
{:else}
<button aria-label="Notifications" class={cx('--header__action')} type="button">
<Icon {...icon} render={icon[0].render} />
<input
type="text"
autocomplete="off"
tabindex={closeTabIndex}
class="input-search"
class:input-hidden={!searchIsActive}
placeholder="Search"
on:focus={() => dispatch('focusInputSearch')}
on:focusout={() => dispatch('focusOutInputSearch')} />
<button
id="right-panel-close-search"
tabindex={closeTabIndex}
class={cx('--header__action')}
class:btn-clear={true}
class:btn-clear-hidden={!searchIsActive}
type="button"
aria-label="Clear search">
<Icon {...closeIcon} render={closeIcon.render} />
</button>
{/if}
</div>

View file

@ -1,51 +1,46 @@
<script>
export let rightPanel = undefined;
import { onMount } from 'svelte';
import { cx } from '../../../lib';
import ActionSearch from './ActionSearch.svelte';
import ActionComponent from './ActionComponent.svelte';
import ActionLink from './ActionLink.svelte';
import ActionGeneric from './ActionGeneric.svelte';
import { leftPanelActions } from '../constants';
// let switcherAtLast = [];
// let renderSwitcher = false;
let orderedRightPanel = [];
let customActions = 1;
$: switcherAtLast = rightPanel.map((item, index, array) => {
if (item.action === 'switcher') {
if (index !== rightPanel.length) {
const newItem = array[index];
return newItem;
rightPanel.forEach((item, index) => {
orderedRightPanel[index] = undefined;
if (item.action) {
if (Object.keys(leftPanelActions).indexOf(item.action.charAt(0).toLowerCase() + item.action.slice(1)) === -1) {
orderedRightPanel[customActions] = rightPanel[index];
customActions += 1;
}
} else {
return undefined;
}
});
$: renderSwitcher = switcherAtLast ? true : false;
rightPanel.forEach((item, index) => {
if (item.action) {
if (item.action === leftPanelActions.search.actionString) {
orderedRightPanel[0] = rightPanel[index];
} else if (item.action === leftPanelActions.help.actionString) {
orderedRightPanel[customActions] = rightPanel[index];
} else if (item.action === leftPanelActions.notifications.actionString) {
orderedRightPanel[customActions + 1] = rightPanel[index];
} else if (item.action === leftPanelActions.account.actionString) {
orderedRightPanel[customActions + 2] = rightPanel[index];
} else if (item.action === leftPanelActions.switcher.actionString) {
orderedRightPanel[customActions + 3] = rightPanel[index];
}
}
});
onMount(()=>{
console.log(switcherAtLast);
console.log(renderSwitcher);
})
orderedRightPanel = orderedRightPanel.filter(item => {
return item;
});
</script>
<div class={cx('--header__global')}>
{#each rightPanel as action, index}
{#if action.action === 'search'}
<ActionSearch {...action} />
{:else if action.action === 'switcher'}
{#if index === rightPanel.length}
<ActionLink {...action} />
{/if}
{:else if action.type === 'search'}
<ActionSearch {...action} />
{:else if action.type === 'component'}
<ActionComponent {...action} />
{:else if action.type === 'link'}
<ActionLink {...action} />
{/if}
{#each orderedRightPanel as action, index}
<ActionGeneric {action} />
{/each}
{#if renderSwitcher}
<ActionLink {...switcherAtLast} />
{/if}
</div>

View file

@ -0,0 +1,86 @@
import Search20 from 'carbon-icons-svelte/lib/Search20';
import Help20 from 'carbon-icons-svelte/lib/Help20';
import Notification20 from 'carbon-icons-svelte/lib/Notification20';
import UserAvatar20 from 'carbon-icons-svelte/lib/UserAvatar20';
import AppSwitcher20 from 'carbon-icons-svelte/lib/AppSwitcher20';
import Close20 from 'carbon-icons-svelte/lib/Close20';
export const leftPanelActions = {
search: {
actionString: 'Search',
iconProps: {
class: undefined,
skeleton: false,
render: Search20,
title: 'Search',
tabIndex: 0,
focusable: false,
style: undefined
}
},
help: {
actionString: 'Help',
iconProps: {
class: undefined,
skeleton: false,
render: Help20,
title: 'Help',
tabIndex: 0,
focusable: false,
style: undefined
}
},
notifications: {
actionString: 'Notifications',
iconProps: {
class: undefined,
skeleton: false,
render: Notification20,
title: 'Notifications',
tabIndex: 0,
focusable: false,
style: undefined
}
},
account: {
actionString: 'Account',
iconProps: {
class: undefined,
skeleton: false,
render: UserAvatar20,
title: 'Account',
tabIndex: 0,
focusable: false,
style: undefined
}
},
switcher: {
actionString: 'Switcher',
iconProps: {
class: undefined,
skeleton: false,
render: AppSwitcher20,
title: 'App Switcher',
tabIndex: 0,
focusable: false,
style: undefined
}
}
};
export const leftPanelTypes = {
search: 'Search',
component: 'Component',
link: 'Link',
links: 'Links'
};
export const closeIcon = {
class: undefined,
skeleton: false,
render: Close20,
title: 'Close',
tabIndex: 0,
focusable: false,
style: undefined
};