Skip to content

Commit 52e6f45

Browse files
authored
Merge pull request #314 from appwrite/feat-spreadsheet
2 parents 0bad73d + 18188b7 commit 52e6f45

31 files changed

+4127
-72
lines changed
27.5 KB
Loading
27.5 KB
Loading
27.5 KB
Loading

v2/pink-sb/.storybook/main.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import { dirname, join } from 'path';
2+
import { createRequire } from 'node:module';
3+
4+
const require = createRequire(import.meta.url);
25

36
/** @type { import('@storybook/sveltekit').StorybookConfig } */
47
const config = {

v2/pink-sb/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"scripts": {
55
"dev": "storybook dev -p 6006",
66
"dev:package": "svelte-kit sync && svelte-package --watch",
7-
"build": "vite build && pnpm run package && pnpm run sb:build",
7+
"build": "vite build && pnpm run package",
88
"preview": "vite preview",
99
"package": "svelte-kit sync && svelte-package && publint",
1010
"prepublishOnly": "pnpm run package",

v2/pink-sb/src/lib/Dialog.svelte

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,11 @@
4848
<Icon icon={IconX} />
4949
</Button>
5050
</Stack>
51-
<Text variant="m-400" color="--fgcolor-neutral-secondary">
52-
<slot />
53-
</Text>
51+
<div class="dialog-content">
52+
<Text variant="m-400" color="--fgcolor-neutral-secondary">
53+
<slot />
54+
</Text>
55+
</div>
5456
</header>
5557
<footer>
5658
<slot name="footer">
@@ -107,6 +109,10 @@
107109
border-bottom: var(--border-width-s) solid var(--border-neutral);
108110
background: var(--bgcolor-neutral-primary);
109111
padding-block-start: var(--space-7);
112+
113+
& .dialog-content {
114+
width: 100%;
115+
}
110116
}
111117
}
112118

v2/pink-sb/src/lib/Popover.svelte

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
<script lang="ts">
2+
import { fade } from 'svelte/transition';
23
import { activePopover } from './context.js';
34
import type { Placement } from '@floating-ui/dom';
4-
import { onMount, hasContext, setContext } from 'svelte';
5+
import { onMount, hasContext, setContext, tick } from 'svelte';
56
import { computePosition, autoUpdate, shift, offset, flip } from '@floating-ui/dom';
67
78
export let portal: boolean = false;
@@ -17,6 +18,8 @@
1718
1819
$: showTooltip = $activeInstance === id;
1920
21+
setContext('popover-group', true);
22+
2023
function toggle(e?: Event) {
2124
return tooltipAction(e, 'toggle');
2225
}
@@ -60,10 +63,12 @@
6063
6164
async function update() {
6265
if (!referenceElement || !tooltipElement) return;
66+
6367
const firstChild = referenceElement.firstElementChild;
6468
if (!(firstChild instanceof HTMLElement)) {
6569
return;
6670
}
71+
6772
const { x, y } = await computePosition(firstChild, tooltipElement, {
6873
placement,
6974
middleware: [offset(2), flip(), shift()]
@@ -96,27 +101,42 @@
96101
};
97102
}
98103
99-
setContext('popover-group', true);
104+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
105+
function autoUpdateAction(_: HTMLDivElement) {
106+
let cleanup: (() => void) | undefined;
100107
101-
onMount(() => autoUpdate(referenceElement, tooltipElement, update));
108+
tick().then(() => {
109+
if (!referenceElement || !tooltipElement) return;
110+
cleanup = autoUpdate(referenceElement, tooltipElement, update);
111+
});
112+
113+
return {
114+
destroy: () => cleanup?.()
115+
};
116+
}
102117
</script>
103118

104119
<svelte:window on:click={onBlur} on:keydown={onKeyDown} on:resize={update} />
105120

106121
<span aria-describedby={id} bind:this={referenceElement}>
107122
<slot showing={showTooltip} {toggle} {update} {show} {hide} />
108123
</span>
109-
<div
110-
{id}
111-
role="tooltip"
112-
aria-hidden={!showTooltip}
113-
bind:this={tooltipElement}
114-
class:padding-m={padding === 'm'}
115-
class:padding-none={padding === 'none'}
116-
use:portalPopover
117-
>
118-
<slot showing={showTooltip} {toggle} {update} {show} {hide} name="tooltip" />
119-
</div>
124+
125+
{#if showTooltip}
126+
<div
127+
{id}
128+
role="tooltip"
129+
aria-hidden="false"
130+
bind:this={tooltipElement}
131+
class:padding-m={padding === 'm'}
132+
class:padding-none={padding === 'none'}
133+
use:portalPopover
134+
use:autoUpdateAction
135+
transition:fade={{ duration: 150 }}
136+
>
137+
<slot showing={showTooltip} {toggle} {update} {show} {hide} name="tooltip" />
138+
</div>
139+
{/if}
120140

121141
<style lang="scss">
122142
span {

v2/pink-sb/src/lib/Tooltip.svelte

Lines changed: 93 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,59 @@
11
<script lang="ts">
2+
import { tick, hasContext } from 'svelte';
23
import type { Placement } from '@floating-ui/dom';
34
import { autoUpdate, computePosition, flip, offset, shift } from '@floating-ui/dom';
4-
import { onMount } from 'svelte';
55
6+
export let portal: boolean = false;
67
export let placement: Placement | undefined = undefined;
78
export let padding: 'none' | 'm' = 'm';
89
export let offsetAmount: number = 6;
910
export let disabled = false;
1011
export let maxWidth = '11.25rem';
12+
export let delay: number = 0;
1113
1214
let show = false;
1315
let showing = false;
14-
const id = 'tooltip-' + Math.random().toString(36).substring(2, 9);
15-
let referenceElement: HTMLSpanElement;
16+
let delayTimeout: ReturnType<typeof setTimeout>;
17+
1618
let tooltipElement: HTMLDivElement;
19+
let referenceElement: HTMLSpanElement;
20+
const id = 'tooltip-' + Math.random().toString(36).substring(2, 9);
21+
22+
const inDialogGroup = hasContext('dialog-group');
1723
1824
async function showTooltip() {
19-
await update();
20-
showing = show = !disabled;
25+
if (disabled) return;
26+
27+
if (delayTimeout) {
28+
clearTimeout(delayTimeout);
29+
}
30+
31+
if (delay > 0) {
32+
delayTimeout = setTimeout(async () => {
33+
await update();
34+
showing = show = true;
35+
}, delay);
36+
} else {
37+
await update();
38+
showing = show = true;
39+
}
2140
}
2241
2342
function hideTooltip() {
43+
if (delayTimeout) {
44+
clearTimeout(delayTimeout);
45+
}
2446
show = false;
2547
}
2648
2749
async function update() {
50+
if (!referenceElement || !tooltipElement) return;
51+
2852
const firstChild = referenceElement.firstElementChild;
2953
if (!(firstChild instanceof HTMLElement)) {
3054
return;
3155
}
56+
3257
const { x, y } = await computePosition(firstChild, tooltipElement, {
3358
placement,
3459
middleware: [offset(offsetAmount), flip(), shift()]
@@ -40,7 +65,50 @@
4065
});
4166
}
4267
43-
onMount(() => autoUpdate(referenceElement, tooltipElement, update));
68+
function portalPopover(node: HTMLElement) {
69+
if (!portal && !inDialogGroup) return;
70+
71+
const target = !inDialogGroup
72+
? document.body
73+
: // can be inside a modal/dialog
74+
document.body.querySelector<HTMLDialogElement>('dialog[open]');
75+
76+
if (target) {
77+
target.appendChild(node);
78+
}
79+
80+
return {
81+
destroy() {
82+
if (target && node.parentNode === target) {
83+
target.removeChild(node);
84+
}
85+
}
86+
};
87+
}
88+
89+
function fadeSlide(_: Node, { y = 8, duration = 200 } = {}) {
90+
return {
91+
duration,
92+
css: (time: number) => `
93+
opacity: ${time};
94+
transform: translateY(${(1 - time) * y}px);
95+
`
96+
};
97+
}
98+
99+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
100+
function autoUpdateAction(_: HTMLDivElement) {
101+
let cleanup: (() => void) | undefined;
102+
103+
tick().then(() => {
104+
if (!referenceElement || !tooltipElement) return;
105+
cleanup = autoUpdate(referenceElement, tooltipElement, update);
106+
});
107+
108+
return {
109+
destroy: () => cleanup?.()
110+
};
111+
}
44112
</script>
45113

46114
<svelte:window on:resize={update} />
@@ -57,19 +125,25 @@
57125
>
58126
<slot {showing} {update} />
59127
</span>
60-
<div
61-
{id}
62-
on:transitionend={() => (showing = false)}
63-
bind:this={tooltipElement}
64-
aria-hidden={!show}
65-
class:padding-none={padding === 'none'}
66-
class:padding-m={padding === 'm'}
67-
role="tooltip"
68-
style:max-inline-size={maxWidth}
69-
data-state={!show ? 'closed' : 'open'}
70-
>
71-
<slot {showing} {update} name="tooltip" />
72-
</div>
128+
129+
{#if show}
130+
<div
131+
{id}
132+
transition:fadeSlide
133+
use:autoUpdateAction
134+
use:portalPopover
135+
on:transitionend={() => (showing = false)}
136+
bind:this={tooltipElement}
137+
aria-hidden={!show}
138+
class:padding-none={padding === 'none'}
139+
class:padding-m={padding === 'm'}
140+
role="tooltip"
141+
style:max-inline-size={maxWidth}
142+
data-state={!show ? 'closed' : 'open'}
143+
>
144+
<slot {showing} {update} name="tooltip" />
145+
</div>
146+
{/if}
73147

74148
<style lang="scss">
75149
[role='note'] {
@@ -88,7 +162,6 @@
88162
color: var(--fgcolor-on-invert);
89163
visibility: hidden;
90164
opacity: 0;
91-
transition: visibility 0s linear 0.2s;
92165
z-index: 9002;
93166
94167
&[aria-hidden='false'] {
@@ -104,34 +177,5 @@
104177
padding: var(--space-2) var(--space-4);
105178
}
106179
}
107-
108-
&[data-state='open'] {
109-
animation: pink-tooltip-enter 0.2s ease-out;
110-
}
111-
112-
&[data-state='closed'] {
113-
animation: pink-tooltip-exit 0.2s ease-out;
114-
}
115-
}
116-
@keyframes pink-tooltip-enter {
117-
from {
118-
opacity: 0;
119-
transform: translateY(0.5rem);
120-
}
121-
to {
122-
opacity: 1;
123-
transform: translateY(0);
124-
}
125-
}
126-
127-
@keyframes pink-tooltip-exit {
128-
from {
129-
opacity: 1;
130-
transform: translateY(0);
131-
}
132-
to {
133-
opacity: 0;
134-
transform: translateY(0.5rem);
135-
}
136180
}
137181
</style>
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<script lang="ts">
2+
import { Divider } from '../index.js';
3+
import { autofocusInput } from '$lib/input/autofocus.js';
4+
import type { HTMLInputAttributes } from 'svelte/elements';
5+
6+
type $$Props = HTMLInputAttributes & {
7+
autofocus?: boolean;
8+
};
9+
10+
export let value: $$Props['value'] = '';
11+
export let type: $$Props['type'] = 'text';
12+
export let disabled: $$Props['disabled'] = false;
13+
export let readonly: $$Props['readonly'] = false;
14+
export let required: $$Props['required'] = false;
15+
export let maxlength: $$Props['maxlength'] = undefined;
16+
export let id: $$Props['id'] = undefined;
17+
export let autofocus: $$Props['autofocus'] = false;
18+
</script>
19+
20+
<div class="input-text-wrapper">
21+
<input
22+
bind:value
23+
{...{ type }}
24+
{disabled}
25+
{readonly}
26+
{required}
27+
{maxlength}
28+
{id}
29+
{...$$restProps}
30+
use:autofocusInput={autofocus}
31+
/>
32+
33+
<Divider />
34+
</div>
35+
36+
<style lang="scss">
37+
.input-text-wrapper {
38+
padding-block-end: 0.5rem;
39+
40+
& input {
41+
padding: var(--space-3) var(--space-3) var(--space-3) var(--space-5);
42+
43+
inline-size: 100%;
44+
line-height: 140%;
45+
46+
&:disabled {
47+
color: var(--fgcolor-neutral-tertiary);
48+
}
49+
50+
&::placeholder {
51+
color: var(--fgcolor-neutral-tertiary);
52+
}
53+
54+
&:is(:-webkit-autofill, :autofill) {
55+
-webkit-background-clip: text;
56+
}
57+
58+
&::-webkit-calendar-picker-indicator {
59+
filter: invert(0.5);
60+
}
61+
}
62+
}
63+
</style>

0 commit comments

Comments
 (0)