mirror of
https://github.com/carbon-design-system/carbon-components-svelte.git
synced 2025-09-15 10:21:05 +00:00
first commit on the UIShell
This commit is contained in:
parent
0bdae666c4
commit
d3c2732c4c
13 changed files with 492 additions and 0 deletions
3
src/components/UIShell/FormTest.svelte
Normal file
3
src/components/UIShell/FormTest.svelte
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<script>
|
||||||
|
|
||||||
|
</script>
|
155
src/components/UIShell/UIShell.Story.svelte
Normal file
155
src/components/UIShell/UIShell.Story.svelte
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
<script>
|
||||||
|
export let story = undefined;
|
||||||
|
|
||||||
|
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';
|
||||||
|
|
||||||
|
const navMenu = [
|
||||||
|
{
|
||||||
|
href: '#',
|
||||||
|
text: 'Link 1',
|
||||||
|
subMenu: undefined
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '#',
|
||||||
|
text: 'Link 2',
|
||||||
|
subMenu: undefined
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '#',
|
||||||
|
text: 'Link 3',
|
||||||
|
subMenu: undefined
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: undefined,
|
||||||
|
text: 'Link 4',
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
href: '#',
|
||||||
|
text: 'Sub-link 1'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '#',
|
||||||
|
text: 'Sub-link 2'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '#',
|
||||||
|
text: 'Sub-link 3'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
/*
|
||||||
|
Format:
|
||||||
|
|
||||||
|
action: will be place on the aria-label, use to identify the action from the others.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
icon: you need to specify the properties specified in carbon-icons-svelte components as shown in the example below.
|
||||||
|
|
||||||
|
content: what will be shown on the component, could vary the information depending on the type property.
|
||||||
|
|
||||||
|
*** actions 'search' and 'switcher' doesnt need to specify type and icon because those are predefined actions in the component.
|
||||||
|
*/
|
||||||
|
const rightPanel = [
|
||||||
|
{
|
||||||
|
action: 'search'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action: 'userlogin',
|
||||||
|
type: 'component',
|
||||||
|
icon: [
|
||||||
|
{
|
||||||
|
class: undefined,
|
||||||
|
skeleton: false,
|
||||||
|
render: Notification20,
|
||||||
|
title: 'Notification20',
|
||||||
|
tabIndex: 0,
|
||||||
|
focusable: false,
|
||||||
|
style: undefined
|
||||||
|
}
|
||||||
|
],
|
||||||
|
content: FormTest
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action: 'switcher',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
subjet: 'Switcher theme 1',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
href: '#',
|
||||||
|
text: 'Switcher item 1'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subjet: 'Switcher theme 2',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
href: '#',
|
||||||
|
text: 'Switcher item 1'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '#',
|
||||||
|
text: 'Switcher item 2'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '#',
|
||||||
|
text: 'Switcher item 3'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '#',
|
||||||
|
text: 'Switcher item 4'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subjet: 'Switcher theme 3',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
href: '#',
|
||||||
|
text: 'Switcher item 1'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '#',
|
||||||
|
text: 'Switcher item 2'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action: 'userlogin',
|
||||||
|
type: 'component',
|
||||||
|
icon: [
|
||||||
|
{
|
||||||
|
class: undefined,
|
||||||
|
skeleton: false,
|
||||||
|
render: UserAvatar20,
|
||||||
|
title: 'UserAvatar20',
|
||||||
|
tabIndex: 0,
|
||||||
|
focusable: false,
|
||||||
|
style: undefined
|
||||||
|
}
|
||||||
|
],
|
||||||
|
content: FormTest
|
||||||
|
}
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if story === 'with-nav'}
|
||||||
|
<UIShell {...$$props} {navMenu} />
|
||||||
|
{:else if story === 'with-actions'}
|
||||||
|
<UIShell {...$$props} {rightPanel} />
|
||||||
|
{:else}
|
||||||
|
<UIShell {...$$props} />
|
||||||
|
{/if}
|
34
src/components/UIShell/UIShell.stories.js
Normal file
34
src/components/UIShell/UIShell.stories.js
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import { withKnobs, text } from '@storybook/addon-knobs';
|
||||||
|
import Component from './UIShell.Story.svelte';
|
||||||
|
|
||||||
|
export default { title: 'UIShell', decorators: [withKnobs] };
|
||||||
|
|
||||||
|
export const Default = () => ({
|
||||||
|
Component,
|
||||||
|
props: {
|
||||||
|
story: 'default',
|
||||||
|
href: text('The link href (href)', '#'),
|
||||||
|
company: text('Company name', 'IBM'),
|
||||||
|
platformName: text('Platform name', 'Platform Name')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const WithNav = () => ({
|
||||||
|
Component,
|
||||||
|
props: {
|
||||||
|
story: 'with-nav',
|
||||||
|
href: text('The link href (href)', '#'),
|
||||||
|
company: text('Company name', 'IBM'),
|
||||||
|
platformName: text('Platform name', 'Platform Name')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const WithActions = () => ({
|
||||||
|
Component,
|
||||||
|
props: {
|
||||||
|
story: 'with-actions',
|
||||||
|
href: text('The link href (href)', '#'),
|
||||||
|
company: text('Company name', 'IBM'),
|
||||||
|
platformName: text('Platform name', 'Platform Name')
|
||||||
|
}
|
||||||
|
});
|
32
src/components/UIShell/UIShell.svelte
Normal file
32
src/components/UIShell/UIShell.svelte
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
<script>
|
||||||
|
export let uiShellAriaLabel = undefined;
|
||||||
|
export let href = undefined;
|
||||||
|
export let company = undefined;
|
||||||
|
export let platformName = undefined;
|
||||||
|
export let navMenu = undefined;
|
||||||
|
export let rightPanel = undefined;
|
||||||
|
|
||||||
|
import { cx } from '../../lib';
|
||||||
|
import UIShellNavWrapper from './UIShellNav/UIShellNavWrapper.svelte';
|
||||||
|
import UIShellNavItem from './UIShellNav/UIShellNavItem.svelte';
|
||||||
|
import UIShellRightPanel from './UIShellRightPanel/UIShellRightPanel.svelte';
|
||||||
|
|
||||||
|
$: ariaLabel = 'IBM ' + (uiShellAriaLabel || $$props['aria-label'] || platformName);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<header aria-label={ariaLabel} class={cx('--header')} role="banner">
|
||||||
|
<a class={cx('--header__name')} {href}>
|
||||||
|
<span class={cx('--header__name--prefix')}>{company}</span>
|
||||||
|
{platformName}
|
||||||
|
</a>
|
||||||
|
{#if navMenu}
|
||||||
|
<UIShellNavWrapper ariaLabel>
|
||||||
|
{#each navMenu as itemMenu}
|
||||||
|
<UIShellNavItem {...itemMenu} />
|
||||||
|
{/each}
|
||||||
|
</UIShellNavWrapper>
|
||||||
|
{/if}
|
||||||
|
{#if rightPanel}
|
||||||
|
<UIShellRightPanel {rightPanel} />
|
||||||
|
{/if}
|
||||||
|
</header>
|
41
src/components/UIShell/UIShellNav/UIShellNavItem.svelte
Normal file
41
src/components/UIShell/UIShellNav/UIShellNavItem.svelte
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<script>
|
||||||
|
export let href = undefined;
|
||||||
|
export let text = undefined;
|
||||||
|
export let subMenu = undefined;
|
||||||
|
|
||||||
|
import { cx } from '../../../lib';
|
||||||
|
import UIShellSubmenu from './UIShellNavSubmenu.svelte';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if href}
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
class={cx('--header__menu-item')}
|
||||||
|
role="menuitem"
|
||||||
|
tabindex="0"
|
||||||
|
on:click
|
||||||
|
on:mouseover
|
||||||
|
on:mouseenter
|
||||||
|
on:mouseleave
|
||||||
|
on:keyup
|
||||||
|
on:keydown
|
||||||
|
on:focus
|
||||||
|
on:blur
|
||||||
|
{href}>
|
||||||
|
<span class={cx('--text-truncate--end')}>{text}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{:else}
|
||||||
|
<UIShellSubmenu
|
||||||
|
{href}
|
||||||
|
{text}
|
||||||
|
{subMenu}
|
||||||
|
on:click
|
||||||
|
on:mouseover
|
||||||
|
on:mouseenter
|
||||||
|
on:mouseleave
|
||||||
|
on:keyup
|
||||||
|
on:keydown
|
||||||
|
on:focus
|
||||||
|
on:blur />
|
||||||
|
{/if}
|
73
src/components/UIShell/UIShellNav/UIShellNavSubmenu.svelte
Normal file
73
src/components/UIShell/UIShellNav/UIShellNavSubmenu.svelte
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
<script>
|
||||||
|
export let href = undefined;
|
||||||
|
export let text = undefined;
|
||||||
|
export let subMenu = undefined;
|
||||||
|
export let iconDescription = 'Expand/Collapse';
|
||||||
|
export let expanded = false;
|
||||||
|
|
||||||
|
import ChevronDown16 from 'carbon-icons-svelte/lib/ChevronDown16';
|
||||||
|
import { cx } from '../../../lib';
|
||||||
|
|
||||||
|
let listItemSubMenu = undefined;
|
||||||
|
|
||||||
|
window.addEventListener('mouseup', ({ target }) => {
|
||||||
|
if (target !== listItemSubMenu) {
|
||||||
|
if (expanded) {
|
||||||
|
console.log('entered first if');
|
||||||
|
expanded = false;
|
||||||
|
}
|
||||||
|
} else if (listItemSubMenu.contains(target) || target === listItemSubMenu) {
|
||||||
|
console.log('entered second if');
|
||||||
|
expanded = !expanded;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<li class={cx('--header__submenu')} title={iconDescription}>
|
||||||
|
<a
|
||||||
|
bind:this={listItemSubMenu}
|
||||||
|
aria-haspopup="menu"
|
||||||
|
aria-expanded={expanded}
|
||||||
|
class={cx('--header__menu-item', '--header__menu-title')}
|
||||||
|
role="menuitem"
|
||||||
|
tabindex="0"
|
||||||
|
aria-label={text}
|
||||||
|
href="javascript:void(0)"
|
||||||
|
on:keydown
|
||||||
|
on:keydown={({ key }) => {
|
||||||
|
if (key === 'Enter') {
|
||||||
|
expanded = !expanded;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
on:click
|
||||||
|
on:mouseover
|
||||||
|
on:mouseenter
|
||||||
|
on:mouseleave
|
||||||
|
on:keyup
|
||||||
|
on:focus
|
||||||
|
on:blur>
|
||||||
|
{text}
|
||||||
|
<ChevronDown16 class={cx('--header__menu-arrow')} aria-label={iconDescription} />
|
||||||
|
</a>
|
||||||
|
<ul aria-label={href} class={cx('--header__menu')} role="menu">
|
||||||
|
{#each subMenu as item}
|
||||||
|
<li role="none">
|
||||||
|
<a
|
||||||
|
href={item.href}
|
||||||
|
class={cx('--header__menu-item')}
|
||||||
|
role="menuitem"
|
||||||
|
tabindex="0"
|
||||||
|
on:click
|
||||||
|
on:mouseover
|
||||||
|
on:mouseenter
|
||||||
|
on:mouseleave
|
||||||
|
on:keyup
|
||||||
|
on:keydown
|
||||||
|
on:focus
|
||||||
|
on:blur>
|
||||||
|
<span class={cx('--text-truncate--end')}>{item.text}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
</li>
|
11
src/components/UIShell/UIShellNav/UIShellNavWrapper.svelte
Normal file
11
src/components/UIShell/UIShellNav/UIShellNavWrapper.svelte
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<script>
|
||||||
|
export let ariaLabel = undefined;
|
||||||
|
|
||||||
|
import { cx } from '../../../lib';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<nav aria-label={ariaLabel} class={cx('--header__nav')}>
|
||||||
|
<ul aria-label={ariaLabel} class={cx('--header__menu-bar')} role="menubar">
|
||||||
|
<slot />
|
||||||
|
</ul>
|
||||||
|
</nav>
|
|
@ -0,0 +1,13 @@
|
||||||
|
<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';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<button aria-label="Notifications" class={cx('--header__action')} type="button">
|
||||||
|
<Icon {...icon} render={icon[0].render} />
|
||||||
|
</button>
|
38
src/components/UIShell/UIShellRightPanel/ActionLink.svelte
Normal file
38
src/components/UIShell/UIShellRightPanel/ActionLink.svelte
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<script>
|
||||||
|
export let action = undefined;
|
||||||
|
// export let type = undefined;
|
||||||
|
// export let icon = undefined;
|
||||||
|
// export let content = undefined;
|
||||||
|
|
||||||
|
// import { onMount } from 'svelte';
|
||||||
|
import { cx } from '../../../lib';
|
||||||
|
import AppSwitcher20 from 'carbon-icons-svelte/lib/AppSwitcher20';
|
||||||
|
import Icon from '../../Icon/Icon.svelte';
|
||||||
|
|
||||||
|
console.log(action);
|
||||||
|
|
||||||
|
$: switcherIconProps =
|
||||||
|
action === 'switcher'
|
||||||
|
? (switcherIconProps = [
|
||||||
|
{
|
||||||
|
class: undefined,
|
||||||
|
skeleton: false,
|
||||||
|
render: AppSwitcher20,
|
||||||
|
title: 'Switcher',
|
||||||
|
tabIndex: 0,
|
||||||
|
focusable: false,
|
||||||
|
style: undefined
|
||||||
|
}
|
||||||
|
])
|
||||||
|
: (switcherIconProps = []);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if action === 'switcher'}
|
||||||
|
<button aria-label="Notifications" class={cx('--header__action')} type="button">
|
||||||
|
<Icon {...switcherIconProps} render={switcherIconProps[0].render} />
|
||||||
|
</button>
|
||||||
|
{:else}
|
||||||
|
<!-- <button aria-label="Notifications" class={cx('--header__action')} type="button">
|
||||||
|
<Icon {...icon} render={icon[0].render} />
|
||||||
|
</button> -->
|
||||||
|
{/if}
|
36
src/components/UIShell/UIShellRightPanel/ActionSearch.svelte
Normal file
36
src/components/UIShell/UIShellRightPanel/ActionSearch.svelte
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<script>
|
||||||
|
export let action = undefined;
|
||||||
|
// export let type = undefined;
|
||||||
|
export let icon = undefined;
|
||||||
|
// export let content = undefined;
|
||||||
|
|
||||||
|
// import { onMount } from 'svelte';
|
||||||
|
import { cx } from '../../../lib';
|
||||||
|
import Search20 from 'carbon-icons-svelte/lib/Search20';
|
||||||
|
import Icon from '../../Icon/Icon.svelte';
|
||||||
|
|
||||||
|
$: searchIconProps =
|
||||||
|
action === 'search'
|
||||||
|
? (searchIconProps = [
|
||||||
|
{
|
||||||
|
class: undefined,
|
||||||
|
skeleton: false,
|
||||||
|
render: Search20,
|
||||||
|
title: 'Search',
|
||||||
|
tabIndex: 0,
|
||||||
|
focusable: false,
|
||||||
|
style: undefined
|
||||||
|
}
|
||||||
|
])
|
||||||
|
: (searchIconProps = []);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if action === 'search'}
|
||||||
|
<button aria-label="Notifications" class={cx('--header__action')} type="button">
|
||||||
|
<Icon {...searchIconProps} render={searchIconProps[0].render} />
|
||||||
|
</button>
|
||||||
|
{:else}
|
||||||
|
<button aria-label="Notifications" class={cx('--header__action')} type="button">
|
||||||
|
<Icon {...icon} render={icon[0].render} />
|
||||||
|
</button>
|
||||||
|
{/if}
|
|
@ -0,0 +1,51 @@
|
||||||
|
<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';
|
||||||
|
|
||||||
|
// let switcherAtLast = [];
|
||||||
|
// let renderSwitcher = false;
|
||||||
|
|
||||||
|
$: switcherAtLast = rightPanel.map((item, index, array) => {
|
||||||
|
if (item.action === 'switcher') {
|
||||||
|
if (index !== rightPanel.length) {
|
||||||
|
const newItem = array[index];
|
||||||
|
return newItem;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$: renderSwitcher = switcherAtLast ? true : false;
|
||||||
|
|
||||||
|
onMount(()=>{
|
||||||
|
console.log(switcherAtLast);
|
||||||
|
console.log(renderSwitcher);
|
||||||
|
})
|
||||||
|
</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}
|
||||||
|
{#if renderSwitcher}
|
||||||
|
<ActionLink {...switcherAtLast} />
|
||||||
|
{/if}
|
||||||
|
</div>
|
3
src/components/UIShell/index.js
Normal file
3
src/components/UIShell/index.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import UIShell from './UIShell.svelte';
|
||||||
|
|
||||||
|
export default UIShell;
|
|
@ -82,6 +82,7 @@ import ToggleSmall, { ToggleSmallSkeleton } from './components/ToggleSmall';
|
||||||
import Tooltip from './components/Tooltip';
|
import Tooltip from './components/Tooltip';
|
||||||
import TooltipDefinition from './components/TooltipDefinition';
|
import TooltipDefinition from './components/TooltipDefinition';
|
||||||
import TooltipIcon from './components/TooltipIcon';
|
import TooltipIcon from './components/TooltipIcon';
|
||||||
|
import UIShell from './components/UIShell';
|
||||||
import UnorderedList from './components/UnorderedList';
|
import UnorderedList from './components/UnorderedList';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
@ -193,5 +194,6 @@ export {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
TooltipDefinition,
|
TooltipDefinition,
|
||||||
TooltipIcon,
|
TooltipIcon,
|
||||||
|
UIShell,
|
||||||
UnorderedList
|
UnorderedList
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue