Skip to content
Snippets Groups Projects
Verified Commit d2e92767 authored by Louis's avatar Louis :fire:
Browse files

Start building out input element

parent edabc671
Branches trunk
No related tags found
No related merge requests found
Showing
with 263 additions and 67 deletions
......@@ -23,3 +23,6 @@ Thumbs.db
# Vite
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
# Storybook
storybook-static/
\ No newline at end of file
......@@ -2,12 +2,7 @@ import type { StorybookConfig } from '@storybook/sveltekit';
const config: StorybookConfig = {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|ts|svelte)'],
addons: [
'@storybook/addon-svelte-csf',
'@storybook/addon-essentials',
'@chromatic-com/storybook',
'@storybook/addon-interactions'
],
addons: ['@storybook/addon-svelte-csf', '@storybook/addon-essentials'],
framework: {
name: '@storybook/sveltekit',
options: {}
......
......@@ -3,6 +3,7 @@ import type { Preview } from '@storybook/svelte';
const preview: Preview = {
parameters: {
controls: {
disableSaveFromUI: false,
matchers: {
color: /(background|color)$/i,
date: /Date$/i
......
{
"name": "barbi",
"version": "0.0.1",
"publishConfig": {
"registry": "https://npm.weirdboi.dev"
},
"scripts": {
"dev": "vite dev",
"build": "vite build && npm run prepack",
......@@ -51,10 +54,8 @@
"svelte": "^5.0.0"
},
"devDependencies": {
"@chromatic-com/storybook": "^3.2.4",
"@playwright/test": "^1.49.1",
"@storybook/addon-essentials": "^8.5.3",
"@storybook/addon-interactions": "^8.5.3",
"@storybook/addon-svelte-csf": "^5.0.0-next.23",
"@storybook/blocks": "^8.5.3",
"@storybook/svelte": "^8.5.3",
......
......@@ -21,8 +21,7 @@
{...props}
{tag}
hover
active
>
active>
{@render children?.()}
</Panel>
......
<script module>
import { BarbiTheme, Input } from '$lib';
import { defineMeta } from '@storybook/addon-svelte-csf';
const { Story } = defineMeta({
title: 'Components/Atoms/Input',
component: Input
});
</script>
<BarbiTheme />
<div>
<style>
.sb-show-main {
padding: var(--spacing-md) !important;
}
</style>
</div>
<Story name="Simple Text Input" args={{ placeholder: 'Simple Text Input' }} />
<Story name="Disabled Input" args={{ placeholder: 'Cant edit this', disabled: true }} />
<Story name="Number Input" args={{ value: 0, type: 'number' }} />
<Story name="Range Input" args={{ value: 0, type: 'range', min: 0, max: 100, step: 10 }} />
<Story name="Checkbox Input" args={{ type: 'checkbox' }} />
<script lang="ts" module>
export type TextInputProps = {
value?: string;
autocomplete?: 'on' | 'off';
};
export type NumberInputProps = {
value?: number;
min?: number;
max?: number;
step?: number;
};
export type BaseInputProps<inputType, additonal> = {
type: inputType;
id?: string;
name?: string;
disabled?: boolean;
required?: boolean;
placeholder?: string;
} & additonal;
export type Props =
| BaseInputProps<'text' | undefined, TextInputProps>
| BaseInputProps<'number', NumberInputProps>;
</script>
<script lang="ts">
let { value = $bindable(undefined), class: className = '', ...props }: Props = $props();
</script>
<input bind:value class={['input', className]} {...props} />
<style>
.input {
--component-background: var(--colour-white);
--component-colour: var(--colour-text);
--component-placeholder: var(--colour-steel);
--border-colour: var(--colour-steel);
color: var(--component-colour);
background-color: var(--component-background);
font-family: var(--font-family-body);
font-size: var(--font-size-base);
border: var(--border-width) var(--border-style) var(--border-colour);
padding: var(--spacing-sm) var(--spacing-md);
transition: all var(--transition-fast);
box-shadow: var(--shadow-md);
outline: none;
}
.input:hover {
--component-background: var(--colour-background);
--border-colour: var(--colour-steel);
}
.input:focus {
--border-colour: var(--colour-primary);
}
.input:disabled {
--component-background: var(--colour-concrete);
opacity: 0.9;
cursor: not-allowed;
}
/* Placeholder styles */
.input::placeholder {
color: var(--component-placeholder);
}
/* Specific styles for different .input types */
.input[type='checkbox'],
.input[type='radio'] {
width: var(--spacing-md);
height: var(--spacing-md);
margin-right: var(--spacing-sm);
}
.input[type='color'] {
padding: var(--spacing-xs);
height: calc(var(--font-size-base) * 2);
}
.input[type='range'] {
--component-background: transparent;
-webkit-appearance: none;
border: none;
box-shadow: none;
padding: 0;
appearance: none;
cursor: pointer;
}
.input[type='range']::-webkit-slider-runnable-track,
.input[type='range']::-moz-range-track {
--border-colour: var(--colour-contrast);
background: var(--colour-white);
height: var(--spacing-sm);
border: var(--border-width) var(--border-style) var(--border-colour);
transition: border-color var(--transition-fast);
}
.input[type='range']::-webkit-slider-container,
.input[type='range']::-moz-range-progress {
--border-colour: var(--colour-contrast);
background: var(--colour-primary);
height: var(--spacing-sm);
border: var(--border-width) var(--border-style) var(--border-colour);
transition: border-color var(--transition-fast);
}
.input[type='range']:focus::-webkit-slider-container,
.input[type='range']:focus::-moz-range-progress {
--border-colour: var(--colour-primary);
}
.input[type='range']:focus {
outline: none;
}
.input[type='range']:focus::-webkit-slider-runnable-track,
.input[type='range']:focus::-moz-range-track {
--border-colour: var(--colour-primary);
}
.input[type='range']::-webkit-slider-thumb,
.input[type='range']::-moz-range-thumb {
--border-colour: var(--colour-contrast);
-webkit-appearance: none;
appearance: none;
border-radius: 0;
border: var(--border-width) var(--border-style) var(--border-colour);
width: var(--spacing-sm);
height: var(--spacing-md);
background: var(--colour-background);
}
</style>
......@@ -11,7 +11,7 @@
disabled?: boolean;
flat?: boolean;
full?: boolean;
} & HTMLElement;
} & IntrinsicElements[Tag];
const {
// @ts-ignore
......@@ -55,8 +55,7 @@
variant
]}
{disabled}
{...rest}
>
{...rest}>
{@render children?.()}
</svelte:element>
......
......@@ -78,8 +78,7 @@
<div
class={['popover-wrapper', wrapperClass]}
style={`${style ? style : ''}; --component-gap-vertical: ${spaceVertical}; --component-gap-horizontal: ${spaceHorizontal};`}
>
style={`${style ? style : ''}; --component-gap-vertical: ${spaceVertical}; --component-gap-horizontal: ${spaceHorizontal};`}>
<div bind:this={triggerElement} onclick={openPopover}>
{@render children?.()}
</div>
......
......@@ -2,7 +2,10 @@
const { class: className, ...props } = $props();
</script>
<svg viewBox="0 0 200 110" xmlns="http://www.w3.org/2000/svg"
class={['icon', className]} {...props}>
<path d="m1.636 5.368 196.73 1.5-97.764 97.764-98.964-99.264z" fill="var(--colour-icon)"/>
<svg
viewBox="0 0 200 110"
xmlns="http://www.w3.org/2000/svg"
class={['icon', className]}
{...props}>
<path d="m1.636 5.368 196.73 1.5-97.764 97.764-98.964-99.264z" fill="var(--colour-icon)" />
</svg>
......@@ -9,11 +9,11 @@
argTypes: {
alternate: {
name: 'Alternate Style',
control: 'boolean',
control: 'boolean'
},
flat: {
name: 'Flat Style',
control: 'boolean',
control: 'boolean'
},
options: {
table: {
......@@ -29,11 +29,16 @@
args={{
label: "I'm a dropdown",
options: [
{ value: '1', label: 'I log to the console', onClick() { console.log("First button") } },
{ value: '2', label: "I'm an external link", href: 'https://example.com' },
{
value: '1',
label: 'I log to the console',
onClick() {
console.log('First button');
}
},
{ value: '2', label: "I'm an external link", href: 'https://example.com' }
]
}}
>
}}>
{#snippet children(args)}
<BarbiTheme />
<div class="storybook-center">
......@@ -48,11 +53,16 @@
label: "This dropdown uses variant='secondary'",
alternate: true,
options: [
{ value: '1', label: 'I log to the console', onClick() { console.log("First button") } },
{ value: '2', label: "I'm an external link", href: 'https://example.com' },
{
value: '1',
label: 'I log to the console',
onClick() {
console.log('First button');
}
},
{ value: '2', label: "I'm an external link", href: 'https://example.com' }
]
}}
>
}}>
{#snippet children(args)}
<BarbiTheme />
<div class="storybook-center">
......@@ -67,11 +77,16 @@
label: "I'm Flat!",
flat: true,
options: [
{ value: '1', label: 'I log to the console', onClick() { console.log("First button") } },
{ value: '2', label: "I'm an external link", href: 'https://example.com' },
{
value: '1',
label: 'I log to the console',
onClick() {
console.log('First button');
}
},
{ value: '2', label: "I'm an external link", href: 'https://example.com' }
]
}}
>
}}>
{#snippet children(args)}
<BarbiTheme />
<div class="storybook-center">
......@@ -83,13 +98,18 @@
<Story
name="Only Icon"
args={{
label: "",
label: '',
options: [
{ value: '1', label: 'I log to the console', onClick() { console.log("First button") } },
{ value: '2', label: "I'm an external link", href: 'https://example.com' },
{
value: '1',
label: 'I log to the console',
onClick() {
console.log('First button');
}
},
{ value: '2', label: "I'm an external link", href: 'https://example.com' }
]
}}
>
}}>
{#snippet children(args)}
<BarbiTheme />
<div class="storybook-center">
......@@ -105,16 +125,23 @@
label: "It won't spin",
icon: CustomIcon,
options: [
{ value: '1', label: 'I log to the console', onClick() { console.log("First button") } },
{ value: '2', label: "I'm an external link", href: 'https://example.com' },
{
value: '1',
label: 'I log to the console',
onClick() {
console.log('First button');
}
},
{ value: '2', label: "I'm an external link", href: 'https://example.com' }
]
}}
>
}}>
{#snippet children(args)}
<BarbiTheme />
<div class="storybook-center">
<Dropdown {...args} />
<span>Pass a component to the 'icon' prop that uses the global '.icon' class, and merges it with a 'class' property</span>
<span
>Pass a component to the 'icon' prop that uses the global '.icon' class, and merges
it with a 'class' property</span>
</div>
{/snippet}
</Story>
......
......@@ -4,9 +4,9 @@
export type Props = {
label?: string;
options?: DropdownOption[];
alternate?: boolean,
flat?: boolean,
icon?: Component<any>
alternate?: boolean;
flat?: boolean;
icon?: Component<any>;
};
export type DropdownOption = {
......@@ -23,9 +23,15 @@
import Popover from '../../atoms/popover/Popover.svelte';
import AngleDownIcon from '../..//patterns/icons/AngleDownIcon.svelte';
const { label = '', options = [], alternate = false, flat = false, icon: Icon = AngleDownIcon }: Props = $props();
const {
label = '',
options = [],
alternate = false,
flat = false,
icon: Icon = AngleDownIcon
}: Props = $props();
let open = $state(false);
let shouldAnimate = $derived.by(() => Icon === AngleDownIcon)
let shouldAnimate = $derived.by(() => Icon === AngleDownIcon);
function wrapOnClick(option: DropdownOption) {
if (option.onClick) {
......@@ -43,15 +49,21 @@
</script>
<Popover bind:open>
<Button variant={alternate ? 'secondary' : 'primary'} flat={flat}>
<Button variant={alternate ? 'secondary' : 'primary'} {flat}>
<span class="dropdown-label">{label}</span>
<Icon style="--size-icon: 1.25rem;" class={['dropdown-arrow', shouldAnimate && 'dropdown-arrow-animate', { open }]} />
<Icon
style="--size-icon: 1.25rem;"
class={['dropdown-arrow', shouldAnimate && 'dropdown-arrow-animate', { open }]} />
</Button>
{#snippet popover()}
<Panel space="none" class="dropdown-popover-list" flat={flat}>
<Panel space="none" class="dropdown-popover-list" {flat}>
{#each options as option}
<Button variant={alternate ? 'success' : 'secondary'} flat onclick={wrapOnClick(option)} href={option.href}>
<Button
variant={alternate ? 'success' : 'secondary'}
flat
onclick={wrapOnClick(option)}
href={option.href}>
{option.label}
</Button>
{/each}
......
......@@ -5,6 +5,7 @@ import CheckerboardPattern from './patterns/CheckerboardPattern.svelte';
import Popover from './atoms/popover/Popover.svelte';
import { Anchor } from './atoms/popover/utils.js';
import Dropdown from './components/modal/Dropdown.svelte';
import Input from './atoms/Input.svelte';
import type { AnchorName } from './atoms/popover/utils.ts';
......@@ -12,9 +13,7 @@ import type { AnchorName } from './atoms/popover/utils.ts';
export { BarbiTheme, CheckerboardPattern, Anchor };
// Atoms
export {
Panel, Button, Popover,
}
export { Panel, Button, Popover, Input };
// Components
export { Dropdown };
......
......@@ -32,8 +32,7 @@
<Story
name="Modified Colours"
args={{ style: '--pattern-light: #ECA400; --pattern-dark: #27476E;', scale: 2 }}
>
args={{ style: '--pattern-light: #ECA400; --pattern-dark: #27476E;', scale: 2 }}>
{#snippet children(args)}
<BarbiTheme />
<CheckerboardPattern {...args} />
......
......@@ -18,16 +18,14 @@
class={className}
bind:clientHeight={height}
bind:clientWidth={width}
{...props}
>
{...props}>
<defs>
<pattern
id="checks"
patternUnits="userSpaceOnUse"
width="16"
height="16"
class={scrollDirection ? ['scroll', scrollDirection] : []}
>
class={scrollDirection ? ['scroll', scrollDirection] : []}>
<rect width="16" height="16" fill="var(--pattern-light)" />
<rect width="8" height="8" x="0" y="0" fill="var(--pattern-dark)" />
<rect width="8" height="8" x="8" y="8" fill="var(--pattern-dark)" />
......
......@@ -6,10 +6,8 @@
xmlns="http://www.w3.org/2000/svg"
class={['icon', className]}
viewBox="0 0 448 512"
{...props}
>
{...props}>
<path
style="fill: var(--colour-icon)"
d="M201.4 374.6c12.5 12.5 32.8 12.5 45.3 0l160-160c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L224 306.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l160 160z"
/>
d="M201.4 374.6c12.5 12.5 32.8 12.5 45.3 0l160-160c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L224 306.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l160 160z" />
</svg>
......@@ -13,12 +13,11 @@
label: 'First Option',
value: '123',
onClick() {
console.log("FOO")
console.log('FOO');
}
},
{ label: 'Second Option', value: '456', href: 'https://example.com' }
]}
/>
]} />
</div>
<style>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment