Skip to content

Commit c2b4379

Browse files
authored
Merge pull request #381 from appwrite/feat-SER-464-Compound-Tag
2 parents 53df8e1 + 1bf564d commit c2b4379

File tree

5 files changed

+354
-0
lines changed

5 files changed

+354
-0
lines changed
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
<script lang="ts">
2+
import type { HTMLButtonAttributes } from 'svelte/elements';
3+
import { createEventDispatcher, getContext } from 'svelte';
4+
5+
type $$Props = HTMLButtonAttributes & {
6+
variant?: 'default' | 'code';
7+
selected?: boolean;
8+
disabled?: boolean;
9+
dismiss?: boolean;
10+
};
11+
12+
export let variant: $$Props['variant'] = 'default';
13+
export let selected: $$Props['selected'] = false;
14+
export let disabled: $$Props['disabled'] = false;
15+
export let dismiss: $$Props['dismiss'] = false;
16+
17+
const dispatch = createEventDispatcher();
18+
const dismissFn = getContext<() => void>('compound-tag-dismiss');
19+
const size = getContext<'s' | 'm'>('compound-tag-size');
20+
</script>
21+
22+
<button
23+
class:code={variant === 'code'}
24+
class:selected
25+
class:s={size === 's'}
26+
on:click
27+
on:click|capture={() => {
28+
if (dismiss) {
29+
dismissFn?.();
30+
dispatch('dismiss');
31+
}
32+
}}
33+
type="button"
34+
{disabled}
35+
{...$$restProps}
36+
>
37+
<slot />
38+
</button>
39+
40+
<style lang="scss">
41+
@use '../../scss/mixins/transitions';
42+
43+
button {
44+
@include transitions.common;
45+
46+
--p-compound-tag-child-font-family:
47+
var(--badge-font-family, var(--font-family-sansserif)), var(--sans-fallbacks);
48+
--p-compound-tag-child-font-size: var(--badge-font-size, var(--font-size-s));
49+
--p-compound-tag-child-padding-block: var(--badge-padding-block, var(--space-5));
50+
--p-compound-tag-child-padding-inline: var(--badge-padding-inline, var(--space-5));
51+
--p-compound-tag-child-gap: var(--badge-gap, var(--space-3));
52+
--p-compound-tag-child-color: var(--tag-color, var(--fgcolor-neutral-secondary));
53+
--p-compound-tag-child-background-color: var(
54+
--tag-background-color,
55+
var(--bgcolor-neutral-default)
56+
);
57+
--p-compound-tag-child-border-color: var(--border-neutral);
58+
--p-compound-tag-child-divider-color: var(--border-neutral);
59+
60+
display: inline-flex;
61+
align-items: center;
62+
justify-content: center;
63+
padding-block: var(--p-compound-tag-child-padding-block);
64+
padding-inline: var(--p-compound-tag-child-padding-inline);
65+
gap: var(--p-compound-tag-child-gap);
66+
min-height: 100%;
67+
68+
color: var(--p-compound-tag-child-color);
69+
background-color: transparent;
70+
border: none;
71+
border-right: var(--border-width-s) solid var(--p-compound-tag-child-divider-color);
72+
73+
font-family: var(--p-compound-tag-child-font-family);
74+
font-size: var(--p-compound-tag-child-font-size);
75+
font-style: normal;
76+
font-weight: 500;
77+
line-height: 140%;
78+
79+
cursor: pointer;
80+
81+
&:last-child {
82+
border-right: none;
83+
}
84+
85+
&:hover {
86+
background-color: var(--bgcolor-neutral-secondary);
87+
}
88+
89+
&:active {
90+
background-color: var(--bgcolor-neutral-secondary);
91+
}
92+
93+
&:focus-visible {
94+
outline: var(--border-width-l) solid var(--border-focus);
95+
outline-offset: -2px;
96+
}
97+
98+
&:disabled {
99+
background-color: var(--bgcolor-neutral-primary);
100+
opacity: 0.4;
101+
cursor: not-allowed;
102+
}
103+
104+
&.code {
105+
--p-compound-tag-child-font-family: var(--font-family-code), var(--mono-fallbacks);
106+
}
107+
108+
&.selected {
109+
--p-compound-tag-child-background-color: var(--bgcolor-neutral-secondary);
110+
border: var(--border-width-s) solid var(--border-neutral-stronger);
111+
}
112+
113+
/* size variants */
114+
&.s {
115+
--p-compound-tag-child-padding-block: var(--space-2);
116+
--p-compound-tag-child-padding-inline: var(--space-3);
117+
}
118+
}
119+
</style>
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<script lang="ts">
2+
type $$Props = {
3+
size?: 's' | 'm';
4+
};
5+
6+
import { setContext } from 'svelte';
7+
8+
export let size: $$Props['size'] = 'm';
9+
10+
let visible = true;
11+
function handleDismiss() {
12+
visible = false;
13+
}
14+
15+
setContext('compound-tag-dismiss', handleDismiss);
16+
setContext('compound-tag-size', size);
17+
</script>
18+
19+
{#if visible}
20+
<div class="compound-tag" class:s={size === 's'} role="group">
21+
<slot />
22+
</div>
23+
{/if}
24+
25+
<style lang="scss">
26+
@use '../../scss/mixins/transitions';
27+
28+
.compound-tag {
29+
@include transitions.common;
30+
31+
--p-compound-tag-font-family:
32+
var(--badge-font-family, var(--font-family-sansserif)), var(--sans-fallbacks);
33+
--p-compound-tag-font-size: var(--badge-font-size, var(--font-size-s));
34+
--p-compound-tag-padding-block: var(--badge-padding-block, var(--space-5));
35+
--p-compound-tag-padding-inline: var(--badge-padding-inline, var(--space-5));
36+
--p-compound-tag-gap: var(--badge-gap, var(--space-3));
37+
--p-compound-tag-color: var(--tag-color, var(--fgcolor-neutral-secondary));
38+
--p-compound-tag-background-color: var(
39+
--tag-background-color,
40+
var(--bgcolor-neutral-default)
41+
);
42+
--p-compound-tag-border-color: var(--border-neutral);
43+
--p-compound-tag-divider-color: var(--border-neutral);
44+
45+
display: inline-flex;
46+
align-items: stretch;
47+
background-color: var(--p-compound-tag-background-color);
48+
border: var(--border-width-s) solid var(--p-compound-tag-border-color);
49+
border-radius: var(--border-radius-XS, 6px);
50+
overflow: hidden;
51+
52+
&.s {
53+
--p-compound-tag-padding-block: var(--space-2);
54+
--p-compound-tag-padding-inline: var(--space-3);
55+
}
56+
}
57+
</style>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export { default as CompoundTag } from './CompoundTag.svelte';
2+
export { default as Child } from './Child.svelte';
3+
export { default } from './CompoundTag.svelte';

v2/pink-sb/src/lib/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ export { default as InteractiveText } from './InteractiveText.svelte';
99
export { default as Root } from './Root.svelte';
1010
export { default as Status } from './Status.svelte';
1111
export { default as Tag } from './Tag.svelte';
12+
export * as CompoundTag from './CompoundTag/index.js';
13+
export { CompoundTag as CompoundTagRoot, Child as CompoundTagChild } from './CompoundTag/index.js';
1214
export { default as ToggleButton } from './ToggleButton.svelte';
1315
export { default as ProgressCircle } from './ProgressCircle.svelte';
1416
export { default as Tooltip } from './Tooltip.svelte';
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
<script context="module" lang="ts">
2+
import { CompoundTagRoot, CompoundTagChild } from '$lib/index.js';
3+
import type { MetaProps } from '@storybook/addon-svelte-csf';
4+
5+
export const meta: MetaProps = {
6+
title: 'Components/CompoundTag',
7+
component: CompoundTagRoot
8+
};
9+
</script>
10+
11+
<script>
12+
import Icon from '$lib/Icon.svelte';
13+
import Popover from '$lib/Popover.svelte';
14+
import ActionMenu from '$lib/action-menu/index.js';
15+
import { Story, Template } from '@storybook/addon-svelte-csf';
16+
import {
17+
IconXCircle,
18+
IconFilter,
19+
IconCheck,
20+
IconX,
21+
IconInfo,
22+
IconChevronDoubleRight,
23+
IconDuplicate
24+
} from '@appwrite.io/pink-icons-svelte';
25+
</script>
26+
27+
<Template let:args>
28+
<CompoundTagRoot {...args} />
29+
</Template>
30+
31+
<Story name="Default" let:args>
32+
<CompoundTagRoot size={args.size}>
33+
<CompoundTagChild on:click={() => console.log('Clicked Status')}>Status</CompoundTagChild>
34+
<CompoundTagChild on:click={() => console.log('Clicked is')}>is</CompoundTagChild>
35+
<CompoundTagChild selected on:click={() => console.log('Clicked Active')}
36+
>Active</CompoundTagChild
37+
>
38+
<CompoundTagChild aria-label="Dismiss" dismiss>
39+
<Icon size="s" icon={IconX} />
40+
</CompoundTagChild>
41+
</CompoundTagRoot>
42+
</Story>
43+
44+
<Story name="Action" let:args>
45+
<CompoundTagRoot size={args.size}>
46+
<CompoundTagChild>
47+
<Icon icon={IconDuplicate} />
48+
Action
49+
</CompoundTagChild>
50+
<CompoundTagChild aria-label="Dismiss" dismiss>
51+
<Icon size="s" icon={IconX} />
52+
</CompoundTagChild>
53+
</CompoundTagRoot>
54+
</Story>
55+
56+
<Story name="Filter Example" let:args>
57+
<CompoundTagRoot size={args.size}>
58+
<CompoundTagChild on:click={() => console.log('Clicked field')}>
59+
<Icon icon={IconFilter} />
60+
Build duration
61+
</CompoundTagChild>
62+
<CompoundTagChild on:click={() => console.log('Clicked operator')}>is</CompoundTagChild>
63+
<CompoundTagChild selected on:click={() => console.log('Clicked value')}
64+
>more than 2 minutes</CompoundTagChild
65+
>
66+
<CompoundTagChild on:click={() => console.log('Clicked remove')}>
67+
<Icon icon={IconXCircle} />
68+
</CompoundTagChild>
69+
</CompoundTagRoot>
70+
</Story>
71+
72+
<Story name="Status Example" let:args>
73+
<CompoundTagRoot size={args.size}>
74+
<CompoundTagChild on:click={() => console.log('Clicked type')}>Status</CompoundTagChild>
75+
<CompoundTagChild selected on:click={() => console.log('Clicked status')}>
76+
<Icon icon={IconCheck} />
77+
Completed
78+
</CompoundTagChild>
79+
<CompoundTagChild on:click={() => console.log('Clicked action')}>
80+
<Icon icon={IconXCircle} />
81+
</CompoundTagChild>
82+
</CompoundTagRoot>
83+
</Story>
84+
85+
<Story name="Sizes">
86+
<div
87+
style="display: flex; flex-direction: column; gap: var(--space-6); align-items: flex-start;"
88+
>
89+
<CompoundTagRoot size="s">
90+
<CompoundTagChild on:click={() => console.log('Clicked Tag')}>Tag</CompoundTagChild>
91+
<CompoundTagChild on:click={() => console.log('Clicked is')}>is</CompoundTagChild>
92+
<CompoundTagChild on:click={() => console.log('Clicked Tag')}>Tag</CompoundTagChild>
93+
<CompoundTagChild dismiss on:click={() => console.log('Clicked close')}
94+
><Icon icon={IconX} size="s" /></CompoundTagChild
95+
>
96+
</CompoundTagRoot>
97+
<CompoundTagRoot size="m">
98+
<CompoundTagChild on:click={() => console.log('Clicked Tag')}>Tag</CompoundTagChild>
99+
<CompoundTagChild on:click={() => console.log('Clicked is')}>is</CompoundTagChild>
100+
<CompoundTagChild on:click={() => console.log('Clicked Tag')}>Tag</CompoundTagChild>
101+
<CompoundTagChild dismiss on:click={() => console.log('Clicked close')}
102+
><Icon icon={IconX} size="s" /></CompoundTagChild
103+
>
104+
</CompoundTagRoot>
105+
</div>
106+
</Story>
107+
108+
<Story name="With Disabled Child" let:args>
109+
<CompoundTagRoot size={args.size}>
110+
<CompoundTagChild on:click={() => console.log('Clicked Tag')}>Tag</CompoundTagChild>
111+
<CompoundTagChild on:click={() => console.log('Clicked is')}>is</CompoundTagChild>
112+
<CompoundTagChild disabled on:click={() => console.log('Clicked Tag')}>Tag</CompoundTagChild
113+
>
114+
<CompoundTagChild on:click={() => console.log('Clicked close')}>×</CompoundTagChild>
115+
</CompoundTagRoot>
116+
</Story>
117+
118+
<Story name="Code Variant" let:args>
119+
<CompoundTagRoot size={args.size}>
120+
<CompoundTagChild on:click={() => console.log('Clicked function')}
121+
>function</CompoundTagChild
122+
>
123+
<CompoundTagChild variant="code" on:click={() => console.log('Clicked name')}
124+
>getUserData</CompoundTagChild
125+
>
126+
<CompoundTagChild on:click={() => console.log('Clicked params')}>()</CompoundTagChild>
127+
<CompoundTagChild on:click={() => console.log('Clicked return')}>return</CompoundTagChild>
128+
</CompoundTagRoot>
129+
</Story>
130+
131+
<Story name="With Action Menu" let:args>
132+
<CompoundTagRoot size={args.size}>
133+
<CompoundTagChild on:click={() => console.log('Clicked Tag')}>Tag</CompoundTagChild>
134+
<CompoundTagChild on:click={() => console.log('Clicked is')}>is</CompoundTagChild>
135+
<Popover let:toggle placement="bottom-start" padding="none">
136+
<CompoundTagChild selected on:click={toggle}>Actions</CompoundTagChild>
137+
<svelte:fragment slot="tooltip">
138+
<ActionMenu.Root>
139+
<ActionMenu.Item.Button
140+
leadingIcon={IconInfo}
141+
trailingIcon={IconChevronDoubleRight}
142+
badge="1"
143+
>
144+
Default
145+
</ActionMenu.Item.Button>
146+
<ActionMenu.Item.Button
147+
leadingIcon={IconInfo}
148+
trailingIcon={IconChevronDoubleRight}
149+
badge="1"
150+
>
151+
Default with a very long text
152+
</ActionMenu.Item.Button>
153+
<ActionMenu.Item.Button
154+
leadingIcon={IconInfo}
155+
trailingIcon={IconChevronDoubleRight}
156+
disabled
157+
>
158+
Disabled
159+
</ActionMenu.Item.Button>
160+
<ActionMenu.Item.Button leadingIcon={IconInfo} status="danger">
161+
Danger
162+
</ActionMenu.Item.Button>
163+
<ActionMenu.Item.Button leadingIcon={IconInfo} status="danger" disabled>
164+
Danger Disabled
165+
</ActionMenu.Item.Button>
166+
</ActionMenu.Root>
167+
</svelte:fragment>
168+
</Popover>
169+
<CompoundTagChild dismiss on:click={() => console.log('Clicked close')}>
170+
<Icon icon={IconX} />
171+
</CompoundTagChild>
172+
</CompoundTagRoot>
173+
</Story>

0 commit comments

Comments
 (0)