mirror of
https://github.com/carbon-design-system/carbon-components-svelte.git
synced 2025-09-14 18:01:06 +00:00
feat(components): add TextInput
This commit is contained in:
parent
bf502f904f
commit
e786e0e78f
8 changed files with 362 additions and 11 deletions
25
README.md
25
README.md
|
@ -18,16 +18,16 @@ Currently, the following components are supported:
|
|||
|
||||
- Accordion
|
||||
- AccordionItem
|
||||
- AccordionSkeleton
|
||||
- AccordionSkeleton
|
||||
- Breadcrumb
|
||||
- BreadcrumbItem
|
||||
- BreadcrumbSkeleton
|
||||
- BreadcrumbSkeleton
|
||||
- Button
|
||||
- ButtonSkeleton
|
||||
- ButtonSkeleton
|
||||
- Checkbox
|
||||
- CheckboxSkeleton
|
||||
- CheckboxSkeleton
|
||||
- CodeSnippet
|
||||
- CodeSnippetSkeleton
|
||||
- CodeSnippetSkeleton
|
||||
- Copy
|
||||
- CopyButton
|
||||
- InlineLoading
|
||||
|
@ -36,19 +36,22 @@ Currently, the following components are supported:
|
|||
- ListItem
|
||||
- OrderedList
|
||||
- RadioButton
|
||||
- RadioButtonSkeleton
|
||||
- RadioButtonSkeleton
|
||||
- Search
|
||||
- SearchSkeleton
|
||||
- SearchSkeleton
|
||||
- SkeletonPlaceholder
|
||||
- SkeletonText
|
||||
- Tag
|
||||
- TagSkeleton
|
||||
- TagSkeleton
|
||||
- TextArea
|
||||
- TextAreaSkeleton
|
||||
- TextAreaSkeleton
|
||||
- TextInput
|
||||
- TextInputSkeleton
|
||||
- PasswordInput
|
||||
- Toggle
|
||||
- ToggleSkeleton
|
||||
- ToggleSkeleton
|
||||
- ToggleSmall
|
||||
- ToggleSmallSkeleton
|
||||
- ToggleSmallSkeleton
|
||||
- TooltipDefinition
|
||||
- TooltipIcon
|
||||
- UnorderedList
|
||||
|
|
112
src/components/TextInput/PasswordInput.svelte
Normal file
112
src/components/TextInput/PasswordInput.svelte
Normal file
|
@ -0,0 +1,112 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let disabled = false;
|
||||
export let id = Math.random();
|
||||
export let labelText = '';
|
||||
export let placeholder = '';
|
||||
export let type = 'password';
|
||||
export let value = '';
|
||||
export let invalid = false;
|
||||
export let invalidText = '';
|
||||
export let helperText = '';
|
||||
export let hideLabel = false;
|
||||
export let light = false;
|
||||
export let tooltipPosition = 'bottom';
|
||||
export let tooltipAlignment = 'center';
|
||||
export let hidePasswordLabel = 'Hide password';
|
||||
export let showPasswordLabel = 'Show password';
|
||||
export let props = {};
|
||||
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import WarningFilled16 from 'carbon-icons-svelte/lib/WarningFilled16';
|
||||
import View16 from 'carbon-icons-svelte/lib/View16';
|
||||
import ViewOff16 from 'carbon-icons-svelte/lib/ViewOff16';
|
||||
import { cx } from '../../lib';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
const errorId = `${id}-error`;
|
||||
const passwordIsVisible = type === 'text';
|
||||
const _labelClass = cx(
|
||||
'--label',
|
||||
hideLabel && '--visually-hidden',
|
||||
disabled && '--label--disabled'
|
||||
);
|
||||
const _helperTextClass = cx('--form__helper-text', disabled && '--form__helper-text--disabled');
|
||||
const _textInputClass = cx(
|
||||
'--text-input',
|
||||
'--password-input',
|
||||
light && '--text-input--light',
|
||||
invalid && '--text-input--invalid',
|
||||
className
|
||||
);
|
||||
const _passwordVisibilityToggleClass = cx(
|
||||
'--text-input--password__visibility__toggle',
|
||||
'--btn--icon-only',
|
||||
'--tooltip__trigger',
|
||||
'--tooltip--a11y',
|
||||
tooltipPosition && `--tooltip--${tooltipPosition}`,
|
||||
tooltipAlignment && `--tooltip--align-${tooltipAlignment}`
|
||||
);
|
||||
</script>
|
||||
|
||||
<div class={cx('--form-item', '--text-input-wrapper', '--password-input-wrapper')}>
|
||||
{#if labelText}
|
||||
<label for={id} class={_labelClass}>{labelText}</label>
|
||||
{/if}
|
||||
|
||||
{#if helperText}
|
||||
<div class={_helperTextClass}>{helperText}</div>
|
||||
{/if}
|
||||
<div class={cx('--text-input__field-wrapper')} data-invalid={invalid || undefined}>
|
||||
{#if invalid}
|
||||
<WarningFilled16 class={cx('--text-input__invalid-icon')} />
|
||||
{/if}
|
||||
<input
|
||||
{...props}
|
||||
class={_textInputClass}
|
||||
on:click={event => {
|
||||
if (!disabled) {
|
||||
dispatch('click', event);
|
||||
}
|
||||
}}
|
||||
on:change={event => {
|
||||
if (!disabled) {
|
||||
dispatch('change', event);
|
||||
}
|
||||
}}
|
||||
on:input={event => {
|
||||
value = event.target.value;
|
||||
if (!disabled) {
|
||||
dispatch('input', event);
|
||||
}
|
||||
}}
|
||||
data-invalid={invalid || undefined}
|
||||
aria-invalid={invalid || undefined}
|
||||
aria-describedby={invalid ? errorId : undefined}
|
||||
{id}
|
||||
{placeholder}
|
||||
{type}
|
||||
{value}
|
||||
{disabled} />
|
||||
<button
|
||||
type="button"
|
||||
class={_passwordVisibilityToggleClass}
|
||||
on:click={() => {
|
||||
type = type === 'password' ? 'text' : 'password';
|
||||
}}>
|
||||
<span class={cx('--assistive-text')}>
|
||||
{#if passwordIsVisible}{hidePasswordLabel}{:else}{showPasswordLabel}{/if}
|
||||
</span>
|
||||
|
||||
{#if passwordIsVisible}
|
||||
<ViewOff16 class={cx('--icon-visibility-off')} />
|
||||
{:else}
|
||||
<View16 class={cx('--icon-visibility-on')} />
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
{#if invalid}
|
||||
<div class={cx('--form-requirement')} id={errorId}>{invalidText}</div>
|
||||
{/if}
|
||||
</div>
|
17
src/components/TextInput/TextInput.Skeleton.svelte
Normal file
17
src/components/TextInput/TextInput.Skeleton.svelte
Normal 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>
|
42
src/components/TextInput/TextInput.Story.svelte
Normal file
42
src/components/TextInput/TextInput.Story.svelte
Normal file
|
@ -0,0 +1,42 @@
|
|||
<script>
|
||||
export let story = undefined;
|
||||
|
||||
import Layout from '../../internal/ui/Layout.svelte';
|
||||
import TextInput from './TextInput.svelte';
|
||||
import PasswordInput from './PasswordInput.svelte';
|
||||
import TextInputSkeleton from './TextInput.Skeleton.svelte';
|
||||
|
||||
let value = '';
|
||||
let type = 'password';
|
||||
</script>
|
||||
|
||||
<Layout>
|
||||
{value}
|
||||
{#if story === 'skeleton'}
|
||||
<div aria-label="loading text input" aria-live="assertive" role="status" tabindex="0">
|
||||
<TextInputSkeleton />
|
||||
<br />
|
||||
<TextInputSkeleton hideLabel />
|
||||
</div>
|
||||
{:else if story === 'password-visibility'}
|
||||
<PasswordInput {...$$props} aria-level="" />
|
||||
{:else if story === 'controlled'}
|
||||
<PasswordInput {...$$props} {type} />
|
||||
<div>
|
||||
<button
|
||||
on:click={() => {
|
||||
type = 'text';
|
||||
}}>
|
||||
Show password
|
||||
</button>
|
||||
<button
|
||||
on:click={() => {
|
||||
type = 'password';
|
||||
}}>
|
||||
Hide password
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<TextInput {...$$props} bind:value />
|
||||
{/if}
|
||||
</Layout>
|
89
src/components/TextInput/TextInput.stories.js
Normal file
89
src/components/TextInput/TextInput.stories.js
Normal file
|
@ -0,0 +1,89 @@
|
|||
import { withKnobs, boolean, text, select } from '@storybook/addon-knobs';
|
||||
import Component from './TextInput.Story.svelte';
|
||||
|
||||
export default { title: 'TextInput', decorators: [withKnobs] };
|
||||
|
||||
export const Default = () => ({
|
||||
Component,
|
||||
props: {
|
||||
disabled: boolean('Disabled (disabled)', false),
|
||||
light: boolean('Light variant (light)', false),
|
||||
hideLabel: boolean('No label (hideLabel)', false),
|
||||
labelText: text('Label text (labelText)', 'Text Input 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.'),
|
||||
id: 'text-input'
|
||||
}
|
||||
});
|
||||
|
||||
export const TogglePasswordVisibility = () => ({
|
||||
Component,
|
||||
props: {
|
||||
story: 'password-visibility',
|
||||
disabled: boolean('Disabled (disabled)', false),
|
||||
light: boolean('Light variant (light)', false),
|
||||
hideLabel: boolean('No label (hideLabel)', false),
|
||||
labelText: text('Label text (labelText)', 'Text Input 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.'),
|
||||
id: 'text-input',
|
||||
tooltipPosition: select(
|
||||
'Tooltip position (tooltipPosition)',
|
||||
['top', 'right', 'bottom', 'left'],
|
||||
'bottom'
|
||||
),
|
||||
tooltipAlignment: select(
|
||||
'Tooltip alignment (tooltipAlignment)',
|
||||
['start', 'center', 'end'],
|
||||
'center'
|
||||
),
|
||||
hidePasswordLabel: text(
|
||||
'"Hide password" tooltip label for password visibility toggle (hidePasswordLabel)',
|
||||
'Hide password'
|
||||
),
|
||||
showPasswordLabel: text(
|
||||
'"Show password" tooltip label for password visibility toggle (showPasswordLabel)',
|
||||
'Show password'
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
export const ControlledTogglePasswordVisibility = () => ({
|
||||
Component,
|
||||
props: {
|
||||
story: 'controlled',
|
||||
disabled: boolean('Disabled (disabled)', false),
|
||||
light: boolean('Light variant (light)', false),
|
||||
hideLabel: boolean('No label (hideLabel)', false),
|
||||
labelText: text('Label text (labelText)', 'Text Input 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.'),
|
||||
id: 'text-input',
|
||||
tooltipPosition: select(
|
||||
'Tooltip position (tooltipPosition)',
|
||||
['top', 'right', 'bottom', 'left'],
|
||||
'bottom'
|
||||
),
|
||||
tooltipAlignment: select(
|
||||
'Tooltip alignment (tooltipAlignment)',
|
||||
['start', 'center', 'end'],
|
||||
'center'
|
||||
),
|
||||
hidePasswordLabel: text(
|
||||
'"Hide password" tooltip label for password visibility toggle (hidePasswordLabel)',
|
||||
'Hide password'
|
||||
),
|
||||
showPasswordLabel: text(
|
||||
'"Show password" tooltip label for password visibility toggle (showPasswordLabel)',
|
||||
'Show password'
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
export const Skeleton = () => ({ Component, props: { story: 'skeleton' } });
|
79
src/components/TextInput/TextInput.svelte
Normal file
79
src/components/TextInput/TextInput.svelte
Normal file
|
@ -0,0 +1,79 @@
|
|||
<script>
|
||||
let className = undefined;
|
||||
export { className as class };
|
||||
export let disabled = false;
|
||||
export let id = Math.random();
|
||||
export let labelText = '';
|
||||
export let placeholder = '';
|
||||
export let type = '';
|
||||
export let value = '';
|
||||
export let invalid = false;
|
||||
export let invalidText = '';
|
||||
export let helperText = '';
|
||||
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 _textInputClass = cx(
|
||||
'--text-input',
|
||||
light && '--text-input--light',
|
||||
invalid && '--text-input--invalid',
|
||||
className
|
||||
);
|
||||
</script>
|
||||
|
||||
<div class={cx('--form-item', '--text-input-wrapper')}>
|
||||
{#if labelText}
|
||||
<label for={id} class={_labelClass}>{labelText}</label>
|
||||
{/if}
|
||||
{#if helperText}
|
||||
<div class={_helperTextClass}>{helperText}</div>
|
||||
{/if}
|
||||
<div class={cx('--text-input__field-wrapper')} data-invalid={invalid || undefined}>
|
||||
{#if invalid}
|
||||
<WarningFilled16 class={cx('--text-input__invalid-icon')} />
|
||||
{/if}
|
||||
<input
|
||||
{...props}
|
||||
class={_textInputClass}
|
||||
on:click={event => {
|
||||
if (!disabled) {
|
||||
dispatch('click', event);
|
||||
}
|
||||
}}
|
||||
on:change={event => {
|
||||
if (!disabled) {
|
||||
dispatch('change', event);
|
||||
}
|
||||
}}
|
||||
on:input={event => {
|
||||
value = event.target.value;
|
||||
if (!disabled) {
|
||||
dispatch('input', event);
|
||||
}
|
||||
}}
|
||||
data-invalid={invalid || undefined}
|
||||
aria-invalid={invalid || undefined}
|
||||
aria-describedby={invalid ? errorId : undefined}
|
||||
{id}
|
||||
{placeholder}
|
||||
{type}
|
||||
{value}
|
||||
{disabled} />
|
||||
</div>
|
||||
{#if invalid}
|
||||
<div class={cx('--form-requirement')} id={errorId}>{invalidText}</div>
|
||||
{/if}
|
||||
</div>
|
5
src/components/TextInput/index.js
Normal file
5
src/components/TextInput/index.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
import TextInput from './TextInput.svelte';
|
||||
|
||||
export default TextInput;
|
||||
export { default as TextInputSkeleton } from './TextInput.Skeleton.svelte';
|
||||
export { default as PasswordInput } from './PasswordInput.svelte';
|
|
@ -16,6 +16,7 @@ import SkeletonPlaceholder from './components/SkeletonPlaceholder';
|
|||
import SkeletonText from './components/SkeletonText';
|
||||
import Tag, { TagSkeleton } from './components/Tag';
|
||||
import TextArea, { TextAreaSkeleton } from './components/TextArea';
|
||||
import TextInput, { TextInputSkeleton, PasswordInput } from './components/TextInput';
|
||||
import Toggle, { ToggleSkeleton } from './components/Toggle';
|
||||
import ToggleSmall, { ToggleSmallSkeleton } from './components/ToggleSmall';
|
||||
import TooltipDefinition from './components/TooltipDefinition';
|
||||
|
@ -52,6 +53,9 @@ export {
|
|||
TagSkeleton,
|
||||
TextArea,
|
||||
TextAreaSkeleton,
|
||||
TextInput,
|
||||
TextInputSkeleton,
|
||||
PasswordInput,
|
||||
Toggle,
|
||||
ToggleSkeleton,
|
||||
ToggleSmall,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue