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
ce469df6db
commit
93b912240f
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;
|
|
@ -78,6 +78,7 @@ import ToggleSmall, { ToggleSmallSkeleton } from './components/ToggleSmall';
|
|||
import Tooltip from './components/Tooltip';
|
||||
import TooltipDefinition from './components/TooltipDefinition';
|
||||
import TooltipIcon from './components/TooltipIcon';
|
||||
import UIShell from './components/UIShell';
|
||||
import UnorderedList from './components/UnorderedList';
|
||||
|
||||
export {
|
||||
|
@ -192,5 +193,6 @@ export {
|
|||
Tooltip,
|
||||
TooltipDefinition,
|
||||
TooltipIcon,
|
||||
UIShell,
|
||||
UnorderedList
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue