mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
feat: inputs
This commit is contained in:
+11
-11
@@ -23,14 +23,14 @@
|
||||
"@appwrite.io/pink": "0.25.0",
|
||||
"@appwrite.io/pink-icons": "0.25.0",
|
||||
"@appwrite.io/pink-icons-svelte": "1.0.0-next.3",
|
||||
"@appwrite.io/pink-svelte": "1.0.0-next.20",
|
||||
"@appwrite.io/pink-svelte": "1.0.0-next.22",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@sentry/sveltekit": "^8.26.0",
|
||||
"@sentry/sveltekit": "^8.27.0",
|
||||
"@stripe/stripe-js": "^3.5.0",
|
||||
"ai": "^2.2.37",
|
||||
"analytics": "^0.8.14",
|
||||
"cron-parser": "^4.9.0",
|
||||
"dayjs": "^1.11.12",
|
||||
"dayjs": "^1.11.13",
|
||||
"deep-equal": "^2.2.3",
|
||||
"echarts": "^5.5.1",
|
||||
"envfile": "^7.1.0",
|
||||
@@ -44,12 +44,12 @@
|
||||
"devDependencies": {
|
||||
"@melt-ui/pp": "^0.3.2",
|
||||
"@melt-ui/svelte": "^0.83.0",
|
||||
"@playwright/test": "^1.46.0",
|
||||
"@playwright/test": "^1.46.1",
|
||||
"@sveltejs/adapter-static": "^3.0.4",
|
||||
"@sveltejs/kit": "^2.5.22",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.1.1",
|
||||
"@sveltejs/kit": "^2.5.25",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.1.2",
|
||||
"@testing-library/dom": "^10.4.0",
|
||||
"@testing-library/jest-dom": "^6.4.8",
|
||||
"@testing-library/jest-dom": "^6.5.0",
|
||||
"@testing-library/svelte": "^5.2.1",
|
||||
"@testing-library/user-event": "^14.5.2",
|
||||
"@types/deep-equal": "^1.0.4",
|
||||
@@ -65,14 +65,14 @@
|
||||
"prettier": "^3.3.3",
|
||||
"prettier-plugin-svelte": "^3.2.6",
|
||||
"sass": "^1.77.8",
|
||||
"svelte": "^4.2.18",
|
||||
"svelte-check": "^3.8.5",
|
||||
"svelte": "^4.2.19",
|
||||
"svelte-check": "^3.8.6",
|
||||
"svelte-jester": "^2.3.2",
|
||||
"svelte-preprocess": "^6.0.2",
|
||||
"svelte-sequential-preprocessor": "^2.0.1",
|
||||
"tslib": "^2.6.3",
|
||||
"tslib": "^2.7.0",
|
||||
"typescript": "^5.5.4",
|
||||
"vite": "^5.4.0",
|
||||
"vite": "^5.4.2",
|
||||
"vitest": "^1.6.0"
|
||||
},
|
||||
"type": "module",
|
||||
|
||||
Generated
+553
-539
File diff suppressed because it is too large
Load Diff
@@ -3,10 +3,10 @@
|
||||
import { getContext, hasContext } from 'svelte';
|
||||
import { readable } from 'svelte/store';
|
||||
import type { FormContext } from './form.svelte';
|
||||
import { Button, Anchor } from '@appwrite.io/pink-svelte';
|
||||
import { Button } from '@appwrite.io/pink-svelte';
|
||||
import type { ComponentProps } from 'svelte';
|
||||
|
||||
type Props = ComponentProps<Button>;
|
||||
type Props = ComponentProps<Button.Button>;
|
||||
|
||||
export let submit = false;
|
||||
export let secondary = false;
|
||||
@@ -53,7 +53,7 @@
|
||||
</script>
|
||||
|
||||
{#if href}
|
||||
<Anchor
|
||||
<Button.Anchor
|
||||
on:click
|
||||
on:click={track}
|
||||
{href}
|
||||
@@ -67,9 +67,9 @@
|
||||
aria-label={ariaLabel}
|
||||
--button-width={fullWidth ? '100%' : 'fit-content'}>
|
||||
<slot />
|
||||
</Anchor>
|
||||
</Button.Anchor>
|
||||
{:else}
|
||||
<Button
|
||||
<Button.Button
|
||||
on:click
|
||||
on:click={track}
|
||||
{size}
|
||||
@@ -86,5 +86,5 @@
|
||||
aria-hidden="true" />
|
||||
{/if}
|
||||
<slot isSubmitting={$isSubmitting} />
|
||||
</Button>
|
||||
</Button.Button>
|
||||
{/if}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { FormItem, Helper, Label } from '.';
|
||||
import NullCheckbox from './nullCheckbox.svelte';
|
||||
import { Drop } from '$lib/components';
|
||||
import { Input } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let label: string;
|
||||
export let optionalText: string | undefined = undefined;
|
||||
@@ -10,6 +11,7 @@
|
||||
export let id: string;
|
||||
export let value = '';
|
||||
export let placeholder = '';
|
||||
export let name: string | undefined = undefined;
|
||||
export let required = false;
|
||||
export let nullable = false;
|
||||
export let disabled = false;
|
||||
@@ -21,101 +23,38 @@
|
||||
export let popoverProps: Record<string, unknown> = {};
|
||||
export let fullWidth = false;
|
||||
|
||||
let element: HTMLInputElement;
|
||||
let error: string;
|
||||
let show = false;
|
||||
|
||||
onMount(() => {
|
||||
if (element && autofocus) {
|
||||
element.focus();
|
||||
}
|
||||
});
|
||||
|
||||
const handleInvalid = (event: Event) => {
|
||||
const handleInvalid = (event: Event & { currentTarget: EventTarget & HTMLInputElement }) => {
|
||||
event.preventDefault();
|
||||
if (element.validity.typeMismatch) {
|
||||
if (event.currentTarget.validity.typeMismatch) {
|
||||
error = 'Emails should be formatted as: name@example.com';
|
||||
return;
|
||||
}
|
||||
if (element.validity.valueMissing) {
|
||||
if (event.currentTarget.validity.valueMissing) {
|
||||
error = 'This field is required';
|
||||
return;
|
||||
}
|
||||
error = element.validationMessage;
|
||||
error = event.currentTarget.validationMessage;
|
||||
};
|
||||
|
||||
$: if (value) {
|
||||
error = null;
|
||||
}
|
||||
|
||||
let prevValue = '';
|
||||
function handleNullChange(e: CustomEvent<boolean>) {
|
||||
const isNull = e.detail;
|
||||
if (isNull) {
|
||||
prevValue = value;
|
||||
value = null;
|
||||
} else {
|
||||
value = prevValue;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<FormItem {fullWidth}>
|
||||
<Label {required} {optionalText} {tooltip} hide={!showLabel} for={id}>
|
||||
{label}{#if popover}
|
||||
<Drop isPopover bind:show display="inline-block">
|
||||
<!-- TODO: make unclicked icon greyed out and hover and clicked filled -->
|
||||
<button
|
||||
type="button"
|
||||
on:click={() => (show = !show)}
|
||||
class="tooltip"
|
||||
aria-label="input tooltip">
|
||||
<span
|
||||
class="icon-info"
|
||||
aria-hidden="true"
|
||||
style="font-size: var(--icon-size-small)" />
|
||||
</button>
|
||||
<svelte:fragment slot="list">
|
||||
<div
|
||||
class="dropped card u-max-width-250"
|
||||
style:--card-border-radius="var(--border-radius-small)"
|
||||
style:--p-card-padding=".75rem"
|
||||
style:box-shadow="var(--shadow-large)">
|
||||
<svelte:component this={popover} {...popoverProps} />
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Drop>
|
||||
{/if}
|
||||
</Label>
|
||||
|
||||
<div
|
||||
class:input-text-wrapper={!$$slots.default}
|
||||
class:u-flex={$$slots.default}
|
||||
class:u-gap-16={$$slots.default}
|
||||
class:u-cross-center={$$slots.default}>
|
||||
<input
|
||||
{id}
|
||||
{placeholder}
|
||||
{disabled}
|
||||
{required}
|
||||
{readonly}
|
||||
type="email"
|
||||
class="input-text"
|
||||
autocomplete={autocomplete ? 'on' : 'off'}
|
||||
bind:value
|
||||
bind:this={element}
|
||||
on:invalid={handleInvalid} />
|
||||
{#if nullable && !required}
|
||||
<ul
|
||||
class="buttons-list u-cross-center u-gap-8 u-position-absolute u-inset-block-start-8 u-inset-block-end-8 u-inset-inline-end-12">
|
||||
<li class="buttons-list-item">
|
||||
<NullCheckbox checked={value === null} on:change={handleNullChange} />
|
||||
</li>
|
||||
</ul>
|
||||
{/if}
|
||||
<slot />
|
||||
</div>
|
||||
{#if error}
|
||||
<Helper type="warning">{error}</Helper>
|
||||
{/if}
|
||||
</FormItem>
|
||||
<Input.Text
|
||||
{id}
|
||||
{name}
|
||||
{placeholder}
|
||||
{disabled}
|
||||
{required}
|
||||
{label}
|
||||
{nullable}
|
||||
{autofocus}
|
||||
on:input
|
||||
on:invalid={handleInvalid}
|
||||
type="email"
|
||||
helper={error}
|
||||
autocomplete={autocomplete ? 'on' : 'off'}
|
||||
bind:value />
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { SvelteComponent, onMount } from 'svelte';
|
||||
import { FormItem, Helper, Label } from '.';
|
||||
import { Drop } from '$lib/components';
|
||||
import { SvelteComponent } from 'svelte';
|
||||
import { Input } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let id: string;
|
||||
export let label: string;
|
||||
@@ -20,121 +19,37 @@
|
||||
export let popoverProps: Record<string, unknown> = {};
|
||||
export let fullWidth = false;
|
||||
|
||||
let element: HTMLInputElement;
|
||||
let error: string;
|
||||
let showInPlainText = false;
|
||||
let showPopover = false;
|
||||
|
||||
onMount(() => {
|
||||
if (element && autofocus) {
|
||||
element.focus();
|
||||
}
|
||||
});
|
||||
|
||||
const handleInvalid = (event: Event) => {
|
||||
const handleInvalid = (event: Event & { currentTarget: EventTarget & HTMLInputElement }) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (element.validity.valueMissing) {
|
||||
if (event.currentTarget.validity.valueMissing) {
|
||||
error = 'This field is required';
|
||||
return;
|
||||
}
|
||||
if (element.validity.tooShort) {
|
||||
if (event.currentTarget.validity.tooShort) {
|
||||
error = 'Password should contain at least 8 characters';
|
||||
return;
|
||||
}
|
||||
error = element.validationMessage;
|
||||
error = event.currentTarget.validationMessage;
|
||||
};
|
||||
|
||||
$: strength = value ? value.length * 10 : 0;
|
||||
$: if (value) {
|
||||
error = null;
|
||||
}
|
||||
</script>
|
||||
|
||||
<FormItem {fullWidth}>
|
||||
<Label {required} hide={!showLabel} for={id}>
|
||||
{label}{#if popover}
|
||||
<Drop isPopover bind:show={showPopover} display="inline-block">
|
||||
<!-- TODO: make unclicked icon greyed out and hover and clicked filled -->
|
||||
<button
|
||||
type="button"
|
||||
on:click={() => (showPopover = !showPopover)}
|
||||
class="tooltip"
|
||||
aria-label="input tooltip">
|
||||
<span
|
||||
class="icon-info"
|
||||
aria-hidden="true"
|
||||
style="font-size: var(--icon-size-small)" />
|
||||
</button>
|
||||
<svelte:fragment slot="list">
|
||||
<div
|
||||
class="dropped card u-max-width-250"
|
||||
style:--card-border-radius="var(--border-radius-small)"
|
||||
style:--p-card-padding=".75rem"
|
||||
style:box-shadow="var(--shadow-large)">
|
||||
<svelte:component this={popover} {...popoverProps} />
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Drop>
|
||||
{/if}
|
||||
</Label>
|
||||
|
||||
<div class="input-text-wrapper" style={showPasswordButton ? '--amount-of-buttons: 1' : ''}>
|
||||
{#if showInPlainText}
|
||||
<input
|
||||
{id}
|
||||
{placeholder}
|
||||
{disabled}
|
||||
{minlength}
|
||||
{maxlength}
|
||||
name={id}
|
||||
type="text"
|
||||
class="input-text"
|
||||
autocomplete={autocomplete ? 'on' : 'off'}
|
||||
bind:value
|
||||
bind:this={element}
|
||||
on:invalid={handleInvalid} />
|
||||
{:else}
|
||||
<input
|
||||
{id}
|
||||
{placeholder}
|
||||
{disabled}
|
||||
{required}
|
||||
{minlength}
|
||||
{maxlength}
|
||||
type="password"
|
||||
class="input-text"
|
||||
autocomplete={autocomplete ? 'on' : 'off'}
|
||||
bind:value
|
||||
bind:this={element}
|
||||
on:invalid={handleInvalid} />
|
||||
{/if}
|
||||
|
||||
{#if meter}
|
||||
<meter
|
||||
value={strength > 100 ? 100 : strength}
|
||||
min="0"
|
||||
max="100"
|
||||
class="password-meter"
|
||||
class:is-weak={strength !== 0 && strength <= 33}
|
||||
class:is-medium={strength > 33 && strength <= 66}
|
||||
class:is-strong={strength > 66 && strength <= 100}
|
||||
aria-label="Password strength weak" />
|
||||
{/if}
|
||||
{#if showPasswordButton}
|
||||
<button
|
||||
type="button"
|
||||
on:click={() => (showInPlainText = !showInPlainText)}
|
||||
class="show-password-button"
|
||||
aria-label="show password">
|
||||
<span
|
||||
class:icon-eye={!showInPlainText}
|
||||
class:icon-eye-off={showInPlainText}
|
||||
aria-hidden="true" />
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
{#if error}
|
||||
<Helper type="warning">{error}</Helper>
|
||||
{/if}
|
||||
</FormItem>
|
||||
<Input.Password
|
||||
{id}
|
||||
{label}
|
||||
{required}
|
||||
{disabled}
|
||||
{autofocus}
|
||||
{minlength}
|
||||
{maxlength}
|
||||
autocomplete={autocomplete ? 'on' : 'off'}
|
||||
{placeholder}
|
||||
helper={error}
|
||||
on:invalid={handleInvalid}
|
||||
bind:value />
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { SvelteComponent, onMount } from 'svelte';
|
||||
import { FormItem, FormItemPart, Helper, Label } from '.';
|
||||
import NullCheckbox from './nullCheckbox.svelte';
|
||||
import TextCounter from './textCounter.svelte';
|
||||
import { Drop } from '$lib/components';
|
||||
import { Input } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let label: string = undefined;
|
||||
export let optionalText: string | undefined = undefined;
|
||||
@@ -26,124 +23,42 @@
|
||||
export let popover: typeof SvelteComponent<unknown> = null;
|
||||
export let popoverProps: Record<string, unknown> = {};
|
||||
|
||||
let element: HTMLInputElement;
|
||||
let error: string;
|
||||
let show = false;
|
||||
|
||||
onMount(() => {
|
||||
if (element && autofocus) {
|
||||
element.focus();
|
||||
}
|
||||
});
|
||||
|
||||
const handleInvalid = (event: Event) => {
|
||||
const handleInvalid = (event: Event & { currentTarget: EventTarget & HTMLInputElement }) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (element.validity.valueMissing) {
|
||||
if (event.currentTarget.validity.valueMissing) {
|
||||
error = 'This field is required';
|
||||
return;
|
||||
}
|
||||
|
||||
error = element.validationMessage;
|
||||
error = event.currentTarget.validationMessage;
|
||||
};
|
||||
|
||||
$: {
|
||||
value;
|
||||
if (element?.validity?.valid) {
|
||||
if (value) {
|
||||
error = null;
|
||||
}
|
||||
}
|
||||
|
||||
let prevValue = '';
|
||||
function handleNullChange(e: CustomEvent<boolean>) {
|
||||
const isNull = e.detail;
|
||||
if (isNull) {
|
||||
prevValue = value;
|
||||
value = null;
|
||||
} else {
|
||||
value = prevValue;
|
||||
}
|
||||
}
|
||||
|
||||
$: showTextCounter = !!maxlength;
|
||||
$: showNullCheckbox = nullable && !required;
|
||||
|
||||
type $$Events = {
|
||||
input: Event & { target: HTMLInputElement };
|
||||
};
|
||||
|
||||
$: wrapper = isMultiple ? FormItemPart : FormItem;
|
||||
</script>
|
||||
|
||||
<svelte:component this={wrapper} {fullWidth}>
|
||||
{#if label}
|
||||
<Label {required} {hideRequired} {tooltip} {optionalText} hide={!showLabel} for={id}>
|
||||
{label}{#if popover}
|
||||
<Drop isPopover bind:show display="inline-block">
|
||||
<!-- TODO: make unclicked icon greyed out and hover and clicked filled -->
|
||||
<button
|
||||
type="button"
|
||||
on:click={() => (show = !show)}
|
||||
class="tooltip"
|
||||
aria-label="input tooltip">
|
||||
<span
|
||||
class="icon-info"
|
||||
aria-hidden="true"
|
||||
style:font-size="var(--icon-size-small)" />
|
||||
</button>
|
||||
<svelte:fragment slot="list">
|
||||
<div
|
||||
class="dropped card u-max-width-250"
|
||||
style:--p-card-padding=".75rem"
|
||||
style:--card-border-radius="var(--border-radius-small)"
|
||||
style:box-shadow="var(--shadow-large)">
|
||||
<svelte:component this={popover} {...popoverProps} />
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Drop>
|
||||
{/if}
|
||||
</Label>
|
||||
{/if}
|
||||
|
||||
<div
|
||||
class:input-text-wrapper={!$$slots.default}
|
||||
class:u-flex={$$slots.default}
|
||||
class:u-gap-8={$$slots.default}
|
||||
class:u-cross-center={$$slots.default}>
|
||||
<input
|
||||
{id}
|
||||
{name}
|
||||
{placeholder}
|
||||
{disabled}
|
||||
{readonly}
|
||||
{required}
|
||||
{maxlength}
|
||||
autocomplete={autocomplete ? 'on' : 'off'}
|
||||
type="text"
|
||||
class="input-text"
|
||||
bind:value
|
||||
class:u-padding-inline-end-56={typeof maxlength === 'number'}
|
||||
bind:this={element}
|
||||
on:invalid={handleInvalid}
|
||||
on:input />
|
||||
{#if showTextCounter || showNullCheckbox}
|
||||
<ul
|
||||
class="buttons-list u-cross-center u-gap-8 u-position-absolute u-inset-block-start-8 u-inset-block-end-8 u-inset-inline-end-12">
|
||||
{#if showTextCounter}
|
||||
<li class="buttons-list-item">
|
||||
<TextCounter max={maxlength} count={value?.length ?? 0} />
|
||||
</li>
|
||||
{/if}
|
||||
{#if showNullCheckbox}
|
||||
<li class="buttons-list-item">
|
||||
<NullCheckbox checked={value === null} on:change={handleNullChange} />
|
||||
</li>
|
||||
{/if}
|
||||
</ul>
|
||||
{/if}
|
||||
<slot />
|
||||
</div>
|
||||
{#if error}
|
||||
<Helper type="warning">{error}</Helper>
|
||||
{/if}
|
||||
</svelte:component>
|
||||
<Input.Text
|
||||
{id}
|
||||
{name}
|
||||
{placeholder}
|
||||
{disabled}
|
||||
{required}
|
||||
{maxlength}
|
||||
{label}
|
||||
{nullable}
|
||||
{autofocus}
|
||||
helper={error}
|
||||
on:invalid={handleInvalid}
|
||||
on:input
|
||||
autocomplete={autocomplete ? 'on' : 'off'}
|
||||
bind:value />
|
||||
|
||||
Reference in New Issue
Block a user