mirror of
https://github.com/carbon-design-system/carbon-components-svelte.git
synced 2025-09-15 10:21:05 +00:00
commit
396386ab68
24 changed files with 3217 additions and 567 deletions
22
package.json
22
package.json
|
@ -23,14 +23,14 @@
|
|||
"carbon-icons-svelte": "^10.8.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.7.4",
|
||||
"@babel/preset-env": "^7.7.4",
|
||||
"@commitlint/cli": "^8.2.0",
|
||||
"@commitlint/config-conventional": "^8.2.0",
|
||||
"@storybook/addon-knobs": "^5.2.6",
|
||||
"@storybook/addon-storysource": "^5.2.6",
|
||||
"@storybook/cli": "^5.2.6",
|
||||
"@storybook/svelte": "^5.2.6",
|
||||
"@babel/core": "^7.8.0",
|
||||
"@babel/preset-env": "^7.8.0",
|
||||
"@commitlint/cli": "^8.3.4",
|
||||
"@commitlint/config-conventional": "^8.3.4",
|
||||
"@storybook/addon-knobs": "^5.3.1",
|
||||
"@storybook/addon-storysource": "^5.3.1",
|
||||
"@storybook/cli": "^5.3.1",
|
||||
"@storybook/svelte": "^5.3.1",
|
||||
"@testing-library/jest-dom": "^4.2.4",
|
||||
"@testing-library/svelte": "^1.11.0",
|
||||
"babel-jest": "^24.9.0",
|
||||
|
@ -46,12 +46,12 @@
|
|||
"prettier-plugin-svelte": "^0.7.0",
|
||||
"pretty-quick": "^2.0.1",
|
||||
"regenerator-runtime": "^0.13.3",
|
||||
"rollup": "^1.27.13",
|
||||
"rollup": "^1.29.0",
|
||||
"rollup-plugin-commonjs": "^10.1.0",
|
||||
"rollup-plugin-node-resolve": "^5.2.0",
|
||||
"rollup-plugin-svelte": "^5.1.1",
|
||||
"rollup-plugin-terser": "^5.1.3",
|
||||
"svelte": "^3.16.4",
|
||||
"rollup-plugin-terser": "^5.2.0",
|
||||
"svelte": "^3.16.7",
|
||||
"svelte-loader": "^2.13.6"
|
||||
},
|
||||
"contributors": [
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Breadcrumb default 1`] = `"<div><div class=\\"layout svelte-q1u82c\\"><nav aria-label=\\"Breadcrumb\\"><ol class=\\"bx--breadcrumb\\"><li class=\\"bx--breadcrumb-item\\"><a class=\\"bx--link\\" href=\\"/#\\">Breadcrumb 1</a></li> <li class=\\"bx--breadcrumb-item\\"><a class=\\"bx--link\\" href=\\"#\\">Breadcrumb 2</a></li> <li class=\\"bx--breadcrumb-item\\"><a class=\\"bx--link\\" href=\\"#\\">Breadcrumb 3</a></li></ol></nav></div></div>"`;
|
||||
exports[`Breadcrumb default 1`] = `"<div><div class=\\"layout svelte-1b6lfs3\\"><nav aria-label=\\"Breadcrumb\\"><ol class=\\"bx--breadcrumb\\"><li class=\\"bx--breadcrumb-item\\"><a class=\\"bx--link\\" href=\\"/#\\">Breadcrumb 1</a></li> <li class=\\"bx--breadcrumb-item\\"><a class=\\"bx--link\\" href=\\"#\\">Breadcrumb 2</a></li> <li class=\\"bx--breadcrumb-item\\"><a class=\\"bx--link\\" href=\\"#\\">Breadcrumb 3</a></li></ol></nav></div></div>"`;
|
||||
|
||||
exports[`Breadcrumb default 2`] = `"<div><div class=\\"layout svelte-q1u82c\\"><nav aria-label=\\"Breadcrumb\\"><ol class=\\"bx--breadcrumb bx--breadcrumb--no-trailing-slash\\"><li class=\\"bx--breadcrumb-item\\"><a class=\\"bx--link\\" href=\\"/#\\">Breadcrumb 1</a></li> <li class=\\"bx--breadcrumb-item\\"><a class=\\"bx--link\\" href=\\"#\\">Breadcrumb 2</a></li> <li class=\\"bx--breadcrumb-item\\"><a class=\\"bx--link\\" href=\\"#\\">Breadcrumb 3</a></li></ol></nav></div></div>"`;
|
||||
exports[`Breadcrumb default 2`] = `"<div><div class=\\"layout svelte-1b6lfs3\\"><nav aria-label=\\"Breadcrumb\\"><ol class=\\"bx--breadcrumb bx--breadcrumb--no-trailing-slash\\"><li class=\\"bx--breadcrumb-item\\"><a class=\\"bx--link\\" href=\\"/#\\">Breadcrumb 1</a></li> <li class=\\"bx--breadcrumb-item\\"><a class=\\"bx--link\\" href=\\"#\\">Breadcrumb 2</a></li> <li class=\\"bx--breadcrumb-item\\"><a class=\\"bx--link\\" href=\\"#\\">Breadcrumb 3</a></li></ol></nav></div></div>"`;
|
||||
|
||||
exports[`Breadcrumb skeleton 1`] = `"<div><div class=\\"layout svelte-q1u82c\\"><div class=\\"bx--breadcrumb bx--skeleton\\"><div class=\\"bx--breadcrumb-item\\"><span class=\\"bx--link\\"> </span> </div><div class=\\"bx--breadcrumb-item\\"><span class=\\"bx--link\\"> </span> </div><div class=\\"bx--breadcrumb-item\\"><span class=\\"bx--link\\"> </span> </div></div></div></div>"`;
|
||||
exports[`Breadcrumb skeleton 1`] = `"<div><div class=\\"layout svelte-1b6lfs3\\"><div class=\\"bx--breadcrumb bx--skeleton\\"><div class=\\"bx--breadcrumb-item\\"><span class=\\"bx--link\\"> </span> </div><div class=\\"bx--breadcrumb-item\\"><span class=\\"bx--link\\"> </span> </div><div class=\\"bx--breadcrumb-item\\"><span class=\\"bx--link\\"> </span> </div></div></div></div>"`;
|
||||
|
|
15
src/components/UIShell/FormTest.svelte
Normal file
15
src/components/UIShell/FormTest.svelte
Normal file
|
@ -0,0 +1,15 @@
|
|||
<script>
|
||||
import TextInput from '../TextInput/TextInput.svelte';
|
||||
import Toggle from '../Toggle/Toggle.svelte';
|
||||
import Button from '../Button/Button.svelte';
|
||||
</script>
|
||||
|
||||
<label>Test Field</label>
|
||||
<br><br>
|
||||
<TextInput />
|
||||
<br><br>
|
||||
<Toggle labelText='Test toggler' />
|
||||
<br><br>
|
||||
<Button>
|
||||
Test Button
|
||||
</Button>
|
262
src/components/UIShell/UIShell.Story.svelte
Normal file
262
src/components/UIShell/UIShell.Story.svelte
Normal file
|
@ -0,0 +1,262 @@
|
|||
<script>
|
||||
export let story = undefined;
|
||||
|
||||
import UIShell from './UIShell.svelte';
|
||||
import FormTest from './FormTest.svelte';
|
||||
import SettingsAdjust20 from 'carbon-icons-svelte/lib/SettingsAdjust20';
|
||||
import { leftPanelActions, leftPanelTypes } from './constants';
|
||||
import searchStore from './searchStore';
|
||||
|
||||
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. 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 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.
|
||||
|
||||
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: leftPanelActions.search.actionString,
|
||||
type: leftPanelTypes.search,
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
action: 'customsettings',
|
||||
type: leftPanelTypes.component,
|
||||
icon: [
|
||||
{
|
||||
class: undefined,
|
||||
skeleton: false,
|
||||
render: SettingsAdjust20,
|
||||
title: 'Settings',
|
||||
tabIndex: 0,
|
||||
focusable: false,
|
||||
style: undefined
|
||||
}
|
||||
],
|
||||
content: FormTest,
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
action: leftPanelActions.help.actionString,
|
||||
type: leftPanelTypes.link,
|
||||
content: {
|
||||
href: '#',
|
||||
text: leftPanelActions.help.actionString
|
||||
},
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
action: leftPanelActions.notifications.actionString,
|
||||
type: leftPanelTypes.links,
|
||||
content: [
|
||||
{
|
||||
subject: 'Notification subject 1',
|
||||
items: [
|
||||
{
|
||||
href: '#',
|
||||
text: 'Notification item 1'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
subject: 'Notification subject 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: [
|
||||
{
|
||||
subject: 'Switcher subject 1',
|
||||
items: [
|
||||
{
|
||||
href: '#',
|
||||
text: 'Switcher item 1'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
subject: 'Switcher subject 2',
|
||||
items: [
|
||||
{
|
||||
href: '#',
|
||||
text: 'Switcher item 1'
|
||||
},
|
||||
{
|
||||
href: '#',
|
||||
text: 'Switcher item 2'
|
||||
},
|
||||
{
|
||||
href: '#',
|
||||
text: 'Switcher item 3'
|
||||
},
|
||||
{
|
||||
href: '#',
|
||||
text: 'Switcher item 4'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
subject: 'Switcher subject 3',
|
||||
items: [
|
||||
{
|
||||
href: '#',
|
||||
text: 'Switcher item 1'
|
||||
},
|
||||
{
|
||||
href: '#',
|
||||
text: 'Switcher item 2'
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
action: leftPanelActions.account.actionString,
|
||||
type: leftPanelTypes.link,
|
||||
content: {
|
||||
href: '#',
|
||||
text: leftPanelActions.account.actionString
|
||||
},
|
||||
isVisible: true
|
||||
}
|
||||
];
|
||||
|
||||
const sideNavMenu = [
|
||||
{
|
||||
href: undefined,
|
||||
text: 'Category 1',
|
||||
subMenu: [
|
||||
{
|
||||
href: '#',
|
||||
text: 'Sub-link 1'
|
||||
},
|
||||
{
|
||||
href: '#',
|
||||
text: 'Sub-link 2'
|
||||
},
|
||||
{
|
||||
href: '#',
|
||||
text: 'Sub-link 3'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
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'
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
function searchInStore(event) {
|
||||
if (event.detail.action === leftPanelActions.search.actionString) {
|
||||
searchStore.search(event.detail.textInput);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if story === 'with-nav'}
|
||||
<UIShell {...$$props} {navMenu} />
|
||||
{:else if story === 'with-actions'}
|
||||
<UIShell {...$$props} {rightPanel} on:inputSearch={searchInStore} />
|
||||
{:else if story === 'with-actions-nav'}
|
||||
<UIShell {...$$props} {navMenu} {rightPanel} on:inputSearch={searchInStore} />
|
||||
{:else if story === 'with-actions-sidenav'}
|
||||
<UIShell {...$$props} {sideNavMenu} {rightPanel} on:inputSearch={searchInStore} />
|
||||
{:else}
|
||||
<UIShell {...$$props} />
|
||||
{/if}
|
54
src/components/UIShell/UIShell.stories.js
Normal file
54
src/components/UIShell/UIShell.stories.js
Normal file
|
@ -0,0 +1,54 @@
|
|||
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')
|
||||
}
|
||||
});
|
||||
|
||||
export const WithActionsAndNav = () => ({
|
||||
Component,
|
||||
props: {
|
||||
story: 'with-actions-nav',
|
||||
href: text('The link href (href)', '#'),
|
||||
company: text('Company name', 'IBM'),
|
||||
platformName: text('Platform name', 'Platform Name')
|
||||
}
|
||||
});
|
||||
|
||||
export const WithActionsAndSidenav = () => ({
|
||||
Component,
|
||||
props: {
|
||||
story: 'with-actions-sidenav',
|
||||
href: text('The link href (href)', '#'),
|
||||
company: text('Company name', 'IBM'),
|
||||
platformName: text('Platform name', 'Platform Name')
|
||||
}
|
||||
});
|
42
src/components/UIShell/UIShell.svelte
Normal file
42
src/components/UIShell/UIShell.svelte
Normal file
|
@ -0,0 +1,42 @@
|
|||
<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;
|
||||
export let sideNavMenu = undefined;
|
||||
|
||||
import { cx } from '../../lib';
|
||||
import UIShellNavWrapper from './UIShellNav/UIShellNavWrapper.svelte';
|
||||
import UIShellNavItem from './UIShellNav/UIShellNavItem.svelte';
|
||||
import UIShellRightPanel from './UIShellRightPanel/UIShellRightPanel.svelte';
|
||||
import UIShellSideNavWrapper from './UIShellSideNav/UIShellSideNavWrapper.svelte';
|
||||
import UIShellSideNavItem from './UIShellSideNav/UIShellSideNavItem.svelte';
|
||||
import HamburgerMenu from './UIShellSideNav/HamburgerMenu.svelte';
|
||||
|
||||
$: ariaLabel = company + (uiShellAriaLabel || $$props['aria-label'] || platformName);
|
||||
</script>
|
||||
|
||||
<header aria-label={ariaLabel} class={cx('--header')} role="banner">
|
||||
<HamburgerMenu />
|
||||
<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} on:inputSearch />
|
||||
{/if}
|
||||
{#if sideNavMenu}
|
||||
<UIShellSideNavWrapper>
|
||||
<UIShellSideNavItem />
|
||||
</UIShellSideNavWrapper>
|
||||
{/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 (listItemSubMenu) {
|
||||
if (listItemSubMenu.contains(target) || target === listItemSubMenu) {
|
||||
expanded = !expanded;
|
||||
} else {
|
||||
if (expanded) {
|
||||
expanded = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</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,40 @@
|
|||
<script>
|
||||
export let type = undefined;
|
||||
export let icon = undefined;
|
||||
export let content = undefined;
|
||||
export let componentIsActive = undefined;
|
||||
|
||||
import { cx } from '../../../lib';
|
||||
import Icon from '../../Icon/Icon.svelte';
|
||||
import { slide } from 'svelte/transition';
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.component-form {
|
||||
margin: 1rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="right-panel-action-component">
|
||||
<button
|
||||
aria-label={type}
|
||||
class={cx('--header__action', componentIsActive && '--header__action--active')}
|
||||
type="button"
|
||||
on:keydown={({ key }) => {
|
||||
if (key === 'Enter') {
|
||||
componentIsActive = !componentIsActive;
|
||||
}
|
||||
}}>
|
||||
<Icon {...icon[0]} render={icon[0].render} />
|
||||
</button>
|
||||
{#if componentIsActive}
|
||||
<div
|
||||
id="right-panel-action-component-form"
|
||||
class={cx('--header-panel', '--header-panel--expanded')}
|
||||
transition:slide={{ duration: 200 }}>
|
||||
<div class="component-form">
|
||||
<svelte:component this={content} />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
138
src/components/UIShell/UIShellRightPanel/ActionGeneric.svelte
Normal file
138
src/components/UIShell/UIShellRightPanel/ActionGeneric.svelte
Normal file
|
@ -0,0 +1,138 @@
|
|||
<script>
|
||||
export let action = undefined;
|
||||
|
||||
import ActionLink from './ActionLink.svelte';
|
||||
import ActionComponent from './ActionComponent.svelte';
|
||||
import ActionSearch from './ActionSearch.svelte';
|
||||
import { leftPanelTypes } from '../constants';
|
||||
import { isChildOf } from '../helpers';
|
||||
|
||||
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 }) => {
|
||||
checkForClicksTypeComponent(target, typeComponent);
|
||||
checkForClicksTypeSearch(target, typeSearch);
|
||||
checkForClicksTypeLink(target, typeLink);
|
||||
});
|
||||
|
||||
function checkForClicksTypeComponent(target, component) {
|
||||
if (component && target) {
|
||||
if (
|
||||
!isChildOf(target, 'right-panel-action-component') ||
|
||||
!isChildOf(target, 'right-panel-action-component-form')
|
||||
) {
|
||||
if (component.contains(target) || target === component) {
|
||||
componentIsActive = !componentIsActive;
|
||||
} else {
|
||||
if (componentIsActive) {
|
||||
componentIsActive = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkForClicksTypeSearch(target, component) {
|
||||
if (component && target) {
|
||||
if (!isChildOf(target, 'right-panel-action-search')) {
|
||||
if (component.contains(target) || target === component) {
|
||||
searchIsActive = !searchIsActive;
|
||||
} else {
|
||||
if (searchIsActive) {
|
||||
searchIsActive = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!searchIsActive && target.id !== 'right-panel-close-search') {
|
||||
searchIsActive = true;
|
||||
} else if (searchIsActive && isChildOf(target, 'right-panel-close-search')) {
|
||||
searchIsActive = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkForClicksTypeLink(target, component) {
|
||||
if (component && target) {
|
||||
if (component.contains(target) || target === component) {
|
||||
linkIsActive = !linkIsActive;
|
||||
} else {
|
||||
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.isVisible}
|
||||
{#if action.type === leftPanelTypes.component}
|
||||
<div bind:this={typeComponent}>
|
||||
<ActionComponent
|
||||
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;
|
||||
}}
|
||||
on:inputSearch />
|
||||
</div>
|
||||
{:else if action.type === leftPanelTypes.link || action.type === leftPanelTypes.links}
|
||||
<div bind:this={typeLink}>
|
||||
<ActionLink
|
||||
action={action.action}
|
||||
type={action.type}
|
||||
icon={action.icon ? action.icon : undefined}
|
||||
content={action.content}
|
||||
bind:linkIsActive />
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
99
src/components/UIShell/UIShellRightPanel/ActionLink.svelte
Normal file
99
src/components/UIShell/UIShellRightPanel/ActionLink.svelte
Normal file
|
@ -0,0 +1,99 @@
|
|||
<script>
|
||||
export let action = undefined;
|
||||
export let type = undefined;
|
||||
export let icon = undefined;
|
||||
export let content = undefined;
|
||||
export let linkIsActive = undefined;
|
||||
|
||||
import { cx } from '../../../lib';
|
||||
import Icon from '../../Icon/Icon.svelte';
|
||||
import { leftPanelActions, leftPanelTypes } from '../constants';
|
||||
import { slide } from 'svelte/transition';
|
||||
|
||||
let href = undefined;
|
||||
|
||||
if (type === leftPanelTypes.link) {
|
||||
href = content.href;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.action-link {
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
vertical-align: middle;
|
||||
justify-content: center;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.subject-divider {
|
||||
color: #525252;
|
||||
padding-bottom: 4px;
|
||||
border-bottom: 1px solid #525252;
|
||||
margin: 32px 1rem 8px;
|
||||
}
|
||||
|
||||
.subject-divider span {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 400;
|
||||
line-height: 1rem;
|
||||
letter-spacing: 0.32px;
|
||||
color: #c6c6c6;
|
||||
}
|
||||
</style>
|
||||
|
||||
{#if type === leftPanelTypes.link}
|
||||
<a
|
||||
aria-label={type}
|
||||
class={cx('--header__action', linkIsActive && '--header__action--active')}
|
||||
class:action-link={true}
|
||||
{href}>
|
||||
<Icon {...icon} />
|
||||
</a>
|
||||
{:else}
|
||||
<button
|
||||
aria-label={type}
|
||||
class={cx('--header__action', linkIsActive && '--header__action--active')}
|
||||
type="button"
|
||||
on:keydown={({ key }) => {
|
||||
if (key === 'Enter') {
|
||||
linkIsActive = !linkIsActive;
|
||||
}
|
||||
}}>
|
||||
<Icon {...icon} />
|
||||
</button>
|
||||
{#if linkIsActive && type === leftPanelTypes.links}
|
||||
<div
|
||||
class={cx('--header-panel', '--header-panel--expanded')}
|
||||
transition:slide={{ duration: 200 }}>
|
||||
<ul class={cx('--switcher__item')}>
|
||||
{#each content as subject}
|
||||
{#if subject.subject}
|
||||
<li class="subject-divider">
|
||||
<span>{subject.subject}</span>
|
||||
</li>
|
||||
{/if}
|
||||
{#each subject.items as link}
|
||||
<li class={cx('--switcher__item')}>
|
||||
<a class={cx('--switcher__item-link')} href={link.href}>{link.text}</a>
|
||||
</li>
|
||||
{/each}
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
191
src/components/UIShell/UIShellRightPanel/ActionSearch.svelte
Normal file
191
src/components/UIShell/UIShellRightPanel/ActionSearch.svelte
Normal file
|
@ -0,0 +1,191 @@
|
|||
<script>
|
||||
export let action = undefined;
|
||||
export let icon = undefined;
|
||||
export let searchIsActive = undefined;
|
||||
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { cx } from '../../../lib';
|
||||
import Icon from '../../Icon/Icon.svelte';
|
||||
import { leftPanelActions, closeIcon } from '../constants';
|
||||
import searchStore from '../searchStore';
|
||||
import ActionSearchResult from './ActionSearchResult.svelte';
|
||||
|
||||
let searchTabIndex = 0;
|
||||
let closeTabIndex = -1;
|
||||
let inputSearchField = undefined;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function dispatchInputs(event) {
|
||||
const params = {
|
||||
action: action,
|
||||
textInput: event.target.value
|
||||
};
|
||||
|
||||
dispatch('inputSearch', params);
|
||||
}
|
||||
|
||||
$: if (!searchIsActive) {
|
||||
if (inputSearchField) {
|
||||
inputSearchField.value = '';
|
||||
}
|
||||
searchStore.clear();
|
||||
}
|
||||
|
||||
$: if (searchIsActive) {
|
||||
searchTabIndex = -1;
|
||||
closeTabIndex = 0;
|
||||
} else {
|
||||
searchTabIndex = 0;
|
||||
closeTabIndex = -1;
|
||||
}
|
||||
|
||||
$: showResults = $searchStore ? true : false;
|
||||
</script>
|
||||
|
||||
<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;
|
||||
display: block;
|
||||
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;
|
||||
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>
|
||||
|
||||
<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"
|
||||
on:keydown={({ key }) => {
|
||||
if (key === 'Enter') {
|
||||
searchIsActive = !searchIsActive;
|
||||
}
|
||||
}}>
|
||||
<Icon {...icon} />
|
||||
</button>
|
||||
<input
|
||||
bind:this={inputSearchField}
|
||||
id="input-search-field"
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
tabindex={closeTabIndex}
|
||||
class="input-search"
|
||||
class:input-hidden={!searchIsActive}
|
||||
placeholder="Search"
|
||||
on:focus={() => dispatch('focusInputSearch')}
|
||||
on:focusout={() => dispatch('focusOutInputSearch')}
|
||||
on:input={dispatchInputs} />
|
||||
<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"
|
||||
on:click={() => searchStore.clear()}
|
||||
on:keydown={({ key }) => {
|
||||
if (key === 'Enter') {
|
||||
searchIsActive = !searchIsActive;
|
||||
}
|
||||
}}>
|
||||
<Icon {...closeIcon} />
|
||||
</button>
|
||||
</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}
|
|
@ -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}
|
||||
<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}
|
||||
<span class="search-list-item-menu">→ {searchItem.menu}</span>
|
||||
<span class="search-list-item-description">{searchItem.description}</span>
|
||||
</a>
|
||||
</li>
|
||||
{/if}
|
|
@ -0,0 +1,50 @@
|
|||
<script>
|
||||
export let rightPanel = undefined;
|
||||
|
||||
import { cx } from '../../../lib';
|
||||
import ActionGeneric from './ActionGeneric.svelte';
|
||||
import { leftPanelActions } from '../constants';
|
||||
|
||||
let orderedRightPanel = [];
|
||||
let customActions = 1;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
orderedRightPanel = orderedRightPanel.filter(item => {
|
||||
return item;
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class={cx('--header__global')}>
|
||||
{#each orderedRightPanel as action, index}
|
||||
<ActionGeneric {action} on:inputSearch />
|
||||
{/each}
|
||||
</div>
|
29
src/components/UIShell/UIShellSideNav/HamburgerMenu.svelte
Normal file
29
src/components/UIShell/UIShellSideNav/HamburgerMenu.svelte
Normal file
|
@ -0,0 +1,29 @@
|
|||
<script>
|
||||
export let ariaLabel = undefined;
|
||||
|
||||
import { cx } from '../../../lib';
|
||||
import Menu20 from 'carbon-icons-svelte/lib/Menu20';
|
||||
import Icon from '../../Icon/Icon.svelte';
|
||||
import { closeIcon } from '../constants';
|
||||
|
||||
let isOpen = false;
|
||||
|
||||
$: iconProps = !isOpen ? {
|
||||
class: undefined,
|
||||
skeleton: false,
|
||||
render: Menu20,
|
||||
title: 'Open Menu',
|
||||
tabIndex: 0,
|
||||
focusable: false,
|
||||
style: undefined
|
||||
} : closeIcon;
|
||||
</script>
|
||||
|
||||
<button
|
||||
aria-label={ariaLabel}
|
||||
class={cx('--header__action', '--header__menu-trigger', '--header__menu-toggle')}
|
||||
title="Open menu"
|
||||
type="button"
|
||||
on:click={() => isOpen = !isOpen}>
|
||||
<Icon {...iconProps} />
|
||||
</button>
|
185
src/components/UIShell/UIShellSideNav/UIShellSideNavItem.svelte
Normal file
185
src/components/UIShell/UIShellSideNav/UIShellSideNavItem.svelte
Normal file
|
@ -0,0 +1,185 @@
|
|||
<li class="bx--side-nav__item bx--side-nav__item--icon">
|
||||
<button aria-haspopup="true" aria-expanded="false" class="bx--side-nav__submenu" type="button">
|
||||
<div class="bx--side-nav__icon">
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 32 32"
|
||||
aria-hidden="true">
|
||||
<path
|
||||
d="M8.24 25.14L7 26.67a14 14 0 0 0 4.18 2.44l.68-1.88a12 12 0 0 1-3.62-2.09zm-4.05-7.07l-2
|
||||
.35A13.89 13.89 0 0 0 3.86 23l1.73-1a11.9 11.9 0 0 1-1.4-3.93zm7.63-13.31l-.68-1.88A14 14
|
||||
0 0 0 7 5.33l1.24 1.53a12 12 0 0 1 3.58-2.1zM5.59 10L3.86 9a13.89 13.89 0 0 0-1.64 4.54l2
|
||||
.35A11.9 11.9 0 0 1 5.59 10zM16 2v2a12 12 0 0 1 0 24v2a14 14 0 0 0 0-28z" />
|
||||
</svg>
|
||||
</div>
|
||||
<span class="bx--side-nav__submenu-title">Category title</span>
|
||||
<div class="bx--side-nav__icon bx--side-nav__icon--small bx--side-nav__submenu-chevron">
|
||||
<svg
|
||||
focusable="false"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 32 32"
|
||||
aria-hidden="true"
|
||||
style="will-change: transform;">
|
||||
<path d="M16 22L6 12l1.4-1.4 8.6 8.6 8.6-8.6L26 12z" />
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
<ul class="bx--side-nav__menu" role="menu">
|
||||
<li class="bx--side-nav__menu-item" role="none">
|
||||
<a href="javascript:void(0)" class="bx--side-nav__link" role="menuitem">
|
||||
<span class="bx--side-nav__link-text">Link</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="bx--side-nav__menu-item" role="none">
|
||||
<a aria-current="page" href="javascript:void(0)" class="bx--side-nav__link" role="menuitem">
|
||||
<span class="bx--side-nav__link-text">Link</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="bx--side-nav__menu-item" role="none">
|
||||
<a href="javascript:void(0)" class="bx--side-nav__link" role="menuitem">
|
||||
<span class="bx--side-nav__link-text">Link</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="bx--side-nav__item bx--side-nav__item--icon">
|
||||
<button aria-haspopup="true" aria-expanded="false" class="bx--side-nav__submenu" type="button">
|
||||
<div class="bx--side-nav__icon">
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 32 32"
|
||||
aria-hidden="true">
|
||||
<path
|
||||
d="M8.24 25.14L7 26.67a14 14 0 0 0 4.18 2.44l.68-1.88a12 12 0 0 1-3.62-2.09zm-4.05-7.07l-2
|
||||
.35A13.89 13.89 0 0 0 3.86 23l1.73-1a11.9 11.9 0 0 1-1.4-3.93zm7.63-13.31l-.68-1.88A14 14
|
||||
0 0 0 7 5.33l1.24 1.53a12 12 0 0 1 3.58-2.1zM5.59 10L3.86 9a13.89 13.89 0 0 0-1.64 4.54l2
|
||||
.35A11.9 11.9 0 0 1 5.59 10zM16 2v2a12 12 0 0 1 0 24v2a14 14 0 0 0 0-28z" />
|
||||
</svg>
|
||||
</div>
|
||||
<span class="bx--side-nav__submenu-title">Category title</span>
|
||||
<div class="bx--side-nav__icon bx--side-nav__icon--small bx--side-nav__submenu-chevron">
|
||||
<svg
|
||||
focusable="false"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 32 32"
|
||||
aria-hidden="true"
|
||||
style="will-change: transform;">
|
||||
<path d="M16 22L6 12l1.4-1.4 8.6 8.6 8.6-8.6L26 12z" />
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
<ul class="bx--side-nav__menu" role="menu">
|
||||
<li class="bx--side-nav__menu-item" role="none">
|
||||
<a href="javascript:void(0)" class="bx--side-nav__link" role="menuitem">
|
||||
<span class="bx--side-nav__link-text">Link</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="bx--side-nav__menu-item" role="none">
|
||||
<a aria-current="page" href="javascript:void(0)" class="bx--side-nav__link" role="menuitem">
|
||||
<span class="bx--side-nav__link-text">Link</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="bx--side-nav__menu-item" role="none">
|
||||
<a href="javascript:void(0)" class="bx--side-nav__link" role="menuitem">
|
||||
<span class="bx--side-nav__link-text">Link</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="bx--side-nav__item bx--side-nav__item--icon">
|
||||
<button aria-haspopup="true" aria-expanded="false" class="bx--side-nav__submenu" type="button">
|
||||
<div class="bx--side-nav__icon">
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 32 32"
|
||||
aria-hidden="true">
|
||||
<path
|
||||
d="M8.24 25.14L7 26.67a14 14 0 0 0 4.18 2.44l.68-1.88a12 12 0 0 1-3.62-2.09zm-4.05-7.07l-2
|
||||
.35A13.89 13.89 0 0 0 3.86 23l1.73-1a11.9 11.9 0 0 1-1.4-3.93zm7.63-13.31l-.68-1.88A14 14
|
||||
0 0 0 7 5.33l1.24 1.53a12 12 0 0 1 3.58-2.1zM5.59 10L3.86 9a13.89 13.89 0 0 0-1.64 4.54l2
|
||||
.35A11.9 11.9 0 0 1 5.59 10zM16 2v2a12 12 0 0 1 0 24v2a14 14 0 0 0 0-28z" />
|
||||
</svg>
|
||||
</div>
|
||||
<span class="bx--side-nav__submenu-title">Category title</span>
|
||||
<div class="bx--side-nav__icon bx--side-nav__icon--small bx--side-nav__submenu-chevron">
|
||||
<svg
|
||||
focusable="false"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 32 32"
|
||||
aria-hidden="true"
|
||||
style="will-change: transform;">
|
||||
<path d="M16 22L6 12l1.4-1.4 8.6 8.6 8.6-8.6L26 12z" />
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
<ul class="bx--side-nav__menu" role="menu">
|
||||
<li class="bx--side-nav__menu-item" role="none">
|
||||
<a href="javascript:void(0)" class="bx--side-nav__link" role="menuitem">
|
||||
<span class="bx--side-nav__link-text">Link</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="bx--side-nav__menu-item" role="none">
|
||||
<a aria-current="page" href="javascript:void(0)" class="bx--side-nav__link" role="menuitem">
|
||||
<span class="bx--side-nav__link-text">Link</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="bx--side-nav__menu-item" role="none">
|
||||
<a href="javascript:void(0)" class="bx--side-nav__link" role="menuitem">
|
||||
<span class="bx--side-nav__link-text">Link</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="bx--side-nav__item">
|
||||
<a href="javascript:void(0)" class="bx--side-nav__link">
|
||||
<div class="bx--side-nav__icon bx--side-nav__icon--small">
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 32 32"
|
||||
aria-hidden="true">
|
||||
<path
|
||||
d="M8.24 25.14L7 26.67a14 14 0 0 0 4.18 2.44l.68-1.88a12 12 0 0 1-3.62-2.09zm-4.05-7.07l-2
|
||||
.35A13.89 13.89 0 0 0 3.86 23l1.73-1a11.9 11.9 0 0 1-1.4-3.93zm7.63-13.31l-.68-1.88A14 14
|
||||
0 0 0 7 5.33l1.24 1.53a12 12 0 0 1 3.58-2.1zM5.59 10L3.86 9a13.89 13.89 0 0 0-1.64 4.54l2
|
||||
.35A11.9 11.9 0 0 1 5.59 10zM16 2v2a12 12 0 0 1 0 24v2a14 14 0 0 0 0-28z" />
|
||||
</svg>
|
||||
</div>
|
||||
<span class="bx--side-nav__link-text">Link</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="bx--side-nav__item">
|
||||
<a href="javascript:void(0)" class="bx--side-nav__link">
|
||||
<div class="bx--side-nav__icon bx--side-nav__icon--small">
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 32 32"
|
||||
aria-hidden="true">
|
||||
<path
|
||||
d="M8.24 25.14L7 26.67a14 14 0 0 0 4.18 2.44l.68-1.88a12 12 0 0 1-3.62-2.09zm-4.05-7.07l-2
|
||||
.35A13.89 13.89 0 0 0 3.86 23l1.73-1a11.9 11.9 0 0 1-1.4-3.93zm7.63-13.31l-.68-1.88A14 14
|
||||
0 0 0 7 5.33l1.24 1.53a12 12 0 0 1 3.58-2.1zM5.59 10L3.86 9a13.89 13.89 0 0 0-1.64 4.54l2
|
||||
.35A11.9 11.9 0 0 1 5.59 10zM16 2v2a12 12 0 0 1 0 24v2a14 14 0 0 0 0-28z" />
|
||||
</svg>
|
||||
</div>
|
||||
<span class="bx--side-nav__link-text">Link</span>
|
||||
</a>
|
||||
</li>
|
|
@ -0,0 +1,29 @@
|
|||
<script>
|
||||
export let ariaLabel = undefined;
|
||||
|
||||
import { cx } from '../../../lib';
|
||||
</script>
|
||||
|
||||
<nav class={cx('--side-nav__navigation', '--side-nav', '--side-nav--ux')} aria-label={ariaLabel}>
|
||||
<ul class={cx('--side-nav__items')}>
|
||||
<slot />
|
||||
</ul>
|
||||
<footer class="bx--side-nav__footer">
|
||||
<button class="bx--side-nav__toggle" type="button" title="Open">
|
||||
<div class="bx--side-nav__icon">
|
||||
<svg
|
||||
focusable="false"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 32 32"
|
||||
aria-hidden="true"
|
||||
style="will-change: transform;">
|
||||
<path d="M22 16L12 26l-1.4-1.4 8.6-8.6-8.6-8.6L12 6z" />
|
||||
</svg>
|
||||
</div>
|
||||
<span class="bx--assistive-text">Open</span>
|
||||
</button>
|
||||
</footer>
|
||||
</nav>
|
86
src/components/UIShell/constants.js
Normal file
86
src/components/UIShell/constants.js
Normal 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
|
||||
};
|
21
src/components/UIShell/helpers.js
Normal file
21
src/components/UIShell/helpers.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
export function isChildOf(target, parentId) {
|
||||
if (target.id) {
|
||||
if (target.id == parentId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (target.parentNode) {
|
||||
if (target.parentNode.id) {
|
||||
if (target.parentNode.id == parentId) {
|
||||
return true;
|
||||
} else {
|
||||
return isChildOf(target.parentNode, parentId);
|
||||
}
|
||||
} else {
|
||||
return isChildOf(target.parentNode, parentId);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
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;
|
58
src/components/UIShell/searchStore.js
Normal file
58
src/components/UIShell/searchStore.js
Normal 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;
|
|
@ -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