mirror of
https://github.com/carbon-design-system/carbon-components-svelte.git
synced 2025-09-15 02:11:05 +00:00
parent
fd3698b49a
commit
85c4a14b2a
25 changed files with 546 additions and 28 deletions
|
@ -31,6 +31,11 @@ Currently, the following components are supported:
|
||||||
- Copy
|
- Copy
|
||||||
- CopyButton
|
- CopyButton
|
||||||
- DataTableSkeleton
|
- DataTableSkeleton
|
||||||
|
- FileUploader
|
||||||
|
- FileUploaderButton
|
||||||
|
- FileUploaderItem
|
||||||
|
- FileUploaderDropContainer
|
||||||
|
- Filename
|
||||||
- Form
|
- Form
|
||||||
- FormGroup
|
- FormGroup
|
||||||
- FormItem
|
- FormItem
|
||||||
|
|
|
@ -70,4 +70,4 @@
|
||||||
}</script><style>#root[hidden],
|
}</script><style>#root[hidden],
|
||||||
#docs-root[hidden] {
|
#docs-root[hidden] {
|
||||||
display: none !important;
|
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.e24a7c3ee0d97007ea8a.bundle.js"></script><script src="vendors~main.e24a7c3ee0d97007ea8a.bundle.js"></script><script src="main.e24a7c3ee0d97007ea8a.bundle.js"></script></body></html>
|
}</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.9ba103da9a704f605f83.bundle.js"></script><script src="vendors~main.9ba103da9a704f605f83.bundle.js"></script><script src="main.9ba103da9a704f605f83.bundle.js"></script></body></html>
|
2
docs/main.9ba103da9a704f605f83.bundle.js
Normal file
2
docs/main.9ba103da9a704f605f83.bundle.js
Normal file
File diff suppressed because one or more lines are too long
1
docs/main.9ba103da9a704f605f83.bundle.js.map
Normal file
1
docs/main.9ba103da9a704f605f83.bundle.js.map
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"main.9ba103da9a704f605f83.bundle.js","sources":["webpack:///main.9ba103da9a704f605f83.bundle.js"],"mappings":"AAAA","sourceRoot":""}
|
File diff suppressed because one or more lines are too long
|
@ -1 +0,0 @@
|
||||||
{"version":3,"file":"main.e24a7c3ee0d97007ea8a.bundle.js","sources":["webpack:///main.e24a7c3ee0d97007ea8a.bundle.js"],"mappings":"AAAA","sourceRoot":""}
|
|
|
@ -1,2 +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()}([]);
|
!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.e24a7c3ee0d97007ea8a.bundle.js.map
|
//# sourceMappingURL=runtime~main.9ba103da9a704f605f83.bundle.js.map
|
|
@ -1 +1 @@
|
||||||
{"version":3,"file":"runtime~main.e24a7c3ee0d97007ea8a.bundle.js","sources":["webpack:///runtime~main.ce61f8335d8fdea2cda4.bundle.js"],"mappings":"AAAA","sourceRoot":""}
|
{"version":3,"file":"runtime~main.9ba103da9a704f605f83.bundle.js","sources":["webpack:///runtime~main.ce61f8335d8fdea2cda4.bundle.js"],"mappings":"AAAA","sourceRoot":""}
|
File diff suppressed because one or more lines are too long
1
docs/vendors~main.9ba103da9a704f605f83.bundle.js.map
Normal file
1
docs/vendors~main.9ba103da9a704f605f83.bundle.js.map
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"vendors~main.9ba103da9a704f605f83.bundle.js","sources":["webpack:///vendors~main.9ba103da9a704f605f83.bundle.js"],"mappings":"AAAA;;;;;AAyqeA;;;;;AAi9JA;;;;;AAkkEA;;;;;;;;;AAukBA;;;AA8odA;;;;;;;;AAg/BA;;;;;;;;AAqEA;;;;;;;;AAkTA;;;;;;;AAyrDA;;;;;;;AAy7CA;;;;;;;AAufA;;;;;;;AAfA","sourceRoot":""}
|
|
@ -1 +0,0 @@
|
||||||
{"version":3,"file":"vendors~main.e24a7c3ee0d97007ea8a.bundle.js","sources":["webpack:///vendors~main.e24a7c3ee0d97007ea8a.bundle.js"],"mappings":"AAAA;;;;;AAwqeA;;;;;AAi9JA;;;;;AAkkEA;;;;;;;;;AAukBA;;;AA8odA;;;;;;;;AAg/BA;;;;;;;;AAqEA;;;;;;;;AAkTA;;;;;;;AAyrDA;;;;;;;AAy7CA;;;;;;;AAufA;;;;;;;AAfA","sourceRoot":""}
|
|
|
@ -23,13 +23,10 @@
|
||||||
|
|
||||||
let buttonRef = undefined;
|
let buttonRef = undefined;
|
||||||
|
|
||||||
$: {
|
$: if (ctx && buttonRef) {
|
||||||
if (ctx && buttonRef) {
|
ctx.declareRef({ name: 'buttonRef', ref: buttonRef });
|
||||||
ctx.declareRef({ name: 'buttonRef', ref: buttonRef });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
$: _class = cx(
|
||||||
const _class = cx(
|
|
||||||
'--btn',
|
'--btn',
|
||||||
size === 'field' && '--btn--field',
|
size === 'field' && '--btn--field',
|
||||||
(size === 'small' || small) && '--btn--sm',
|
(size === 'small' || small) && '--btn--sm',
|
||||||
|
@ -47,7 +44,7 @@
|
||||||
hasIconOnly && tooltipAlignment && `--tooltip--align-${tooltipAlignment}`,
|
hasIconOnly && tooltipAlignment && `--tooltip--align-${tooltipAlignment}`,
|
||||||
className
|
className
|
||||||
);
|
);
|
||||||
const buttonProps = {
|
$: buttonProps = {
|
||||||
role: 'button',
|
role: 'button',
|
||||||
type: href && !disabled ? undefined : type,
|
type: href && !disabled ? undefined : type,
|
||||||
tabindex,
|
tabindex,
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<table on:click on:mouseover on:mouseenter on:mouseleave {style} class={_class}>
|
<table on:click on:mouseover on:mouseenter on:mouseleave class={_class} {style}>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
{#each columns as column, i (column)}
|
{#each columns as column, i (column)}
|
||||||
|
|
17
src/components/FileUploader/FileUploader.Skeleton.svelte
Normal file
17
src/components/FileUploader/FileUploader.Skeleton.svelte
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<script>
|
||||||
|
let className = undefined;
|
||||||
|
export { className as class };
|
||||||
|
export let style = undefined;
|
||||||
|
|
||||||
|
import SkeletonText from '../SkeletonText';
|
||||||
|
import { ButtonSkeleton } from '../Button';
|
||||||
|
import { cx } from '../../lib';
|
||||||
|
|
||||||
|
const _class = cx('--form-item', className);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div on:click on:mouseover on:mouseenter on:mouseleave class={_class} {style}>
|
||||||
|
<SkeletonText heading width="100px" />
|
||||||
|
<SkeletonText width="225px" class={cx('--label-description')} />
|
||||||
|
<ButtonSkeleton />
|
||||||
|
</div>
|
65
src/components/FileUploader/FileUploader.Story.svelte
Normal file
65
src/components/FileUploader/FileUploader.Story.svelte
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
<script>
|
||||||
|
export let story = undefined;
|
||||||
|
|
||||||
|
import Layout from '../../internal/ui/Layout.svelte';
|
||||||
|
import { cx } from '../../lib';
|
||||||
|
import Button from '../Button';
|
||||||
|
import FileUploader from './FileUploader.svelte';
|
||||||
|
import FileUploaderButton from './FileUploaderButton.svelte';
|
||||||
|
import FileUploaderItem from './FileUploaderItem.svelte';
|
||||||
|
import FileUploaderDropContainer from './FileUploaderDropContainer.svelte';
|
||||||
|
import FileUploaderSkeleton from './FileUploader.Skeleton.svelte';
|
||||||
|
|
||||||
|
let files = [];
|
||||||
|
|
||||||
|
$: disabled = files.length === 0;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Layout>
|
||||||
|
<div>
|
||||||
|
{#if story === 'button'}
|
||||||
|
<FileUploaderButton {...$$props} />
|
||||||
|
{:else if story === 'drop container'}
|
||||||
|
<FileUploaderDropContainer
|
||||||
|
{...$$props}
|
||||||
|
on:add={({ detail }) => {
|
||||||
|
console.log(detail);
|
||||||
|
}} />
|
||||||
|
{:else if story === 'item'}
|
||||||
|
<FileUploaderItem
|
||||||
|
{...$$props}
|
||||||
|
on:delete={({ detail }) => {
|
||||||
|
console.log(detail);
|
||||||
|
}}
|
||||||
|
on:click={() => {
|
||||||
|
console.log('click');
|
||||||
|
}} />
|
||||||
|
{:else if story === 'uploader'}
|
||||||
|
<div class={cx('--file__container')}>
|
||||||
|
<FileUploader
|
||||||
|
{...$$props}
|
||||||
|
bind:files
|
||||||
|
on:add={({ detail }) => {
|
||||||
|
console.log('add', detail);
|
||||||
|
}}
|
||||||
|
on:remove={({ detail }) => {
|
||||||
|
console.log('remove', detail);
|
||||||
|
}} />
|
||||||
|
<Button
|
||||||
|
kind="secondary"
|
||||||
|
size="small"
|
||||||
|
style={'margin-top: 1rem'}
|
||||||
|
{disabled}
|
||||||
|
on:click={() => {
|
||||||
|
files = [];
|
||||||
|
}}>
|
||||||
|
Clear File{files.length === 1 ? '' : 's'}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
{:else if story === 'skeleton'}
|
||||||
|
<div style="width: 500px">
|
||||||
|
<FileUploaderSkeleton />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</Layout>
|
98
src/components/FileUploader/FileUploader.stories.js
Normal file
98
src/components/FileUploader/FileUploader.stories.js
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
import { withKnobs, text, select, boolean, number, array } from '@storybook/addon-knobs';
|
||||||
|
import Component from './FileUploader.Story.svelte';
|
||||||
|
|
||||||
|
export default { title: 'FileUploader', decorators: [withKnobs] };
|
||||||
|
|
||||||
|
const buttonKinds = {
|
||||||
|
'Primary (primary)': 'primary',
|
||||||
|
'Secondary (secondary)': 'secondary',
|
||||||
|
'Danger (danger)': 'danger',
|
||||||
|
'Ghost (ghost)': 'ghost',
|
||||||
|
'Tertiary (tertiary)': 'tertiary'
|
||||||
|
};
|
||||||
|
|
||||||
|
const filenameStatuses = {
|
||||||
|
'Edit (edit)': 'edit',
|
||||||
|
'Complete (complete)': 'complete',
|
||||||
|
'Uploading (uploading)': 'uploading'
|
||||||
|
};
|
||||||
|
|
||||||
|
export const FileUploaderButton = () => ({
|
||||||
|
Component,
|
||||||
|
props: {
|
||||||
|
story: 'button',
|
||||||
|
kind: select('Button kind (kind)', buttonKinds, 'primary'),
|
||||||
|
labelText: text('Label text (labelText)', 'Add files'),
|
||||||
|
name: text('Form item name: (name)', ''),
|
||||||
|
multiple: boolean('Supports multiple files (multiple)', true),
|
||||||
|
disabled: boolean('Disabled (disabled)', false),
|
||||||
|
disableLabelChanges: boolean(
|
||||||
|
'Prevent the label from being replaced with file selected file (disableLabelChanges)',
|
||||||
|
false
|
||||||
|
),
|
||||||
|
role: text('ARIA role of the button (role)', 'button'),
|
||||||
|
tabindex: text('Tab index (tabindex)', '0')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
FileUploaderButton.story = { name: 'FileUploaderButton' };
|
||||||
|
|
||||||
|
export const FileUploader = () => ({
|
||||||
|
Component,
|
||||||
|
props: {
|
||||||
|
story: 'uploader',
|
||||||
|
labelTitle: text('The label title (labelTitle)', 'Upload'),
|
||||||
|
labelDescription: text(
|
||||||
|
'The label description (labelDescription)',
|
||||||
|
'only .jpg files at 500mb or less'
|
||||||
|
),
|
||||||
|
buttonLabel: text('The button label (buttonLabel)', 'Add files'),
|
||||||
|
status: select('Status for file name (status)', filenameStatuses, 'edit'),
|
||||||
|
accept: array('Accepted file extensions (accept)', ['.jpg', '.png'], ','),
|
||||||
|
name: text('Form item name: (name)', ''),
|
||||||
|
multiple: boolean('Supports multiple files (multiple)', true),
|
||||||
|
iconDescription: text('Close button icon description (iconDescription)', 'Clear file')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
FileUploader.story = { name: 'FileUploader' };
|
||||||
|
|
||||||
|
export const FileUploaderItem = () => ({
|
||||||
|
Component,
|
||||||
|
props: {
|
||||||
|
story: 'item',
|
||||||
|
name: text('Filename (name)', 'README.md'),
|
||||||
|
status: select('Status for file name (status)', filenameStatuses, 'edit'),
|
||||||
|
iconDescription: text('Close button icon description (iconDescription)', 'Clear file'),
|
||||||
|
invalid: boolean('Invalid (invalid)', false),
|
||||||
|
errorSubject: text('Error subject (errorSubject)', 'File size exceeds limit'),
|
||||||
|
errorBody: text(
|
||||||
|
'Error body (errorBody)',
|
||||||
|
'500kb max file size. Select a new file and try again.'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
FileUploaderItem.story = { name: 'FileUploaderItem' };
|
||||||
|
|
||||||
|
export const FileUploaderDropContainer = () => ({
|
||||||
|
Component,
|
||||||
|
props: {
|
||||||
|
story: 'drop container',
|
||||||
|
labelText: text('Label text (labelText)', 'Drag and drop files here or click to upload'),
|
||||||
|
name: text('Form item name (name)', ''),
|
||||||
|
multiple: boolean('Supports multiple files (multiple)', true),
|
||||||
|
accept: array(
|
||||||
|
'Accepted MIME types or file extensions (accept)',
|
||||||
|
['image/jpeg', 'image/png'],
|
||||||
|
','
|
||||||
|
),
|
||||||
|
disabled: boolean('Disabled (disabled)', false),
|
||||||
|
role: text('ARIA role of the button (role)', ''),
|
||||||
|
tabindex: number('Tab index (tabindex)', '0')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
FileUploaderDropContainer.story = { name: 'FileUploaderDropContainer' };
|
||||||
|
|
||||||
|
export const Skeleton = () => ({ Component, props: { story: 'skeleton' } });
|
77
src/components/FileUploader/FileUploader.svelte
Normal file
77
src/components/FileUploader/FileUploader.svelte
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
<script>
|
||||||
|
let className = undefined;
|
||||||
|
export { className as class };
|
||||||
|
export let files = [];
|
||||||
|
export let name = '';
|
||||||
|
export let labelDescription = '';
|
||||||
|
export let labelTitle = '';
|
||||||
|
export let iconDescription = 'Provide icon description';
|
||||||
|
export let status = 'uploading';
|
||||||
|
export let buttonLabel = '';
|
||||||
|
export let kind = 'primary';
|
||||||
|
export let multiple = false;
|
||||||
|
export let accept = [];
|
||||||
|
export let style = undefined;
|
||||||
|
|
||||||
|
import { createEventDispatcher } from 'svelte';
|
||||||
|
import { cx } from '../../lib';
|
||||||
|
import Filename from './Filename.svelte';
|
||||||
|
import FileUploaderButton from './FileUploaderButton.svelte';
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
const _class = cx('--form-item', className);
|
||||||
|
|
||||||
|
// let files = [];
|
||||||
|
let prevFiles = [];
|
||||||
|
|
||||||
|
$: {
|
||||||
|
if (files.length > prevFiles.length) {
|
||||||
|
dispatch('add', files);
|
||||||
|
} else {
|
||||||
|
dispatch(
|
||||||
|
'remove',
|
||||||
|
prevFiles.filter(_ => !files.includes(_))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
prevFiles = [...files];
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div on:click on:mouseover on:mouseenter on:mouseleave class={_class} {style}>
|
||||||
|
<strong class={cx('--file--label')}>{labelTitle}</strong>
|
||||||
|
<p class={cx('--label-description')}>{labelDescription}</p>
|
||||||
|
<FileUploaderButton
|
||||||
|
disableLabelChanges
|
||||||
|
labelText={buttonLabel}
|
||||||
|
on:change
|
||||||
|
on:change={({ target }) => {
|
||||||
|
files = [...target.files].map(({ name }) => name);
|
||||||
|
}}
|
||||||
|
{accept}
|
||||||
|
{name}
|
||||||
|
{multiple}
|
||||||
|
{kind} />
|
||||||
|
<div class={cx('--file-container')}>
|
||||||
|
{#each files as name, i (name)}
|
||||||
|
<span class={cx('--file__selected-file')}>
|
||||||
|
<p class={cx('--file-filename')}>{name}</p>
|
||||||
|
<span class={cx('--file__state-container')}>
|
||||||
|
<Filename
|
||||||
|
on:keydown
|
||||||
|
on:keydown={({ key }) => {
|
||||||
|
if (key === ' ' || key === 'Enter') {
|
||||||
|
files = files.filter((_, index) => index !== i);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
on:click
|
||||||
|
on:click={evt => {
|
||||||
|
files = files.filter((_, index) => index !== i);
|
||||||
|
}}
|
||||||
|
{iconDescription}
|
||||||
|
{status} />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
64
src/components/FileUploader/FileUploaderButton.svelte
Normal file
64
src/components/FileUploader/FileUploaderButton.svelte
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
<script>
|
||||||
|
let className = undefined;
|
||||||
|
export { className as class };
|
||||||
|
export let disableLabelChanges = false;
|
||||||
|
export let id = Math.random();
|
||||||
|
export let labelText = 'Add file';
|
||||||
|
export let multiple = false;
|
||||||
|
export let name = '';
|
||||||
|
export let role = 'button';
|
||||||
|
export let tabindex = '0';
|
||||||
|
export let kind = 'primary';
|
||||||
|
export let accept = [];
|
||||||
|
export let disabled = false;
|
||||||
|
export let style = undefined;
|
||||||
|
|
||||||
|
import { cx } from '../../lib';
|
||||||
|
|
||||||
|
const _class = cx(
|
||||||
|
'--btn',
|
||||||
|
'--btn--sm',
|
||||||
|
kind && `--btn--${kind}`,
|
||||||
|
disabled && '--btn--disabled',
|
||||||
|
className
|
||||||
|
);
|
||||||
|
|
||||||
|
let inputRef = undefined;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<label
|
||||||
|
tabindex={disabled ? '-1' : tabindex}
|
||||||
|
aria-disabled={disabled}
|
||||||
|
class={_class}
|
||||||
|
for={id}
|
||||||
|
on:keydown
|
||||||
|
on:keydown={({ key }) => {
|
||||||
|
if (key === ' ' || key === 'Enter') {
|
||||||
|
inputRef.click();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
{style}>
|
||||||
|
<span {role}>{labelText}</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
bind:this={inputRef}
|
||||||
|
type="file"
|
||||||
|
tabindex="-1"
|
||||||
|
class={cx('--visually-hidden')}
|
||||||
|
on:change|stopPropagation
|
||||||
|
on:change|stopPropagation={({ target }) => {
|
||||||
|
const files = target.files;
|
||||||
|
const length = files.length;
|
||||||
|
if (files && !disableLabelChanges) {
|
||||||
|
labelText = length > 1 ? `${length} files` : files[0].name;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
on:click
|
||||||
|
on:click={event => {
|
||||||
|
event.target.value = null;
|
||||||
|
}}
|
||||||
|
{id}
|
||||||
|
{disabled}
|
||||||
|
{multiple}
|
||||||
|
{accept}
|
||||||
|
{name} />
|
85
src/components/FileUploader/FileUploaderDropContainer.svelte
Normal file
85
src/components/FileUploader/FileUploaderDropContainer.svelte
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
<script>
|
||||||
|
let className = undefined;
|
||||||
|
export { className as class };
|
||||||
|
export let name = '';
|
||||||
|
export let role = 'button';
|
||||||
|
export let id = Math.random();
|
||||||
|
export let disabled = false;
|
||||||
|
export let tabindex = '0';
|
||||||
|
export let labelText = 'Add file';
|
||||||
|
export let multiple = false;
|
||||||
|
export let accept = [];
|
||||||
|
export let validateFiles = files => files;
|
||||||
|
export let style = undefined;
|
||||||
|
|
||||||
|
import { createEventDispatcher } from 'svelte';
|
||||||
|
import SkeletonText from '../SkeletonText';
|
||||||
|
import { ButtonSkeleton } from '../Button';
|
||||||
|
import { cx } from '../../lib';
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
const _labelClass = cx('--file-browse-btn', disabled && '--file-browse-btn--disabled');
|
||||||
|
|
||||||
|
let over = false;
|
||||||
|
let inputRef = undefined;
|
||||||
|
|
||||||
|
$: _class = cx('--file__drop-container', over && '--file__drop-container--drag-over', className);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class={cx('--file')}
|
||||||
|
on:dragover
|
||||||
|
on:dragover|preventDefault|stopPropagation={({ dataTransfer }) => {
|
||||||
|
if (!disabled) {
|
||||||
|
over = true;
|
||||||
|
dataTransfer.dropEffect = 'copy';
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
on:dragleave
|
||||||
|
on:dragleave|preventDefault|stopPropagation={({ dataTransfer }) => {
|
||||||
|
if (!disabled) {
|
||||||
|
over = false;
|
||||||
|
dataTransfer.dropEffect = 'move';
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
on:drop
|
||||||
|
on:drop|preventDefault|stopPropagation={({ dataTransfer }) => {
|
||||||
|
if (!disabled) {
|
||||||
|
over = false;
|
||||||
|
dispatch('add', validateFiles(dataTransfer.files));
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
{style}>
|
||||||
|
<label
|
||||||
|
class={_labelClass}
|
||||||
|
for={id}
|
||||||
|
on:keydown
|
||||||
|
on:keydown={({ key }) => {
|
||||||
|
if (key === ' ' || key === 'Enter') {
|
||||||
|
inputRef.click();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
{tabindex}>
|
||||||
|
<div class={_class} {role}>
|
||||||
|
{labelText}
|
||||||
|
<input
|
||||||
|
bind:this={inputRef}
|
||||||
|
type="file"
|
||||||
|
tabindex="-1"
|
||||||
|
class={cx('--file-input')}
|
||||||
|
on:change
|
||||||
|
on:change={({ target }) => {
|
||||||
|
dispatch('add', validateFiles(target.files));
|
||||||
|
}}
|
||||||
|
on:click
|
||||||
|
on:click={({ target }) => {
|
||||||
|
target.value = null;
|
||||||
|
}}
|
||||||
|
{id}
|
||||||
|
{disabled}
|
||||||
|
{accept}
|
||||||
|
{name}
|
||||||
|
{multiple} />
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
</div>
|
49
src/components/FileUploader/FileUploaderItem.svelte
Normal file
49
src/components/FileUploader/FileUploaderItem.svelte
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
<script>
|
||||||
|
let className = undefined;
|
||||||
|
export { className as class };
|
||||||
|
export let id = Math.random();
|
||||||
|
export let status = 'uploading';
|
||||||
|
export let iconDescription = '';
|
||||||
|
export let name = '';
|
||||||
|
export let invalid = false;
|
||||||
|
export let errorSubject = '';
|
||||||
|
export let errorBody = '';
|
||||||
|
export let style = undefined;
|
||||||
|
|
||||||
|
import { createEventDispatcher } from 'svelte';
|
||||||
|
import { cx } from '../../lib';
|
||||||
|
import Filename from './Filename.svelte';
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
const _class = cx(
|
||||||
|
'--file__selected-file',
|
||||||
|
invalid && '--file__selected-file--invalid',
|
||||||
|
className
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<span on:mouseover on:mouseenter on:mouseleave class={_class} {style}>
|
||||||
|
<p class={cx('--file-filename')}>{name}</p>
|
||||||
|
<span class={cx('--file__state-container')}>
|
||||||
|
<Filename
|
||||||
|
{iconDescription}
|
||||||
|
{status}
|
||||||
|
{invalid}
|
||||||
|
on:keydown={({ key }) => {
|
||||||
|
if (key === ' ' || key === 'Enter') {
|
||||||
|
dispatch('delete', id);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
on:click={() => {
|
||||||
|
dispatch('delete', id);
|
||||||
|
}} />
|
||||||
|
</span>
|
||||||
|
{#if invalid && errorSubject}
|
||||||
|
<div class={cx('--form-requirement')}>
|
||||||
|
<div class={cx('--form-requirement__title')}>{errorSubject}</div>
|
||||||
|
{#if errorBody}
|
||||||
|
<p class={cx('--form-requirement__supplement')}>{errorBody}</p>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</span>
|
43
src/components/FileUploader/Filename.svelte
Normal file
43
src/components/FileUploader/Filename.svelte
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
<script>
|
||||||
|
let className = undefined;
|
||||||
|
export { className as class };
|
||||||
|
export let status = 'uploading';
|
||||||
|
export let iconDescription = '';
|
||||||
|
export let invalid = false;
|
||||||
|
export let tabindex = '0';
|
||||||
|
export let style = undefined;
|
||||||
|
|
||||||
|
import Close16 from 'carbon-icons-svelte/lib/Close16';
|
||||||
|
import CheckmarkFilled16 from 'carbon-icons-svelte/lib/CheckmarkFilled16';
|
||||||
|
import WarningFilled16 from 'carbon-icons-svelte/lib/WarningFilled16';
|
||||||
|
import { cx } from '../../lib';
|
||||||
|
import Loading from '../Loading';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if status === 'uploading'}
|
||||||
|
<Loading description={iconDescription} withOverlay={false} small class={className} {style} />
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if status === 'edit'}
|
||||||
|
{#if invalid}
|
||||||
|
<WarningFilled16 class={cx('--file-invalid')} />
|
||||||
|
{/if}
|
||||||
|
<!-- TODO: forward keydown event to Svelte icon -->
|
||||||
|
<Close16
|
||||||
|
class={cx('--file-close', className)}
|
||||||
|
aria-label={iconDescription}
|
||||||
|
title={iconDescription}
|
||||||
|
on:click
|
||||||
|
on:keydown
|
||||||
|
{tabindex}
|
||||||
|
{style} />
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if status === 'complete'}
|
||||||
|
<CheckmarkFilled16
|
||||||
|
class={cx('--file-complete', className)}
|
||||||
|
aria-label={iconDescription}
|
||||||
|
title={iconDescription}
|
||||||
|
{tabindex}
|
||||||
|
{style} />
|
||||||
|
{/if}
|
7
src/components/FileUploader/index.js
Normal file
7
src/components/FileUploader/index.js
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import FileUploader from './FileUploader.svelte';
|
||||||
|
|
||||||
|
export default FileUploader;
|
||||||
|
export { default as FileUploaderButton } from './FileUploaderButton.svelte';
|
||||||
|
export { default as FileUploaderItem } from './FileUploaderItem.svelte';
|
||||||
|
export { default as FileUploaderDropContainer } from './FileUploaderDropContainer.svelte';
|
||||||
|
export { default as Filename } from './Filename.svelte';
|
|
@ -10,7 +10,7 @@ export const Default = () => ({
|
||||||
disabled: boolean('Disabled (disabled in <Tab>)', false),
|
disabled: boolean('Disabled (disabled in <Tab>)', false),
|
||||||
href: text('The href for tab (href in <Tab>)', '#'),
|
href: text('The href for tab (href in <Tab>)', '#'),
|
||||||
role: text('ARIA role (role in <Tab>)', 'presentation'),
|
role: text('ARIA role (role in <Tab>)', 'presentation'),
|
||||||
tabindex: number('Tab index (tabindex in <Tab>)', 0)
|
tabindex: text('Tab index (tabindex in <Tab>)', '0')
|
||||||
},
|
},
|
||||||
tabsProps: {
|
tabsProps: {
|
||||||
className: 'some-class',
|
className: 'some-class',
|
||||||
|
|
|
@ -48,7 +48,7 @@ export const Expandable = () => ({
|
||||||
Component,
|
Component,
|
||||||
props: {
|
props: {
|
||||||
story: 'expandable',
|
story: 'expandable',
|
||||||
tabIndex: number('Tab index (tabIndex)', 0),
|
tabindex: text('Tab index (tabindex)', '0'),
|
||||||
expanded: boolean('Expanded (expanded)', false),
|
expanded: boolean('Expanded (expanded)', false),
|
||||||
tileMaxHeight: number('Max height (tileMaxHeight)', 0),
|
tileMaxHeight: number('Max height (tileMaxHeight)', 0),
|
||||||
tileCollapsedIconText: text(
|
tileCollapsedIconText: text(
|
||||||
|
|
11
src/index.js
11
src/index.js
|
@ -8,6 +8,12 @@ import CopyButton from './components/CopyButton';
|
||||||
import ComposedModal, { ModalHeader, ModalBody, ModalFooter } from './components/ComposedModal';
|
import ComposedModal, { ModalHeader, ModalBody, ModalFooter } from './components/ComposedModal';
|
||||||
import CodeSnippet, { CodeSnippetSkeleton } from './components/CodeSnippet';
|
import CodeSnippet, { CodeSnippetSkeleton } from './components/CodeSnippet';
|
||||||
import DataTableSkeleton from './components/DataTableSkeleton';
|
import DataTableSkeleton from './components/DataTableSkeleton';
|
||||||
|
import FileUploader, {
|
||||||
|
FileUploaderButton,
|
||||||
|
FileUploaderItem,
|
||||||
|
FileUploaderDropContainer,
|
||||||
|
Filename
|
||||||
|
} from './components/FileUploader';
|
||||||
import Form from './components/Form';
|
import Form from './components/Form';
|
||||||
import FormGroup from './components/FormGroup';
|
import FormGroup from './components/FormGroup';
|
||||||
import FormItem from './components/FormItem';
|
import FormItem from './components/FormItem';
|
||||||
|
@ -86,6 +92,11 @@ export {
|
||||||
Copy,
|
Copy,
|
||||||
CopyButton,
|
CopyButton,
|
||||||
DataTableSkeleton,
|
DataTableSkeleton,
|
||||||
|
FileUploader,
|
||||||
|
FileUploaderButton,
|
||||||
|
FileUploaderItem,
|
||||||
|
FileUploaderDropContainer,
|
||||||
|
Filename,
|
||||||
Form,
|
Form,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
FormItem,
|
FormItem,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue