feat: initial commit

This commit is contained in:
Eric Liu 2019-12-15 11:20:52 -08:00
commit 72dc38ea56
119 changed files with 14925 additions and 1 deletions

6
.eslintrc.json Normal file
View file

@ -0,0 +1,6 @@
{
"parserOptions": { "ecmaVersion": 2019, "sourceType": "module" },
"env": { "es6": true, "browser": true },
"plugins": ["svelte3"],
"overrides": [{ "files": ["**/*.svelte"], "processor": "svelte3/svelte3" }]
}

1
.github/CODEOWNERS vendored Normal file
View file

@ -0,0 +1 @@
* @metonym

7
.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
/coverage
/lib
/node_modules
.DS_Store
yarn-debug.log*
yarn-error.log*
*.tgz

2
.storybook/addons.js Normal file
View file

@ -0,0 +1,2 @@
import '@storybook/addon-knobs/register';
import '@storybook/addon-storysource/register';

6
.storybook/config.js Normal file
View file

@ -0,0 +1,6 @@
import { configure } from '@storybook/svelte';
// use prebuilt CSS file to refrain from setting up sass, autoprefixer
import '!style-loader!css-loader!carbon-components/css/carbon-components.min.css';
configure(require.context('../src', true, /\.stories\.js$/), module);

View file

@ -0,0 +1,11 @@
const path = require('path');
module.exports = async ({ config }) => {
config.module.rules.push({
test: [/\.stories\.js$/],
loaders: [require.resolve('@storybook/source-loader')],
include: [path.resolve(__dirname, '../src')],
enforce: 'pre'
});
return config;
};

5
.travis.yml Normal file
View file

@ -0,0 +1,5 @@
language: node_js
node_js: 10
cache: yarn
script:
- yarn build

10
CHANGELOG.md Normal file
View file

@ -0,0 +1,10 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.1.0](https://github.com/IBM/carbon-components-svelte/releases/tag/v0.1.0) - 2019-12-15
- Initial release

View file

@ -1,2 +1,67 @@
# carbon-components-svelte
Svelte implementation of the Carbon Design System
> [🚧🚧🚧 UNDER CONSTRUCTION] Svelte implementation of the Carbon Design System
This library is a work in progress.
## Getting Started
```bash
yarn add -D carbon-components carbon-components-svelte carbon-icons-svelte
```
## Supported Components
Currently, the following components are supported:
- Accordion
- AccordionItem
- AccordionSkeleton
- Breadcrumb
- BreadcrumbItem
- BreadcrumbSkeleton
- Button
- ButtonSkeleton
- Checkbox
- CheckboxSkeleton
- CodeSnippet
- CodeSnippetSkeleton
- Copy
- CopyButton
- InlineLoading
- Loading
- Link
- ListItem
- OrderedList
- SkeletonText
- Tag
- TagSkeleton
- TextArea
- TextAreaSkeleton
- Toggle
- ToggleSkeleton
- ToggleSmall
- ToggleSmallSkeleton
- TooltipDefinition
- TooltipIcon
- UnorderedList
## Usage
```html
<script>
import { Tag } from 'carbon-components-svelte';
</script>
<Tag>Text</Tag>
```
## Contributing
[Placeholder]
## [Changelog](CHANGELOG.md)
## License
[Apache 2.0](LICENSE)

BIN
docs/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

73
docs/iframe.html Normal file
View file

@ -0,0 +1,73 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"><title>Storybook</title><meta name="viewport" content="width=device-width,initial-scale=1"><base target="_parent"><style>:not(.sb-show-main) > .sb-main,
:not(.sb-show-nopreview) > .sb-nopreview,
:not(.sb-show-errordisplay) > .sb-errordisplay {
display: none;
}
.sb-wrapper {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
padding: 20px;
font-family: "Nunito Sans", -apple-system, ".SFNSText-Regular", "San Francisco", BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
overflow: auto;
}
.sb-heading {
font-size: 14px;
font-weight: 600;
letter-spacing: 0.2px;
margin: 10px 0;
padding-right: 25px;
}
.sb-nopreview {
display: flex;
align-content: center;
justify-content: center;
}
.sb-nopreview_main {
margin: auto;
padding: 30px;
border-radius: 10px;
background: rgba(0,0,0,0.03);
}
.sb-nopreview_heading {
text-align: center;
}
.sb-errordisplay {
border: 20px solid rgb(187, 49, 49);
background: #222;
color: #fff;
z-index: 999999;
}
.sb-errordisplay_code {
padding: 10px;
background: #000;
color: #eee;
font-family: "Operator Mono", "Fira Code Retina", "Fira Code", "FiraCode-Retina", "Andale Mono", "Lucida Console", Consolas, Monaco, monospace;
}
.sb-errordisplay pre {
white-space: pre-wrap;
}</style><script>/* globals window */
/* eslint-disable no-underscore-dangle */
try {
if (window.parent !== window) {
window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = window.parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
window.__VUE_DEVTOOLS_GLOBAL_HOOK__ = window.parent.__VUE_DEVTOOLS_GLOBAL_HOOK__;
}
} catch (e) {
// eslint-disable-next-line no-console
console.warn('unable to connect to parent frame for connecting dev tools');
}</script><style>#root[hidden],
#docs-root[hidden] {
display: none !important;
}</style></head><body><div class="sb-nopreview sb-wrapper"><div class="sb-nopreview_main"><h1 class="sb-nopreview_heading sb-heading">No Preview</h1><p>Sorry, but you either have no stories or none are selected somehow.</p><ul><li>Please check the Storybook config.</li><li>Try reloading the page.</li></ul><p>If the problem persists, check the browser console, or the terminal you've run Storybook from.</p></div></div><div class="sb-errordisplay sb-wrapper"><pre id="error-message" class="sb-heading"></pre><pre class="sb-errordisplay_code"><code id="error-stack"></code></pre></div><div id="root"></div><div id="docs-root"></div><script src="runtime~main.f68146be2ed36b44235a.bundle.js"></script><script src="vendors~main.f68146be2ed36b44235a.bundle.js"></script><script src="main.f68146be2ed36b44235a.bundle.js"></script></body></html>

20
docs/index.html Normal file
View file

@ -0,0 +1,20 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"><title>Storybook</title><meta name="viewport" content="width=device-width,initial-scale=1"><style>html, body {
overflow: hidden;
height: 100%;
width: 100%;
margin: 0;
padding: 0;
}</style><script>/* globals window */
/* eslint-disable no-underscore-dangle */
try {
if (window.parent !== window) {
window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = window.parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
window.__VUE_DEVTOOLS_GLOBAL_HOOK__ = window.parent.__VUE_DEVTOOLS_GLOBAL_HOOK__;
}
} catch (e) {
// eslint-disable-next-line no-console
console.warn('unable to connect to parent frame for connecting dev tools');
}</script><style>#root[hidden],
#docs-root[hidden] {
display: none !important;
}</style></head><body><div id="root"></div><div id="docs-root"></div><script>window['DOCS_MODE'] = false;</script><script src="runtime~main.67b3c42af12dc2b7ac9d.bundle.js"></script><script src="vendors~main.de76cb4d691ff902e1d8.bundle.js"></script><script src="main.d19aceecbf99f4b72fa7.bundle.js"></script></body></html>

View file

@ -0,0 +1 @@
(window.webpackJsonp=window.webpackJsonp||[]).push([[0],{415:function(n,o,c){c(416),c(529),n.exports=c(878)},438:function(n,o){},529:function(n,o,c){"use strict";c.r(o);c(530),c(850)}},[[415,1,2]]]);

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
{"version":3,"file":"main.f68146be2ed36b44235a.bundle.js","sources":["webpack:///main.f68146be2ed36b44235a.bundle.js"],"mappings":"AAAA","sourceRoot":""}

View file

@ -0,0 +1 @@
!function(e){function r(r){for(var n,l,f=r[0],i=r[1],a=r[2],c=0,s=[];c<f.length;c++)l=f[c],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in i)Object.prototype.hasOwnProperty.call(i,n)&&(e[n]=i[n]);for(p&&p(r);s.length;)s.shift()();return u.push.apply(u,a||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,f=1;f<t.length;f++){var i=t[f];0!==o[i]&&(n=!1)}n&&(u.splice(r--,1),e=l(l.s=t[0]))}return e}var n={},o={1:0},u=[];function l(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,l),t.l=!0,t.exports}l.m=e,l.c=n,l.d=function(e,r,t){l.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,r){if(1&r&&(e=l(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(l.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)l.d(t,n,function(r){return e[r]}.bind(null,n));return t},l.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(r,"a",r),r},l.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},l.p="";var f=window.webpackJsonp=window.webpackJsonp||[],i=f.push.bind(f);f.push=r,f=f.slice();for(var a=0;a<f.length;a++)r(f[a]);var p=i;t()}([]);

View file

@ -0,0 +1,2 @@
!function(modules){function webpackJsonpCallback(data){for(var moduleId,chunkId,chunkIds=data[0],moreModules=data[1],executeModules=data[2],i=0,resolves=[];i<chunkIds.length;i++)chunkId=chunkIds[i],Object.prototype.hasOwnProperty.call(installedChunks,chunkId)&&installedChunks[chunkId]&&resolves.push(installedChunks[chunkId][0]),installedChunks[chunkId]=0;for(moduleId in moreModules)Object.prototype.hasOwnProperty.call(moreModules,moduleId)&&(modules[moduleId]=moreModules[moduleId]);for(parentJsonpFunction&&parentJsonpFunction(data);resolves.length;)resolves.shift()();return deferredModules.push.apply(deferredModules,executeModules||[]),checkDeferredModules()}function checkDeferredModules(){for(var result,i=0;i<deferredModules.length;i++){for(var deferredModule=deferredModules[i],fulfilled=!0,j=1;j<deferredModule.length;j++){var depId=deferredModule[j];0!==installedChunks[depId]&&(fulfilled=!1)}fulfilled&&(deferredModules.splice(i--,1),result=__webpack_require__(__webpack_require__.s=deferredModule[0]))}return result}var installedModules={},installedChunks={1:0},deferredModules=[];function __webpack_require__(moduleId){if(installedModules[moduleId])return installedModules[moduleId].exports;var module=installedModules[moduleId]={i:moduleId,l:!1,exports:{}};return modules[moduleId].call(module.exports,module,module.exports,__webpack_require__),module.l=!0,module.exports}__webpack_require__.m=modules,__webpack_require__.c=installedModules,__webpack_require__.d=function(exports,name,getter){__webpack_require__.o(exports,name)||Object.defineProperty(exports,name,{enumerable:!0,get:getter})},__webpack_require__.r=function(exports){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(exports,"__esModule",{value:!0})},__webpack_require__.t=function(value,mode){if(1&mode&&(value=__webpack_require__(value)),8&mode)return value;if(4&mode&&"object"==typeof value&&value&&value.__esModule)return value;var ns=Object.create(null);if(__webpack_require__.r(ns),Object.defineProperty(ns,"default",{enumerable:!0,value:value}),2&mode&&"string"!=typeof value)for(var key in value)__webpack_require__.d(ns,key,function(key){return value[key]}.bind(null,key));return ns},__webpack_require__.n=function(module){var getter=module&&module.__esModule?function getDefault(){return module.default}:function getModuleExports(){return module};return __webpack_require__.d(getter,"a",getter),getter},__webpack_require__.o=function(object,property){return Object.prototype.hasOwnProperty.call(object,property)},__webpack_require__.p="";var jsonpArray=window.webpackJsonp=window.webpackJsonp||[],oldJsonpFunction=jsonpArray.push.bind(jsonpArray);jsonpArray.push=webpackJsonpCallback,jsonpArray=jsonpArray.slice();for(var i=0;i<jsonpArray.length;i++)webpackJsonpCallback(jsonpArray[i]);var parentJsonpFunction=oldJsonpFunction;checkDeferredModules()}([]);
//# sourceMappingURL=runtime~main.f68146be2ed36b44235a.bundle.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"runtime~main.f68146be2ed36b44235a.bundle.js","sources":["webpack:///runtime~main.f68146be2ed36b44235a.bundle.js"],"mappings":"AAAA","sourceRoot":""}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,104 @@
/**!
* @fileOverview Kickass library to create and place poppers near their reference elements.
* @version 1.15.0
* @license
* Copyright (c) 2016 Federico Zivolo and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/*!
* https://github.com/es-shims/es5-shim
* @license es5-shim Copyright 2009-2015 by contributors, MIT License
* see https://github.com/es-shims/es5-shim/blob/master/LICENSE
*/
/*!
* isobject <https://github.com/jonschlinkert/isobject>
*
* Copyright (c) 2014-2017, Jon Schlinkert.
* Released under the MIT License.
*/
/*!
* https://github.com/paulmillr/es6-shim
* @license es6-shim Copyright 2013-2016 by Paul Miller (http://paulmillr.com)
* and contributors, MIT License
* es6-shim: v0.35.4
* see https://github.com/paulmillr/es6-shim/blob/0.35.3/LICENSE
* Details and documentation:
* https://github.com/paulmillr/es6-shim/
*/
/*!
Copyright (c) 2017 Jed Watson.
Licensed under the MIT License (MIT), see
http://jedwatson.github.io/classnames
*/
/** @license React v16.8.6
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v16.8.6
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v0.13.6
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
object-assign
(c) Sindre Sorhus
@license MIT
*/
/*!
* Fuse.js v3.4.5 - Lightweight fuzzy-search (http://fusejs.io)
*
* Copyright (c) 2012-2017 Kirollos Risk (http://kiro.me)
* All Rights Reserved. Apache Software License 2.0
*
* http://www.apache.org/licenses/LICENSE-2.0
*/
/** @license React v16.8.6
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
{"version":3,"file":"vendors~main.f68146be2ed36b44235a.bundle.js","sources":["webpack:///vendors~main.f68146be2ed36b44235a.bundle.js"],"mappings":"AAAA;;;;;AAk3bA;;;;;AAm0FA;;;;;AAkkEA;;;;;;;;;AAukBA;;;AA8odA;;;;;;;;AAg/BA;;;;;;;;AAqEA;;;;;;;;AAkTA;;;;;;;AAyrDA;;;;;;;AAy7CA;;;;;;;AAogBA;;;;;;;AAfA","sourceRoot":""}

120
package.json Normal file
View file

@ -0,0 +1,120 @@
{
"name": "carbon-components-svelte",
"version": "0.1.0",
"license": "Apache-2.0",
"description": "Svelte implementation of the Carbon Design System",
"svelte": "src/index.js",
"main": "lib/index.js",
"module": "lib/index.mjs",
"scripts": {
"start": "start-storybook",
"build": "rollup -c",
"build:storybook": "build-storybook -o docs",
"test": "jest --coverage",
"test:tdd": "jest --watch",
"prepublishOnly": "yarn build"
},
"dependencies": {
"carbon-icons-svelte": "^10.8.0-rc.0"
},
"peerDependencies": {
"carbon-components": "^10.8.0",
"carbon-icons-svelte": "^10.8.0-rc.0"
},
"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",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/svelte": "^1.11.0",
"babel-jest": "^24.9.0",
"babel-loader": "^8.0.6",
"carbon-components": "10.8.0",
"eslint": "^6.7.2",
"eslint-plugin-svelte3": "^2.7.3",
"husky": "^3.1.0",
"jest": "^24.9.0",
"jest-transform-svelte": "^2.1.0",
"lint-staged": "^9.5.0",
"prettier": "^1.19.1",
"prettier-plugin-svelte": "^0.7.0",
"pretty-quick": "^2.0.1",
"regenerator-runtime": "^0.13.3",
"rollup": "^1.27.13",
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-svelte": "^5.1.1",
"rollup-plugin-terser": "^5.1.3",
"svelte": "^3.16.4",
"svelte-loader": "^2.13.6"
},
"contributors": [
{
"name": "Eric Liu",
"email": "eric.young.liu@ibm.com"
}
],
"husky": {
"hooks": {
"pre-commit": "lint-staged && pretty-quick --pattern 'src/**/*.{js,svelte}' --staged",
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
},
"lint-staged": {
"*.{js,svelte}": [
"eslint --fix 'src/**'",
"git add"
]
},
"prettier": {
"printWidth": 100,
"tabWidth": 2,
"semi": true,
"singleQuote": true
},
"babel": {
"presets": [
"@babel/preset-env"
]
},
"jest": {
"transform": {
"^.+\\.svelte$": "jest-transform-svelte",
"^.+\\.js$": "babel-jest"
},
"moduleFileExtensions": [
"js",
"svelte"
],
"setupFilesAfterEnv": [
"regenerator-runtime/runtime",
"@testing-library/jest-dom/extend-expect"
]
},
"commitlint": {
"extends": [
"@commitlint/config-conventional"
]
},
"resolutions": {
"**/bin-links": ">=1.1.6"
},
"repository": {
"type": "git",
"url": "https://github.com/ibm/carbon-components-svelte.git"
},
"homepage": "https://github.com/ibm/carbon-components-svelte",
"keywords": [
"carbon",
"carbon components",
"svelte"
],
"files": [
"lib"
],
"bugs": "https://github.com/ibm/carbon-components-svelte/issues"
}

24
rollup.config.js Normal file
View file

@ -0,0 +1,24 @@
import { terser } from 'rollup-plugin-terser';
import pkg from './package.json';
import resolve from 'rollup-plugin-node-resolve';
import svelte from 'rollup-plugin-svelte';
export default ['es', 'umd'].map(format => {
const UMD = format === 'umd';
const output = {
format,
file: UMD ? pkg.main : pkg.module
};
if (UMD) {
output.name = 'carbon-components-svelte';
}
return {
input: 'src',
output,
external: Object.keys(pkg.dependencies || {}),
plugins: [svelte(), resolve(), UMD && terser()]
};
});

View file

@ -0,0 +1,39 @@
<script>
let className = undefined;
export { className as class };
export let open = true;
export let count = 4;
export let props = {};
import ChevronRight16 from 'carbon-icons-svelte/lib/ChevronRight16';
import { cx } from '../../lib';
import SkeletonText from '../SkeletonText';
const _class = cx('--accordion', '--skeleton', className);
const skeletonItems = Array.from({ length: open ? count - 1 : count });
</script>
<ul class={_class} {...props}>
{#if open}
<li class={cx('--accordion__item', '--accordion__item--active')}>
<span class={cx('--accordion__heading')}>
<ChevronRight16 class={cx('--accordion__arrow')} />
<SkeletonText class={cx('--accordion__title')} />
</span>
<div class={cx('--accordion__content')}>
<SkeletonText width="90%" />
<SkeletonText width="80%" />
<SkeletonText width="95%" />
</div>
</li>
{/if}
{#each skeletonItems as item}
<li class={cx('--accordion__item')}>
<span class={cx('--accordion__heading')}>
<ChevronRight16 class={cx('--accordion__arrow')} />
<SkeletonText class={cx('--accordion__title')} />
</span>
</li>
{/each}
</ul>

View file

@ -0,0 +1,55 @@
<script>
export let story = undefined;
export let title = undefined;
export let open = undefined;
export let count = undefined;
import Layout from '../../internal/ui/Layout.svelte';
import Accordion from './Accordion.svelte';
import AccordionItem from './AccordionItem.svelte';
import AccordionSkeleton from './Accordion.Skeleton.svelte';
</script>
<Layout>
{#if story === 'skeleton'}
<div style="width: 500px">
<AccordionSkeleton {open} {count} />
</div>
{:else}
<Accordion>
<AccordionItem {title} {open}>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat.
</p>
</AccordionItem>
<AccordionItem title="Section 2 title">
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat.
</p>
</AccordionItem>
<AccordionItem title="Section 3 title">
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat.
</p>
</AccordionItem>
<AccordionItem>
<div slot="title">
Section 4 title (
<em>the title can be a node</em>
)
</div>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat.
</p>
</AccordionItem>
</Accordion>
{/if}
</Layout>

View file

@ -0,0 +1,21 @@
import { withKnobs, text, boolean, number } from '@storybook/addon-knobs';
import Component from './Accordion.Story.svelte';
export default { title: 'Accordion', decorators: [withKnobs] };
export const Default = () => ({
Component,
props: {
title: text('The title (title)', 'Section 1 title'),
open: boolean('Open the section (open)', false)
}
});
export const Skeleton = () => ({
Component,
props: {
story: 'skeleton',
open: boolean('Show first item opened (open)', true),
count: number('Set number of items (count)', 4)
}
});

View file

@ -0,0 +1,11 @@
<script>
let className = undefined;
export { className as class };
export let props = {};
import { cx } from '../../lib';
</script>
<ul {...props} class={cx('--accordion', className)}>
<slot />
</ul>

View file

@ -0,0 +1,62 @@
<script>
// NOTE: no 'renderExpando'; use 'expando' named slot
// TODO: change 'expando' to something more intuitive?
let className = undefined;
export { className as class };
export let title = undefined;
export let iconDescription = 'Expand/Collapse';
export let open = false;
export let props = {};
import { createEventDispatcher } from 'svelte';
import ChevronRight16 from 'carbon-icons-svelte/lib/ChevronRight16';
import { cx } from '../../lib';
const dispatch = createEventDispatcher();
let animation = undefined;
function handleAnimationEnd(event) {
animation = undefined;
}
function handleClick(event) {
animation = open ? 'collapsing' : 'expanding';
open = !open;
dispatch('headingclick', { open, event });
}
function handleKeyDown(event) {
if (open && event.key === 'Escape') {
open = false;
}
}
$: _class = cx(
'--accordion__item',
open && '--accordion__item--active',
animation && `--accordion__item--${animation}`,
className
);
$: accordionItemProps = {
type: 'button',
class: cx('--accordion__heading'),
title: iconDescription,
'aria-expanded': open,
onClick: handleClick,
onKeyDown: handleKeyDown
};
</script>
<li class={_class} {...props} on:animationend on:animationend={handleAnimationEnd}>
<slot name="expando" props={accordionItemProps}>
<button {...accordionItemProps} on:click={handleClick} on:keydown={handleKeyDown}>
<ChevronRight16 class={cx('--accordion__arrow')} aria-label={iconDescription} />
<div class={cx('--accordion__title')}>
<slot name="title">{title}</slot>
</div>
</button>
</slot>
<div class={cx('--accordion__content')}>
<slot />
</div>
</li>

View file

@ -0,0 +1,5 @@
import Accordion from './Accordion.svelte';
export default Accordion;
export { default as AccordionItem } from './AccordionItem.svelte';
export { default as AccordionSkeleton } from './Accordion.Skeleton.svelte';

View file

@ -0,0 +1,17 @@
<script>
let className = undefined;
export { className as class };
export let props = {};
import { cx } from '../../lib';
const _class = cx('--breadcrumb', '--skeleton', className);
</script>
<div {...props} class={_class}>
{#each [0, 1, 2] as item, i (item)}
<div class={cx('--breadcrumb-item')}>
<span class={cx('--link')}>&nbsp;</span>
</div>
{/each}
</div>

View file

@ -0,0 +1,39 @@
<script>
export let story = undefined;
export let noTrailingSlash = undefined;
import Layout from '../../internal/ui/Layout.svelte';
import Breadcrumb from './Breadcrumb.svelte';
import BreadcrumbItem from './BreadcrumbItem.svelte';
import BreadcrumbSkeleton from './Breadcrumb.Skeleton.svelte';
</script>
<Layout>
{#if story === 'current page'}
<Breadcrumb noTrailingSlash>
<BreadcrumbItem let:props>
<a {...props} href="/#">Breadcrumb 1</a>
</BreadcrumbItem>
<BreadcrumbItem href="#">Breadcrumb 2</BreadcrumbItem>
<BreadcrumbItem href="#" isCurrentPage>Breadcrumb 3</BreadcrumbItem>
</Breadcrumb>
{:else if story === 'current page with aria-current'}
<Breadcrumb noTrailingSlash>
<BreadcrumbItem let:props>
<a {...props} href="/#">Breadcrumb 1</a>
</BreadcrumbItem>
<BreadcrumbItem href="#">Breadcrumb 2</BreadcrumbItem>
<BreadcrumbItem href="#" aria-current="page">Breadcrumb 3</BreadcrumbItem>
</Breadcrumb>
{:else if story === 'skeleton'}
<BreadcrumbSkeleton />
{:else}
<Breadcrumb {noTrailingSlash}>
<BreadcrumbItem let:props>
<a {...props} href="/#">Breadcrumb 1</a>
</BreadcrumbItem>
<BreadcrumbItem href="#">Breadcrumb 2</BreadcrumbItem>
<BreadcrumbItem href="#">Breadcrumb 3</BreadcrumbItem>
</Breadcrumb>
{/if}
</Layout>

View file

@ -0,0 +1,24 @@
import { withKnobs, boolean } from '@storybook/addon-knobs';
import Component from './Breadcrumb.Story.svelte';
export default { title: 'Breadcrumb', decorators: [withKnobs] };
export const Default = () => ({
Component,
props: { noTrailingSlash: boolean('No Trailing Slash (noTrailingSlash)', false) }
});
export const Skeleton = () => ({
Component,
props: { story: 'skeleton' }
});
export const CurrentPage = () => ({
Component,
props: { story: 'current page' }
});
export const CurrentPageWithAriaCurrent = () => ({
Component,
props: { story: 'current page with aria-current' }
});

View file

@ -0,0 +1,16 @@
<script>
let className = undefined;
export { className as class };
export let noTrailingSlash = false;
import { cx } from '../../lib';
const ariaLabel = $$props['aria-label'] || 'Breadcrumb';
const _class = cx('--breadcrumb', noTrailingSlash && '--breadcrumb--no-trailing-slash');
</script>
<nav class={className} aria-label={ariaLabel}>
<ol class={_class}>
<slot />
</ol>
</nav>

View file

@ -0,0 +1,31 @@
<script>
let className = undefined;
export { className as class };
export let href = undefined;
export let isCurrentPage = false;
export let props = {};
import { cx } from '../../lib';
import Link from '../Link';
const ariaCurrent = $$props['aria-current'];
$: _class = cx(
'--breadcrumb-item',
isCurrentPage && ariaCurrent !== 'page' && '--breadcrumb-item--current',
className
);
$: itemProps = { 'aria-current': ariaCurrent, class: cx('--link') };
</script>
{#if href}
<li class={_class} {...props}>
<Link {href} props={itemProps}>
<slot />
</Link>
</li>
{:else}
<li class={_class} {...props}>
<slot props={itemProps} />
</li>
{/if}

View file

@ -0,0 +1,5 @@
import Breadcrumb from './Breadcrumb.svelte';
export default Breadcrumb;
export { default as BreadcrumbItem } from './BreadcrumbItem.svelte';
export { default as BreadcrumbSkeleton } from './Breadcrumb.Skeleton.svelte';

View file

@ -0,0 +1,17 @@
<script>
let className = undefined;
export { className as class };
export let small = false;
export let href = undefined;
export let props = {};
import { cx } from '../../lib';
const _class = cx('--skeleton', '--btn', small && '--btn--sm', className);
</script>
{#if href}
<a {...props} class={_class} {href} role="button">{''}</a>
{:else}
<div {...props} class={_class} />
{/if}

View file

@ -0,0 +1,73 @@
<script>
export let story = undefined;
import { cx } from '../../lib';
import Layout from '../../internal/ui/Layout.svelte';
import Button from './Button.svelte';
import ButtonSkeleton from './Button.Skeleton.svelte';
import Add16 from 'carbon-icons-svelte/lib/Add16';
const {
kind,
disabled,
size,
renderIcon,
iconDescription,
small,
tooltipPosition,
tooltipAlignment
} = $$props;
const regularProps = {
kind,
disabled,
size,
iconDescription,
small
};
const iconOnlyProps = {
kind,
disabled,
size,
renderIcon: Add16,
iconDescription,
tooltipPosition,
tooltipAlignment
};
const setProps = { disabled, small, size, iconDescription };
</script>
<Layout>
<div>
{#if story === 'skeleton'}
<ButtonSkeleton />
&nbsp;
<ButtonSkeleton href="#" />
&nbsp;
<ButtonSkeleton small />
{:else if story === 'inline'}
<Button />
{:else if story === 'icon-only buttons'}
<Button {...iconOnlyProps} hasIconOnly />
{:else if story === 'set of buttons'}
<div class={cx('--btn-set')}>
<Button kind="secondary" {...setProps}>Secondary button</Button>
<Button kind="primary" {...setProps}>Primary button</Button>
</div>
{:else}
<Button {...regularProps}>Button</Button>
&nbsp;
<Button {...regularProps} href="#">Link</Button>
&nbsp;
<Button {...regularProps} as let:props>
<p {...props}>Element</p>
</Button>
&nbsp;
<Button {...regularProps} as let:props>
<a href="#link" {...props}>Custom component</a>
</Button>
{/if}
</div>
</Layout>

View file

@ -0,0 +1,64 @@
import { withKnobs, select, boolean, text } from '@storybook/addon-knobs';
import Component from './Button.Story.svelte';
export default { title: 'Button', decorators: [withKnobs] };
// TODO: add selectable renderIcon for Default, Icon-only stories
const kinds = {
'Primary button (primary)': 'primary',
'Secondary button (secondary)': 'secondary',
'Danger button (danger)': 'danger',
'Ghost button (ghost)': 'ghost'
};
const sizes = {
Default: 'default',
Field: 'field',
Small: 'small'
};
export const Default = () => ({
Component,
props: {
kind: select('Button kind (kind)', kinds, 'primary'),
disabled: boolean('Disabled (disabled)', false),
size: select('Button size (size)', sizes, 'default'),
iconDescription: text('Icon description (iconDescription)', 'Button icon'),
small: boolean('Small (small) - Deprecated in favor of `size`', false)
}
});
export const IconOnlyButtons = () => ({
Component,
props: {
story: 'icon-only buttons',
kind: select('Button kind (kind)', kinds, 'primary'),
disabled: boolean('Disabled (disabled)', false),
size: select('Button size (size)', sizes, 'default'),
iconDescription: text('Icon description (iconDescription)', 'Button icon'),
tooltipPosition: select(
'Tooltip position (tooltipPosition)',
['top', 'right', 'bottom', 'left'],
'bottom'
),
tooltipAlignment: select(
'Tooltip alignment (tooltipAlignment)',
['start', 'center', 'end'],
'center'
)
}
});
export const SetOfButtons = () => ({
Component,
props: {
story: 'set of buttons',
disabled: boolean('Disabled (disabled)', false),
small: boolean('Small (small)', false),
size: select('Button size (size)', sizes, 'default'),
iconDescription: text('Icon description (iconDescription)', 'Button icon')
}
});
export const Skeleton = () => ({ Component, props: { story: 'skeleton' } });

View file

@ -0,0 +1,82 @@
<script>
let className = undefined;
export { className as class };
export let as = undefined;
export let disabled = false;
export let size = 'default';
export let small = false;
export let kind = 'primary';
export let href = undefined;
export let tabindex = '0';
export let type = 'button';
export let renderIcon = undefined;
export let iconDescription = undefined;
export let hasIconOnly = false;
export let tooltipPosition = undefined;
export let tooltipAlignment = undefined;
export let props = {};
import { cx } from '../../lib';
const _class = cx(
'--btn',
size === 'field' && '--btn--field',
(size === 'small' || small) && '--btn--sm',
kind === 'primary' && '--btn--primary',
kind === 'danger' && '--btn--danger',
kind === 'secondary' && '--btn--secondary',
kind === 'ghost' && '--btn--ghost',
kind === 'danger--primary' && '--btn--danger--primary',
kind === 'tertiary' && '--btn--tertiary',
disabled && '--btn--disabled',
hasIconOnly && '--btn--icon-only',
hasIconOnly && '--tooltip__trigger',
hasIconOnly && '--tooltip--a11y',
hasIconOnly && tooltipPosition && `--tooltip--${tooltipPosition}`,
hasIconOnly && tooltipAlignment && `--tooltip--align-${tooltipAlignment}`,
className
);
const buttonProps = {
...props,
tabindex,
class: _class,
disabled,
type: href && !disabled ? undefined : type,
role: 'button',
href
};
</script>
{#if as}
<slot props={buttonProps} />
{:else}
{#if href && !disabled}
<a {...buttonProps} {href} on:click on:mouseover on:mouseenter on:mouseleave>
{#if hasIconOnly}
<span class={cx('--assistive-text')}>{iconDescription}</span>
{/if}
<slot />
{#if renderIcon}
<svelte:component
this={renderIcon}
aria-hidden="true"
class={cx('--btn__icon')}
aria-label={iconDescription} />
{/if}
</a>
{:else}
<button {...buttonProps} on:click on:mouseover on:mouseenter on:mouseleave>
{#if hasIconOnly}
<span class={cx('--assistive-text')}>{iconDescription}</span>
{/if}
<slot />
{#if renderIcon}
<svelte:component
this={renderIcon}
aria-hidden="true"
class={cx('--btn__icon')}
aria-label={iconDescription} />
{/if}
</button>
{/if}
{/if}

View file

@ -0,0 +1,4 @@
import Button from './Button.svelte';
export default Button;
export { default as ButtonSkeleton } from './Button.Skeleton.svelte';

View file

@ -0,0 +1,13 @@
<script>
let className = undefined;
export { className as class };
export let props = {};
import { cx } from '../../lib';
const _class = cx('--form-item', '--checkbox-wrapper', className);
</script>
<div {...props} class={_class}>
<span class={cx('--checkbox-label', '--skeleton')} />
</div>

View file

@ -0,0 +1,35 @@
<script>
export let story = undefined;
const { labelText, indeterminate, disabled, hideLabel, wrapperClassName } = $$props;
import { cx } from '../../lib';
import Layout from '../../internal/ui/Layout.svelte';
import Checkbox from './Checkbox.svelte';
import CheckboxSkeleton from './Checkbox.Skeleton.svelte';
const checkboxProps = {
labelText,
indeterminate,
disabled,
hideLabel,
wrapperClassName
};
</script>
<Layout>
{#if story === 'skeleton'}
<CheckboxSkeleton />
{:else if story === 'unchecked'}
<fieldset class={cx('--fieldset')}>
<legend class={cx('--label')}>Checkbox heading</legend>
<Checkbox {...checkboxProps} id="checkbox-label-1" />
<Checkbox {...checkboxProps} id="checkbox-label-2" />
</fieldset>
{:else}
<fieldset class={cx('--fieldset')}>
<legend class={cx('--label')}>Checkbox heading</legend>
<Checkbox {...checkboxProps} checked id="checkbox-label-1" />
<Checkbox {...checkboxProps} checked id="checkbox-label-2" />
</fieldset>
{/if}
</Layout>

View file

@ -0,0 +1,32 @@
import { withKnobs, boolean, text } from '@storybook/addon-knobs';
import Component from './Checkbox.Story.svelte';
export default { title: 'Checkbox', decorators: [withKnobs] };
export const Checked = () => ({
Component,
props: {
labelText: text('Label text (labelText)', 'Checkbox label'),
indeterminate: boolean('Intermediate (indeterminate)', false),
disabled: boolean('Disabled (disabled)', false),
hideLabel: boolean('No label (hideLabel)', false),
wrapperClass: text('Wrapper CSS class name (wrapperClass)', '')
}
});
export const Unchecked = () => ({
Component,
props: {
story: 'unchecked',
labelText: text('Label text (labelText)', 'Checkbox label'),
indeterminate: boolean('Intermediate (indeterminate)', false),
disabled: boolean('Disabled (disabled)', false),
hideLabel: boolean('No label (hideLabel)', false),
wrapperClass: text('Wrapper CSS class name (wrapperClass)', '')
}
});
export const Skeleton = () => ({
Component,
props: { story: 'skeleton' }
});

View file

@ -0,0 +1,41 @@
<script>
let className = undefined;
export { className as class };
export let checked = false;
export let indeterminate = false;
export let disabled = false;
export let id = undefined;
export let labelText = undefined;
export let hideLabel = false;
export let title = '';
export let wrapperClassName = undefined;
export { wrapperClassName as wrapperClass };
export let props = {};
import { createEventDispatcher } from 'svelte';
import { cx } from '../../lib';
const dispatch = createEventDispatcher();
const _labelClass = cx('--checkbox-label', className);
const _innerLabelClass = cx('--checkbox-label-text', hideLabel && '--visually-hidden');
const _wrapperClass = cx('--form-item', '--checkbox-wrapper', wrapperClassName);
function handleChange(event) {
dispatch('change', { checked: event.target.checked, id, event });
}
</script>
<div class={_wrapperClass}>
<input
{...props}
type="checkbox"
class={cx('--checkbox')}
on:change={handleChange}
{indeterminate}
{disabled}
{checked}
{id} />
<label for={id} class={_labelClass} title={title || null}>
<span class={_innerLabelClass}>{labelText}</span>
</label>
</div>

View file

@ -0,0 +1,4 @@
import Checkbox from './Checkbox.svelte';
export default Checkbox;
export { default as CheckboxSkeleton } from './Checkbox.Skeleton.svelte';

View file

@ -0,0 +1,32 @@
<script>
let className = undefined;
export { className as class };
export let type = 'single';
export let props = {};
import { cx } from '../../lib';
const _class = cx(
'--snippet',
'--skeleton',
type === 'single' && '--snippet--single',
type === 'multi' && '--snippet--multi',
className
);
</script>
{#if type === 'single'}
<div {...props} class={_class}>
<div class={cx('--snippet-container')}>
<span />
</div>
</div>
{:else if type === 'multi'}
<div {...props} class={_class}>
<div class={cx('--snippet-container')}>
<span />
<span />
<span />
</div>
</div>
{/if}

View file

@ -0,0 +1,57 @@
<script>
export let story = undefined;
const { light, feedback, copyLabel, copyButtonDescription, showLessText, showMoreText } = $$props;
import Layout from '../../internal/ui/Layout.svelte';
import CodeSnippet from './CodeSnippet.svelte';
import CodeSnippetSkeleton from './CodeSnippet.Skeleton.svelte';
const inlineProps = { light, feedback, copyLabel };
const singleLineProps = {
feedback,
copyButtonDescription,
'aria-label': $$props['aria-label']
};
const multiLineProps = { feedback, showLessText, showMoreText };
</script>
<Layout>
<div>
{#if story === 'skeleton'}
<div style="width: 800px">
<CodeSnippetSkeleton type="single" props={{ style: 'margin-bottom: 8px' }} />
<CodeSnippetSkeleton type="multi" />
</div>
{:else if story === 'inline'}
<CodeSnippet type="inline" {...inlineProps}>{'node -v'}</CodeSnippet>
{:else if story === 'single line'}
<CodeSnippet type="single" {...singleLineProps}>
{'node -v Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis, veritatis voluptate id incidunt molestiae officia possimus, quasi itaque alias, architecto hic, dicta fugit? Debitis delectus quidem explicabo vitae fuga laboriosam!'}
</CodeSnippet>
{:else if story === 'multi line'}
<CodeSnippet type="multi" {...multiLineProps}>
{`@mixin grid-container {
width: 100%;
padding-right: padding(mobile);
padding-left: padding(mobile);
@include breakpoint(bp--xs--major) {
padding-right: padding(xs);
padding-left: padding(xs);
}
}
$z-indexes: (
modal : 9000,
overlay : 8000,
dropdown : 7000,
header : 6000,
footer : 5000,
hidden : - 1,
overflowHidden: - 1,
floating: 10000
);`}
</CodeSnippet>
{/if}
</div>
</Layout>

View file

@ -0,0 +1,39 @@
import { withKnobs, boolean, text } from '@storybook/addon-knobs';
import Component from './CodeSnippet.Story.svelte';
export default { title: 'CodeSnippet', decorators: [withKnobs] };
export const Inline = () => ({
Component,
props: {
story: 'inline',
light: boolean('Light variant (light)', false),
feedback: text('Feedback text (feedback)', 'Feedback Enabled 👍'),
copyLabel: text('ARIA label for the snippet/copy button (copyLabel)', 'copyable code snippet')
}
});
export const SingleLine = () => ({
Component,
props: {
story: 'single line',
feedback: text('Feedback text (feedback)', 'Feedback Enabled 👍'),
copyButtonDescription: text(
'Copy icon description (copyButtonDescription)',
'copyable code snippet'
),
'aria-label': text('ARIA label of the container (ariaLabel)', 'Container label')
}
});
export const MultiLine = () => ({
Component,
props: {
story: 'multi line',
feedback: text('Feedback text (feedback)', 'Feedback Enabled 👍'),
showMoreText: text('Text for "show more" button (showMoreText)', 'Show more'),
showLessText: text('Text for "show less" button (showLessText)', 'Show less')
}
});
export const Skeleton = () => ({ Component, props: { story: 'skeleton' } });

View file

@ -0,0 +1,83 @@
<script>
let className = undefined;
export { className as class };
export let type = 'single';
export let feedback = undefined;
export let copyButtonDescription = undefined;
export let copyLabel = undefined;
export let showMoreText = 'Show more';
export let showLessText = 'Show less';
export let light = false;
export let props = {};
import { onMount } from 'svelte';
import ChevronDown16 from 'carbon-icons-svelte/lib/ChevronDown16';
import { cx } from '../../lib';
import Button from '../Button';
import Copy from '../Copy';
import CopyButton from '../CopyButton';
const id = Math.random();
let codeRef = undefined;
let expandedCode = false;
let shouldShowMoreLessBtn = false;
onMount(() => {
if (codeRef) {
const { height } = codeRef.getBoundingClientRect();
shouldShowMoreLessBtn = type === 'multi' && height > 255;
}
});
$: _class = cx(
'--snippet',
type && `--snippet--${type}`,
expandedCode && '--snippet--expand',
light && '--snippet--light',
className
);
$: expandCodeBtnText = expandedCode ? showLessText : showMoreText;
</script>
{#if type === 'inline'}
<Copy
on:click
aria-label={copyLabel || $$props['aria-label']}
aria-describedby={id}
class={_class}
{feedback}
{props}>
<code {id}>
<slot />
</code>
</Copy>
{:else}
<div {...props} class={_class}>
<div
role="textbox"
tabindex="0"
class={cx('--snippet-container')}
aria-label={$$props['aria-label'] || copyLabel || 'code-snippet'}>
<code>
<pre bind:this={codeRef}>
<slot />
</pre>
</code>
</div>
<CopyButton on:click {feedback} iconDescription={copyButtonDescription} />
{#if shouldShowMoreLessBtn}
<Button
kind="ghost"
size="small"
class={cx('--snippet-btn--expand')}
on:click={() => {
expandedCode = !expandedCode;
}}>
<span class={cx('--snippet-btn--text')}>{expandCodeBtnText}</span>
<ChevronDown16
aria-label={expandCodeBtnText}
class={cx('--icon-chevron--down', '--snippet__icon')} />
</Button>
{/if}
</div>
{/if}

View file

@ -0,0 +1,4 @@
import CodeSnippet from './CodeSnippet.svelte';
export default CodeSnippet;
export { default as CodeSnippetSkeleton } from './CodeSnippet.Skeleton.svelte';

View file

@ -0,0 +1,45 @@
<script>
let className = undefined;
export { className as class };
export let feedback = 'Copied!';
export let feedbackTimeout = 2000;
export let props = {};
import { createEventDispatcher, onDestroy } from 'svelte';
import { cx } from '../../lib';
const dispatch = createEventDispatcher();
let timeoutId = undefined;
let showFeedback = false;
function handleClick(event) {
showFeedback = true;
timeoutId = setTimeout(() => {
showFeedback = false;
}, feedbackTimeout);
}
onDestroy(() => {
if (timeoutId !== undefined) {
clearTimeout(timeoutId);
timeoutId = undefined;
}
});
$: _class = cx('--btn--copy__feedback', showFeedback && '--btn--copy__feedback--displayed');
</script>
<button
{...props}
type="button"
class={className}
on:click
on:click={handleClick}
on:mouseover
on:mouseenter
on:mouseleave>
<slot />
<div class={_class} data-feedback={feedback} />
</button>

View file

@ -0,0 +1,3 @@
import Copy from './Copy.svelte';
export default Copy;

View file

@ -0,0 +1,10 @@
<script>
import Layout from '../../internal/ui/Layout.svelte';
import CopyButton from './CopyButton.svelte';
</script>
<Layout>
<div style="position: relative;">
<CopyButton {...$$props} />
</div>
</Layout>

View file

@ -0,0 +1,13 @@
import { withKnobs, text, number } from '@storybook/addon-knobs';
import Component from './CopyButton.Story.svelte';
export default { title: 'CopyButton', decorators: [withKnobs] };
export const Default = () => ({
Component,
props: {
feedback: text('The text shown upon clicking (feedback)', 'Copied!'),
feedbackTimeout: number('How long the text is shown upon clicking (feedbackTimeout)', 3000),
iconDescription: text('Feedback icon description (iconDescription)', 'Copy to clipboard')
}
});

View file

@ -0,0 +1,62 @@
<script>
let className = undefined;
export { className as class };
export let iconDescription = 'Copy to clipboard';
export let feedback = 'Copied!';
export let feedbackTimeout = 2000;
export let props = {};
import { createEventDispatcher, onDestroy } from 'svelte';
import Copy16 from 'carbon-icons-svelte/lib/Copy16';
import { cx } from '../../lib';
const dispatch = createEventDispatcher();
let animation = undefined;
let timeoutId = undefined;
onDestroy(() => {
if (timeoutId !== undefined) {
clearTimeout(timeoutId);
timeoutId = undefined;
}
});
function handleClick(event) {
animation = 'fade-in';
timeoutId = setTimeout(() => {
animation = 'fade-out';
}, feedbackTimeout);
}
function handleAnimationEnd(event) {
if (event.animationName === 'hide-feedback') {
animation = undefined;
}
}
$: _class = cx(
'--snippet-button', // TODO: deprecated?
'--copy-btn',
animation && '--copy-btn--animating',
animation && `--copy-btn--${animation}`,
className
);
</script>
<button
{...props}
type="button"
aria-label={iconDescription}
title={iconDescription}
class={_class}
on:click
on:click={handleClick}
on:mouseover
on:mouseenter
on:mouseleave
on:animationend
on:animationend={handleAnimationEnd}>
<span class={cx('--assistive-text', '--copy-btn__feedback')}>{feedback}</span>
<Copy16 class={cx('--snippet__icon')} />
</button>

View file

@ -0,0 +1,3 @@
import CopyButton from './CopyButton.svelte';
export default CopyButton;

View file

@ -0,0 +1,67 @@
<script>
export let story = undefined;
export let status = undefined;
export let iconDescription = undefined;
export let description = undefined;
export let successDelay = undefined;
import Layout from '../../internal/ui/Layout.svelte';
import Button from '../Button';
import InlineLoading from './InlineLoading.svelte';
const props = { status, iconDescription, description, successDelay };
let isSubmitting = false;
let success = false;
let ariaLive = 'off';
let loadingDescription = 'Submitting...';
function handleSubmit() {
isSubmitting = true;
ariaLive = 'assertive';
setTimeout(() => {
isSubmitting = false;
loadingDescription = 'Submitted!';
success = true;
setTimeout(() => {
success = false;
isSubmitting = false;
loadingDescription = 'Submitting...';
ariaLive = 'off';
}, 1500);
}, 2000);
}
$: disabled = isSubmitting || success;
</script>
<style>
.wrapper {
display: flex;
width: 300px;
}
:global(.loader) {
margin-left: 1rem;
}
</style>
<Layout>
{#if story === 'ux-example'}
<div class="wrapper">
<Button kind="secondary" {disabled}>Cancel</Button>
{#if disabled}
<InlineLoading
class="loader"
description={loadingDescription}
status={success ? 'finished' : 'active'}
aria-live={ariaLive} />
{:else}
<Button on:click={handleSubmit}>Submit</Button>
{/if}
</div>
{:else}
<InlineLoading {...props} />
{/if}
</Layout>

View file

@ -0,0 +1,30 @@
import { withKnobs, select, text, number } from '@storybook/addon-knobs';
import Component from './InlineLoading.Story.svelte';
export default { title: 'InlineLoading', decorators: [withKnobs] };
export const Default = () => ({
Component,
props: {
status: select(
'Loading status (status)',
['inactive', 'active', 'finished', 'error'],
'active'
),
iconDescription: text('Icon description (iconDescription)', 'Active loading indicator'),
description: text('Loading progress description (description)', 'Loading data...'),
successDelay: number(
'The duration for successful state before `onSuccess` fires (successDelay)',
1500
)
}
});
export const UxExample = () => ({
Component,
props: { story: 'ux-example' }
});
UxExample.story = {
name: 'UX Example'
};

View file

@ -0,0 +1,52 @@
<script>
let className = undefined;
export { className as class };
export let success = false; // TODO: deprecate
export let status = success ? 'finished' : 'active';
export let description = undefined;
export let iconDescription = undefined;
export let successDelay = 1500;
export let props = {};
import { createEventDispatcher, onDestroy } from 'svelte';
import CheckmarkFilled16 from 'carbon-icons-svelte/lib/CheckmarkFilled16';
import Error20 from 'carbon-icons-svelte/lib/Error20';
import { cx } from '../../lib';
import Loading from '../Loading';
const dispatch = createEventDispatcher();
const _class = cx('--inline-loading', className);
let timeoutId = undefined;
onDestroy(() => {
if (timeoutId !== undefined) {
clearTimeout(timeoutId);
timeoutId = undefined;
}
});
$: if (status === 'finished') {
timeoutId = setTimeout(() => {
dispatch('success');
}, successDelay);
}
</script>
<div {...props} class={_class} aria-live={$$props['aria-live'] || 'assertive'}>
<div class={cx('--inline-loading__animation')}>
{#if status === 'error'}
<Error20 class={cx('--inline-loading--error')} />
{:else if status === 'finished'}
<CheckmarkFilled16 class={cx('--inline-loading__checkmark-container')} />
{:else if status === 'inactive' || status === 'active'}
<Loading
small
description={iconDescription}
withOverlay={false}
active={status === 'active'} />
{/if}
</div>
{#if description}
<div class={cx('--inline-loading__text')}>{description}</div>
{/if}
</div>

View file

@ -0,0 +1,3 @@
import InlineLoading from './InlineLoading.svelte';
export default InlineLoading;

View file

@ -0,0 +1,14 @@
<script>
export let href = undefined;
export let inline = undefined;
export let disabled = undefined;
import Layout from '../../internal/ui/Layout.svelte';
import Link from './Link.svelte';
</script>
<Layout>
<div>
<Link {href} {inline} {disabled}>Link</Link>
</div>
</Layout>

View file

@ -0,0 +1,13 @@
import { withKnobs, boolean, text } from '@storybook/addon-knobs';
import Component from './Link.Story.svelte';
export default { title: 'Link', decorators: [withKnobs] };
export const Default = () => ({
Component,
props: {
href: text('The link href (href)', '#'),
inline: boolean('Use the in-line variant (inline)', false),
disabled: boolean('Disabled (disabled)', false)
}
});

View file

@ -0,0 +1,27 @@
<script>
let className = undefined;
export { className as class };
export let href = undefined;
export let disabled = false;
export let inline = false;
export let props = {};
import { cx } from '../../lib';
const _class = cx(
'--link',
disabled && '--link--disabled',
inline && '--link--inline',
className
);
</script>
{#if disabled}
<p {...props} class={_class}>
<slot />
</p>
{:else}
<a {...props} class={_class} {href}>
<slot />
</a>
{/if}

View file

@ -0,0 +1,3 @@
import Link from './Link.svelte';
export default Link;

View file

@ -0,0 +1,13 @@
<script>
let className = undefined;
export { className as class };
export let props = {};
import { cx } from '../../lib';
const _class = cx('--list__item', className);
</script>
<li {...props} class={_class}>
<slot />
</li>

View file

@ -0,0 +1,3 @@
import ListItem from './ListItem.svelte';
export default ListItem;

View file

@ -0,0 +1,8 @@
<script>
import Layout from '../../internal/ui/Layout.svelte';
import Loading from './Loading.svelte';
</script>
<Layout>
<Loading {...$$props} />
</Layout>

View file

@ -0,0 +1,14 @@
import { withKnobs, boolean, text } from '@storybook/addon-knobs';
import Component from './Loading.Story.svelte';
export default { title: 'Loading', decorators: [withKnobs] };
export const Default = () => ({
Component,
props: {
active: boolean('Active (active)', true),
withOverlay: boolean('With overlay (withOverlay)', false),
small: boolean('Small (small)', false),
description: text('Description (description)', 'Active loading indicator')
}
});

View file

@ -0,0 +1,57 @@
<script>
let className = undefined;
export { className as class };
export let active = true;
export let withOverlay = true;
export let small = false;
export let description = 'Active loading indicator';
export let props = {};
import { cx } from '../../lib';
const loadingId = `loading-id-${Math.random()}`;
const spinnerRadius = small ? '26.8125' : '37.5';
const _class = cx(
'--loading',
small && '--loading--small',
!active && '--loading--stop',
className
);
const _overlayClass = cx('--loading-overlay', !active && '--loading-overlay--stop');
</script>
{#if withOverlay}
<div class={_overlayClass}>
<div
{...props}
aria-atomic="true"
aria-labelledby={loadingId}
aria-live={active ? 'assertive' : 'off'}
class={_class}>
<label id={loadingId} class={cx('--visually-hidden')}>{description}</label>
<svg class={cx('--loading__svg')} viewBox="-75 -75 150 150">
<title>{description}</title>
{#if small}
<circle class={cx('--loading__background')} cx="0" cy="0" r={spinnerRadius} />
{/if}
<circle class={cx('--loading__stroke')} cx="0" cy="0" r={spinnerRadius} />
</svg>
</div>
</div>
{:else}
<div
{...props}
aria-atomic="true"
aria-labelledby={loadingId}
aria-live={active ? 'assertive' : 'off'}
class={_class}>
<label id={loadingId} class={cx('--visually-hidden')}>{description}</label>
<svg class={cx('--loading__svg')} viewBox="-75 -75 150 150">
<title>{description}</title>
{#if small}
<circle class={cx('--loading__background')} cx="0" cy="0" r={spinnerRadius} />
{/if}
<circle class={cx('--loading__stroke')} cx="0" cy="0" r={spinnerRadius} />
</svg>
</div>
{/if}

View file

@ -0,0 +1,3 @@
import Loading from './Loading.svelte';
export default Loading;

View file

@ -0,0 +1,37 @@
<script>
export let story = undefined;
import Layout from '../../internal/ui/Layout.svelte';
import ListItem from '../ListItem';
import OrderedList from './OrderedList.svelte';
</script>
<Layout>
<div>
{#if story === 'nested'}
<OrderedList>
<ListItem>
Unordered List level 1
<OrderedList nested>
<ListItem>Ordered List level 2</ListItem>
<ListItem>
Ordered List level 2
<OrderedList nested>
<ListItem>Ordered List level 2</ListItem>
<ListItem>Ordered List level 2</ListItem>
</OrderedList>
</ListItem>
</OrderedList>
</ListItem>
<ListItem>Ordered List level 1</ListItem>
<ListItem>Ordered List level 1</ListItem>
</OrderedList>
{:else}
<OrderedList>
<ListItem>Ordered List level 1</ListItem>
<ListItem>Ordered List level 1</ListItem>
<ListItem>Ordered List level 1</ListItem>
</OrderedList>
{/if}
</div>
</Layout>

View file

@ -0,0 +1,8 @@
import { withKnobs } from '@storybook/addon-knobs';
import Component from './OrderedList.Story.svelte';
export default { title: 'Ordered List', decorators: [withKnobs] };
export const Default = () => ({ Component });
export const Nested = () => ({ Component, props: { story: 'nested' } });

View file

@ -0,0 +1,14 @@
<script>
let className = undefined;
export { className as class };
export let nested = false;
export let props = {};
import { cx } from '../../lib';
const _class = cx('--list--ordered', nested && '--list--nested', className);
</script>
<ol {...props} class={_class}>
<slot />
</ol>

View file

@ -0,0 +1,3 @@
import OrderedList from './OrderedList.svelte';
export default OrderedList;

View file

@ -0,0 +1,10 @@
<script>
import Layout from '../../internal/ui/Layout.svelte';
import SkeletonText from './SkeletonText.svelte';
</script>
<Layout>
<div style="width: 300px">
<SkeletonText {...$$props} />
</div>
</Layout>

View file

@ -0,0 +1,18 @@
import { withKnobs, select, boolean, number } from '@storybook/addon-knobs';
import Component from './SkeletonText.Story.svelte';
export default { title: 'SkeletonText', decorators: [withKnobs] };
export const Default = () => ({
Component,
props: {
heading: boolean('Skeleton text at a larger size (heading)'),
paragraph: boolean('Use multiple lines of text (paragraph)'),
lineCount: number('The number of lines in a paragraph (lineCount)', 3),
width: select(
'Width (in px or %) of single line of text or max-width of paragraph lines (width)',
{ '100%': '100%', '250px': '250px' },
'100%'
)
}
});

View file

@ -0,0 +1,37 @@
<script>
let className = undefined;
export { className as class };
export let paragraph = false;
export let lineCount = 3;
export let width = '100%';
export let heading = false;
export let props = {};
import { cx } from '../../lib';
const randoms = [0.973051493507435, 0.15334737213558558, 0.5671034553053769];
const _class = cx('--skeleton__text', heading && '--skeleton__heading', className);
const widthNum = parseInt(width, 10);
const widthPx = width.includes('px');
const widthPercent = width.includes('%');
let lines = [];
$: if (paragraph) {
for (let i = 0; i < lineCount; i++) {
const min = widthPx ? widthNum - 75 : 0;
const max = widthPx ? widthNum : 75;
const randomWidth = Math.floor(randoms[i % 3] * (max - min + 1)) + min + 'px';
lines = [...lines, { width: widthPx ? randomWidth : `calc(${width} - ${randomWidth})` }];
}
}
</script>
{#if paragraph}
<div>
{#each lines as { width }}
<p {...props} class={_class} style={`width: ${width};`} />
{/each}
</div>
{:else}
<p {...props} class={_class} style={`width: ${width};`} />
{/if}

View file

@ -0,0 +1,3 @@
import SkeletonText from './SkeletonText.svelte';
export default SkeletonText;

View file

@ -0,0 +1,5 @@
<script>
import { cx } from '../../lib';
</script>
<span class={cx('--tag', '--skeleton')} />

View file

@ -0,0 +1,23 @@
<script>
export let story = undefined;
export let type = undefined;
export let disabled = undefined;
export let filter = undefined;
export let slot = undefined;
import Layout from '../../internal/ui/Layout.svelte';
import Tag from './Tag.svelte';
import TagSkeleton from './Tag.Skeleton.svelte';
</script>
<Layout>
<div>
{#if story === 'filter'}
<Tag {filter}>{slot}</Tag>
{:else if story === 'skeleton'}
<TagSkeleton />
{:else}
<Tag {disabled} {type}>{slot}</Tag>
{/if}
</div>
</Layout>

View file

@ -0,0 +1,29 @@
import { withKnobs, select, boolean, text } from '@storybook/addon-knobs';
import Component from './Tag.Story.svelte';
import { TYPES } from './constants';
export default { title: 'Tag', decorators: [withKnobs] };
export const Default = () => ({
Component,
props: {
type: select(
'Tag type (type)',
Object.keys(TYPES).reduce((items, item) => ({ ...items, [`${item} (${item})`]: item }), {}),
'red'
),
disabled: boolean('Disabled (disabled)', false),
slot: text('Content ($$slot)', 'This is not a tag')
}
});
export const Filter = () => ({
Component,
props: {
story: 'filter',
filter: true,
slot: text('Content ($$slot)', 'This is not a tag')
}
});
export const Skeleton = () => ({ Component, props: { story: 'skeleton' } });

View file

@ -0,0 +1,40 @@
<script>
let className = undefined;
export { className as class };
export let type = undefined;
export let disabled = false;
export let filter = false;
export let title = 'Clear filter';
export let props = {};
import Close16 from 'carbon-icons-svelte/lib/Close16';
import { cx } from '../../lib';
import { TYPES } from './constants';
const _class = cx(
'--tag',
type && `--tag--${type}`,
disabled && '--tag--disabled',
filter && '--tag--filter',
className
);
</script>
{#if filter}
<span
{...props}
tabindex="0"
on:click
on:mouseover
on:mouseenter
on:mouseleave
class={_class}
{title}>
<slot>{TYPES[type]}</slot>
<Close16 aria-label={title} />
</span>
{:else}
<span {...props} on:click on:mouseover on:mouseenter on:mouseleave class={_class}>
<slot>{TYPES[type]}</slot>
</span>
{/if}

View file

@ -0,0 +1,12 @@
export const TYPES = {
red: 'Red',
magenta: 'Magenta',
purple: 'Purple',
blue: 'Blue',
cyan: 'Cyan',
teal: 'Teal',
green: 'Green',
gray: 'Gray',
'cool-gray': 'Cool-Gray',
'warm-gray': 'Warm-Gray'
};

View file

@ -0,0 +1,4 @@
import Tag from './Tag.svelte';
export default Tag;
export { default as TagSkeleton } from './Tag.Skeleton.svelte';

View file

@ -0,0 +1,17 @@
<script>
let className = undefined;
export { className as class };
export let hideLabel = false;
export let props = {};
import { cx } from '../../lib';
const _class = cx('--form-item', className);
</script>
<div {...props} class={_class}>
{#if !hideLabel}
<span class={cx('--label', '--skeleton')} />
{/if}
<div class={cx('--skeleton', '--text-area')} />
</div>

View file

@ -0,0 +1,17 @@
<script>
export let story = undefined;
import Layout from '../../internal/ui/Layout.svelte';
import TextArea from './TextArea.svelte';
import TextAreaSkeleton from './TextArea.Skeleton.svelte';
let value = '';
</script>
<Layout>
{#if story === 'skeleton'}
<TextAreaSkeleton {...$$props} />
{:else}
<TextArea {...$$props} bind:value />
{/if}
</Layout>

View file

@ -0,0 +1,23 @@
import { withKnobs, boolean, text, number } from '@storybook/addon-knobs';
import Component from './TextArea.Story.svelte';
export default { title: 'TextArea', decorators: [withKnobs] };
export const Default = () => ({
Component,
props: {
id: 'text-area',
disabled: boolean('Disabled (disabled)', false),
light: boolean('Light variant (light)', false),
hideLabel: boolean('No label (hideLabel)', false),
labelText: text('Label text (labelText)', 'Text Area label'),
invalid: boolean('Show form validation UI (invalid)', false),
invalidText: text('Content of form validation UI (invalidText)', 'A valid value is required'),
helperText: text('Helper text (helperText)', 'Optional helper text.'),
placeholder: text('Placeholder text (placeholder)', 'Placeholder text.'),
cols: number('Columns (columns)', 50),
rows: number('Rows (rows)', 4)
}
});
export const Skeleton = () => ({ Component, props: { story: 'skeleton' } });

View file

@ -0,0 +1,89 @@
<script>
let className = undefined;
export { className as class };
export let cols = 50;
export let disabled = false;
export let id = Math.random();
export let labelText = undefined;
export let placeholder = '';
export let rows = 4;
export let value = undefined;
export let invalid = false;
export let invalidText = undefined;
export let helperText = undefined;
export let hideLabel = false;
export let light = false;
export let props = {};
import { createEventDispatcher } from 'svelte';
import WarningFilled16 from 'carbon-icons-svelte/lib/WarningFilled16';
import { cx } from '../../lib';
const dispatch = createEventDispatcher();
const errorId = `${id}-error`;
const _labelClass = cx(
'--label',
hideLabel && '--visually-hidden',
disabled && '--label--disabled'
);
const _helperTextClass = cx('--form__helper-text', disabled && '--form__helper-text--disabled');
const _textAreaClass = cx(
'--text-area',
light && '--text-area--light',
invalid && '--text-area--invalid',
className
);
function handleClick(event) {
if (!disabled) {
dispatch('click', event);
}
}
function handleChange(event) {
if (!disabled) {
dispatch('change', event);
}
}
function handleInput(event) {
if (!disabled) {
dispatch('input', event);
}
}
</script>
<div class={cx('--form-item')}>
{#if labelText && !hideLabel}
<label for={id} class={_labelClass}>{labelText}</label>
{/if}
{#if helperText}
<div class={_helperTextClass}>{helperText}</div>
{/if}
<div class={cx('--text-area__wrapper')} data-invalid={invalid || undefined}>
{#if invalid}
<WarningFilled16 class={cx('--text-area__invalid-icon')} />
{/if}
<textarea
{...props}
on:click
on:click={handleClick}
on:change
on:change={handleChange}
on:input
on:input={handleInput}
class={_textAreaClass}
aria-invalid={invalid || undefined}
aria-describedby={invalid ? errorId : undefined}
{disabled}
{id}
{cols}
{rows}
{value}
{placeholder}
{value} />
</div>
{#if invalid}
<div class={cx('--form-requirement')} id={errorId}>{invalidText}</div>
{/if}
</div>

View file

@ -0,0 +1,4 @@
import TextArea from './TextArea.svelte';
export default TextArea;
export { default as TextAreaSkeleton } from './TextArea.Skeleton.svelte';

View file

@ -0,0 +1,24 @@
<script>
let className = undefined;
export { className as class };
export let id = undefined;
export let labelText = undefined;
export let props = {};
import { cx } from '../../lib';
const _class = cx('--form-item', className);
const ariaLabel = labelText ? null : $$props['aria-label'] || 'Toggle is loading';
</script>
<div {...props} class={_class}>
<input type="checkbox" class={cx('--toggle --skeleton')} {id} />
<label class={cx('--toggle__label', '--skeleton')} aria-label={ariaLabel} for={id}>
{#if labelText}
<span class={cx('--toggle__label-text')}>{labelText}</span>
{/if}
<span class={cx('--toggle__text--left')} />
<span class={cx('--toggle__appearance')} />
<span class={cx('--toggle__text--right')} />
</label>
</div>

View file

@ -0,0 +1,17 @@
<script>
export let story = undefined;
import Layout from '../../internal/ui/Layout.svelte';
import Toggle from './Toggle.svelte';
import ToggleSkeleton from './Toggle.Skeleton.svelte';
</script>
<Layout>
{#if story === 'toggled'}
<Toggle {...$$props} id="toggle-1" toggled />
{:else if story === 'skeleton'}
<ToggleSkeleton />
{:else}
<Toggle {...$$props} id="toggle-1" />
{/if}
</Layout>

View file

@ -0,0 +1,31 @@
import { withKnobs, boolean, text } from '@storybook/addon-knobs';
import Component from './Toggle.Story.svelte';
export default { title: 'Toggle', decorators: [withKnobs] };
export const Default = () => ({
Component,
props: {
labelText: text('Label toggle input control (labelText)', ''),
'aria-label': text('ARIA label of the toggle (aria-label)', ''),
labelA: text('Label for untoggled state (labelA)', 'Off'),
labelB: text('Label for toggled state (labelB)', 'On'),
disabled: boolean('Disabled (disabled)', false)
}
});
Default.story = { name: 'Default (untoggled)' };
export const Toggled = () => ({
Component,
props: {
story: 'toggled',
labelText: text('Label toggle input control (labelText)', ''),
'aria-label': text('ARIA label of the toggle (aria-label)', ''),
labelA: text('Label for untoggled state (labelA)', 'Off'),
labelB: text('Label for toggled state (labelB)', 'On'),
disabled: boolean('Disabled (disabled)', false)
}
});
export const Skeleton = () => ({ Component, props: { story: 'skeleton' } });

View file

@ -0,0 +1,56 @@
<script>
let className = undefined;
export { className as class };
export let id = Math.random();
export let toggled = false;
export let disabled = false;
export let labelText = undefined;
export let labelA = 'Off';
export let labelB = 'On';
export let props = {};
import { createEventDispatcher } from 'svelte';
import { cx } from '../../lib';
const dispatch = createEventDispatcher();
const _class = cx('--form-item', className);
const ariaLabel = labelText ? undefined : $$props['aria-label'] || 'Toggle';
let inputRef = undefined;
function handleChange(event) {
dispatch('change', event);
dispatch('toggle', { checked: inputRef.checked, id, event });
}
function handleKeyUp(event) {
if (event.key === 'Enter') {
if (inputRef) {
inputRef.checked = !inputRef.checked;
}
handleChange(event);
}
}
</script>
<div class={_class}>
<input
{...props}
type="checkbox"
class={cx('--toggle-input')}
bind:this={inputRef}
checked={toggled}
{disabled}
{id}
on:change
on:change={handleChange}
on:keyup
on:keyup={handleKeyUp} />
<label class={cx('--toggle-input__label')} for={id} aria-label={ariaLabel}>
{labelText}
<span class={cx('--toggle__switch')}>
<span aria-hidden="true" class={cx('--toggle__text--off')}>{labelA}</span>
<span aria-hidden="true" class={cx('--toggle__text--on')}>{labelB}</span>
</span>
</label>
</div>

View file

@ -0,0 +1,4 @@
import Toggle from './Toggle.svelte';
export default Toggle;
export { default as ToggleSkeleton } from './Toggle.Skeleton.svelte';

View file

@ -0,0 +1,26 @@
<script>
let className = undefined;
export { className as class };
export let id = undefined;
export let labelText = undefined;
export let props = {};
import { cx } from '../../lib';
const _class = cx('--form-item', className);
const ariaLabel = labelText ? undefined : $$props['aria-label'] || 'Toggle is loading';
</script>
<div {...props} class={_class}>
<input type="checkbox" class={cx('--toggle', '--toggle--small', '--skeleton')} {id} />
<label class={cx('--toggle__label --skeleton')} for={id}>
{#if labelText}
<span class={cx('--toggle__label-text')}>{labelText}</span>
{/if}
<span class={cx('--toggle__appearance')}>
<svg class={cx('--toggle__check')} width="6" height="5" viewBox="0 0 6 5">
<path d="M2.2403 2.7299L4.9245 0 6 1.1117 2.2384 5 0 2.6863 1.0612 1.511z" />
</svg>
</span>
</label>
</div>

View file

@ -0,0 +1,17 @@
<script>
export let story = undefined;
import Layout from '../../internal/ui/Layout.svelte';
import ToggleSmall from './ToggleSmall.svelte';
import ToggleSmallSkeleton from './ToggleSmall.Skeleton.svelte';
</script>
<Layout>
{#if story === 'toggled'}
<ToggleSmall {...$$props} id="toggle-1" toggled />
{:else if story === 'skeleton'}
<ToggleSmallSkeleton />
{:else}
<ToggleSmall {...$$props} id="toggle-1" />
{/if}
</Layout>

View file

@ -0,0 +1,31 @@
import { withKnobs, boolean, text } from '@storybook/addon-knobs';
import Component from './ToggleSmall.Story.svelte';
export default { title: 'ToggleSmall', decorators: [withKnobs] };
export const Default = () => ({
Component,
props: {
labelText: text('Label toggle input control (labelText)', ''),
'aria-label': text('ARIA label of the toggle (aria-label)', ''),
labelA: text('Label for untoggled state (labelA)', 'Off'),
labelB: text('Label for toggled state (labelB)', 'On'),
disabled: boolean('Disabled (disabled)', false)
}
});
Default.story = { name: 'Default (untoggled)' };
export const Toggled = () => ({
Component,
props: {
story: 'toggled',
labelText: text('Label toggle input control (labelText)', ''),
'aria-label': text('ARIA label of the toggle (aria-label)', ''),
labelA: text('Label for untoggled state (labelA)', 'Off'),
labelB: text('Label for toggled state (labelB)', 'On'),
disabled: boolean('Disabled (disabled)', false)
}
});
export const Skeleton = () => ({ Component, props: { story: 'skeleton' } });

Some files were not shown because too many files have changed in this diff Show more