feat: lint codebase and run precommit hook
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
import Root from "./button-group.svelte";
|
||||
import Text from "./button-group-text.svelte";
|
||||
import Separator from "./button-group-separator.svelte";
|
||||
import Root from './button-group.svelte';
|
||||
import Text from './button-group-text.svelte';
|
||||
import Separator from './button-group-separator.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
Text,
|
||||
Separator,
|
||||
//
|
||||
Root as ButtonGroup,
|
||||
Text as ButtonGroupText,
|
||||
Separator as ButtonGroupSeparator,
|
||||
Root,
|
||||
Text,
|
||||
Separator,
|
||||
//
|
||||
Root as ButtonGroup,
|
||||
Text as ButtonGroupText,
|
||||
Separator as ButtonGroupSeparator
|
||||
};
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
import Root from "./card.svelte";
|
||||
import Content from "./card-content.svelte";
|
||||
import Description from "./card-description.svelte";
|
||||
import Footer from "./card-footer.svelte";
|
||||
import Header from "./card-header.svelte";
|
||||
import Title from "./card-title.svelte";
|
||||
import Action from "./card-action.svelte";
|
||||
import Root from './card.svelte';
|
||||
import Content from './card-content.svelte';
|
||||
import Description from './card-description.svelte';
|
||||
import Footer from './card-footer.svelte';
|
||||
import Header from './card-header.svelte';
|
||||
import Title from './card-title.svelte';
|
||||
import Action from './card-action.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
Content,
|
||||
Description,
|
||||
Footer,
|
||||
Header,
|
||||
Title,
|
||||
Action,
|
||||
//
|
||||
Root as Card,
|
||||
Content as CardContent,
|
||||
Description as CardDescription,
|
||||
Footer as CardFooter,
|
||||
Header as CardHeader,
|
||||
Title as CardTitle,
|
||||
Action as CardAction,
|
||||
Root,
|
||||
Content,
|
||||
Description,
|
||||
Footer,
|
||||
Header,
|
||||
Title,
|
||||
Action,
|
||||
//
|
||||
Root as Card,
|
||||
Content as CardContent,
|
||||
Description as CardDescription,
|
||||
Footer as CardFooter,
|
||||
Header as CardHeader,
|
||||
Title as CardTitle,
|
||||
Action as CardAction
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { Drawer as DrawerPrimitive } from "vaul-svelte";
|
||||
import { Drawer as DrawerPrimitive } from 'vaul-svelte';
|
||||
|
||||
let { ref = $bindable(null), ...restProps }: DrawerPrimitive.CloseProps = $props();
|
||||
let { ref = $bindable(null), ...restProps }: DrawerPrimitive.CloseProps = $props();
|
||||
</script>
|
||||
|
||||
<DrawerPrimitive.Close bind:ref data-slot="drawer-close" {...restProps} />
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { Drawer as DrawerPrimitive } from "vaul-svelte";
|
||||
import { Drawer as DrawerPrimitive } from 'vaul-svelte';
|
||||
|
||||
let { ref = $bindable(null), ...restProps }: DrawerPrimitive.TriggerProps = $props();
|
||||
let { ref = $bindable(null), ...restProps }: DrawerPrimitive.TriggerProps = $props();
|
||||
</script>
|
||||
|
||||
<DrawerPrimitive.Trigger bind:ref data-slot="drawer-trigger" {...restProps} />
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { Drawer as DrawerPrimitive } from "vaul-svelte";
|
||||
import { Drawer as DrawerPrimitive } from 'vaul-svelte';
|
||||
|
||||
let {
|
||||
shouldScaleBackground = true,
|
||||
open = $bindable(false),
|
||||
activeSnapPoint = $bindable(null),
|
||||
...restProps
|
||||
}: DrawerPrimitive.RootProps = $props();
|
||||
let {
|
||||
shouldScaleBackground = true,
|
||||
open = $bindable(false),
|
||||
activeSnapPoint = $bindable(null),
|
||||
...restProps
|
||||
}: DrawerPrimitive.RootProps = $props();
|
||||
</script>
|
||||
|
||||
<DrawerPrimitive.Root {shouldScaleBackground} bind:open bind:activeSnapPoint {...restProps} />
|
||||
|
||||
@@ -1,41 +1,41 @@
|
||||
import { Drawer as DrawerPrimitive } from "vaul-svelte";
|
||||
import { Drawer as DrawerPrimitive } from 'vaul-svelte';
|
||||
|
||||
import Root from "./drawer.svelte";
|
||||
import Content from "./drawer-content.svelte";
|
||||
import Description from "./drawer-description.svelte";
|
||||
import Overlay from "./drawer-overlay.svelte";
|
||||
import Footer from "./drawer-footer.svelte";
|
||||
import Header from "./drawer-header.svelte";
|
||||
import Title from "./drawer-title.svelte";
|
||||
import NestedRoot from "./drawer-nested.svelte";
|
||||
import Close from "./drawer-close.svelte";
|
||||
import Trigger from "./drawer-trigger.svelte";
|
||||
import Root from './drawer.svelte';
|
||||
import Content from './drawer-content.svelte';
|
||||
import Description from './drawer-description.svelte';
|
||||
import Overlay from './drawer-overlay.svelte';
|
||||
import Footer from './drawer-footer.svelte';
|
||||
import Header from './drawer-header.svelte';
|
||||
import Title from './drawer-title.svelte';
|
||||
import NestedRoot from './drawer-nested.svelte';
|
||||
import Close from './drawer-close.svelte';
|
||||
import Trigger from './drawer-trigger.svelte';
|
||||
|
||||
const Portal: typeof DrawerPrimitive.Portal = DrawerPrimitive.Portal;
|
||||
|
||||
export {
|
||||
Root,
|
||||
NestedRoot,
|
||||
Content,
|
||||
Description,
|
||||
Overlay,
|
||||
Footer,
|
||||
Header,
|
||||
Title,
|
||||
Trigger,
|
||||
Portal,
|
||||
Close,
|
||||
Root,
|
||||
NestedRoot,
|
||||
Content,
|
||||
Description,
|
||||
Overlay,
|
||||
Footer,
|
||||
Header,
|
||||
Title,
|
||||
Trigger,
|
||||
Portal,
|
||||
Close,
|
||||
|
||||
//
|
||||
Root as Drawer,
|
||||
NestedRoot as DrawerNestedRoot,
|
||||
Content as DrawerContent,
|
||||
Description as DrawerDescription,
|
||||
Overlay as DrawerOverlay,
|
||||
Footer as DrawerFooter,
|
||||
Header as DrawerHeader,
|
||||
Title as DrawerTitle,
|
||||
Trigger as DrawerTrigger,
|
||||
Portal as DrawerPortal,
|
||||
Close as DrawerClose,
|
||||
//
|
||||
Root as Drawer,
|
||||
NestedRoot as DrawerNestedRoot,
|
||||
Content as DrawerContent,
|
||||
Description as DrawerDescription,
|
||||
Overlay as DrawerOverlay,
|
||||
Footer as DrawerFooter,
|
||||
Header as DrawerHeader,
|
||||
Title as DrawerTitle,
|
||||
Trigger as DrawerTrigger,
|
||||
Portal as DrawerPortal,
|
||||
Close as DrawerClose
|
||||
};
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
import Root from "./empty.svelte";
|
||||
import Header from "./empty-header.svelte";
|
||||
import Media from "./empty-media.svelte";
|
||||
import Title from "./empty-title.svelte";
|
||||
import Description from "./empty-description.svelte";
|
||||
import Content from "./empty-content.svelte";
|
||||
import Root from './empty.svelte';
|
||||
import Header from './empty-header.svelte';
|
||||
import Media from './empty-media.svelte';
|
||||
import Title from './empty-title.svelte';
|
||||
import Description from './empty-description.svelte';
|
||||
import Content from './empty-content.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
Header,
|
||||
Media,
|
||||
Title,
|
||||
Description,
|
||||
Content,
|
||||
//
|
||||
Root as Empty,
|
||||
Header as EmptyHeader,
|
||||
Media as EmptyMedia,
|
||||
Title as EmptyTitle,
|
||||
Description as EmptyDescription,
|
||||
Content as EmptyContent,
|
||||
Root,
|
||||
Header,
|
||||
Media,
|
||||
Title,
|
||||
Description,
|
||||
Content,
|
||||
//
|
||||
Root as Empty,
|
||||
Header as EmptyHeader,
|
||||
Media as EmptyMedia,
|
||||
Title as EmptyTitle,
|
||||
Description as EmptyDescription,
|
||||
Content as EmptyContent
|
||||
};
|
||||
|
||||
@@ -1,14 +1,7 @@
|
||||
import { LinkPreview as HoverCardPrimitive } from "bits-ui";
|
||||
import Content from "./hover-card-content.svelte";
|
||||
import Trigger from "./hover-card-trigger.svelte";
|
||||
import { LinkPreview as HoverCardPrimitive } from 'bits-ui';
|
||||
import Content from './hover-card-content.svelte';
|
||||
import Trigger from './hover-card-trigger.svelte';
|
||||
|
||||
const Root = HoverCardPrimitive.Root;
|
||||
|
||||
export {
|
||||
Root,
|
||||
Content,
|
||||
Trigger,
|
||||
Root as HoverCard,
|
||||
Content as HoverCardContent,
|
||||
Trigger as HoverCardTrigger,
|
||||
};
|
||||
export { Root, Content, Trigger, Root as HoverCard, Content as HoverCardContent, Trigger as HoverCardTrigger };
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
import Root from "./input-group.svelte";
|
||||
import Addon from "./input-group-addon.svelte";
|
||||
import Button from "./input-group-button.svelte";
|
||||
import Input from "./input-group-input.svelte";
|
||||
import Text from "./input-group-text.svelte";
|
||||
import Textarea from "./input-group-textarea.svelte";
|
||||
import Root from './input-group.svelte';
|
||||
import Addon from './input-group-addon.svelte';
|
||||
import Button from './input-group-button.svelte';
|
||||
import Input from './input-group-input.svelte';
|
||||
import Text from './input-group-text.svelte';
|
||||
import Textarea from './input-group-textarea.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
Addon,
|
||||
Button,
|
||||
Input,
|
||||
Text,
|
||||
Textarea,
|
||||
//
|
||||
Root as InputGroup,
|
||||
Addon as InputGroupAddon,
|
||||
Button as InputGroupButton,
|
||||
Input as InputGroupInput,
|
||||
Text as InputGroupText,
|
||||
Textarea as InputGroupTextarea,
|
||||
Root,
|
||||
Addon,
|
||||
Button,
|
||||
Input,
|
||||
Text,
|
||||
Textarea,
|
||||
//
|
||||
Root as InputGroup,
|
||||
Addon as InputGroupAddon,
|
||||
Button as InputGroupButton,
|
||||
Input as InputGroupInput,
|
||||
Text as InputGroupText,
|
||||
Textarea as InputGroupTextarea
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Root from "./input.svelte";
|
||||
import Root from './input.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
//
|
||||
Root as Input,
|
||||
Root,
|
||||
//
|
||||
Root as Input
|
||||
};
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
import Root from "./item.svelte";
|
||||
import Group from "./item-group.svelte";
|
||||
import Separator from "./item-separator.svelte";
|
||||
import Header from "./item-header.svelte";
|
||||
import Footer from "./item-footer.svelte";
|
||||
import Content from "./item-content.svelte";
|
||||
import Title from "./item-title.svelte";
|
||||
import Description from "./item-description.svelte";
|
||||
import Actions from "./item-actions.svelte";
|
||||
import Media from "./item-media.svelte";
|
||||
import Root from './item.svelte';
|
||||
import Group from './item-group.svelte';
|
||||
import Separator from './item-separator.svelte';
|
||||
import Header from './item-header.svelte';
|
||||
import Footer from './item-footer.svelte';
|
||||
import Content from './item-content.svelte';
|
||||
import Title from './item-title.svelte';
|
||||
import Description from './item-description.svelte';
|
||||
import Actions from './item-actions.svelte';
|
||||
import Media from './item-media.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
Group,
|
||||
Separator,
|
||||
Header,
|
||||
Footer,
|
||||
Content,
|
||||
Title,
|
||||
Description,
|
||||
Actions,
|
||||
Media,
|
||||
//
|
||||
Root as Item,
|
||||
Group as ItemGroup,
|
||||
Separator as ItemSeparator,
|
||||
Header as ItemHeader,
|
||||
Footer as ItemFooter,
|
||||
Content as ItemContent,
|
||||
Title as ItemTitle,
|
||||
Description as ItemDescription,
|
||||
Actions as ItemActions,
|
||||
Media as ItemMedia,
|
||||
Root,
|
||||
Group,
|
||||
Separator,
|
||||
Header,
|
||||
Footer,
|
||||
Content,
|
||||
Title,
|
||||
Description,
|
||||
Actions,
|
||||
Media,
|
||||
//
|
||||
Root as Item,
|
||||
Group as ItemGroup,
|
||||
Separator as ItemSeparator,
|
||||
Header as ItemHeader,
|
||||
Footer as ItemFooter,
|
||||
Content as ItemContent,
|
||||
Title as ItemTitle,
|
||||
Description as ItemDescription,
|
||||
Actions as ItemActions,
|
||||
Media as ItemMedia
|
||||
};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import Root from "./kbd.svelte";
|
||||
import Group from "./kbd-group.svelte";
|
||||
import Root from './kbd.svelte';
|
||||
import Group from './kbd-group.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
Group,
|
||||
//
|
||||
Root as Kbd,
|
||||
Group as KbdGroup,
|
||||
Root,
|
||||
Group,
|
||||
//
|
||||
Root as Kbd,
|
||||
Group as KbdGroup
|
||||
};
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
import Root from "./navigation-menu.svelte";
|
||||
import Content from "./navigation-menu-content.svelte";
|
||||
import Indicator from "./navigation-menu-indicator.svelte";
|
||||
import Item from "./navigation-menu-item.svelte";
|
||||
import Link from "./navigation-menu-link.svelte";
|
||||
import List from "./navigation-menu-list.svelte";
|
||||
import Trigger from "./navigation-menu-trigger.svelte";
|
||||
import Viewport from "./navigation-menu-viewport.svelte";
|
||||
import Root from './navigation-menu.svelte';
|
||||
import Content from './navigation-menu-content.svelte';
|
||||
import Indicator from './navigation-menu-indicator.svelte';
|
||||
import Item from './navigation-menu-item.svelte';
|
||||
import Link from './navigation-menu-link.svelte';
|
||||
import List from './navigation-menu-list.svelte';
|
||||
import Trigger from './navigation-menu-trigger.svelte';
|
||||
import Viewport from './navigation-menu-viewport.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
Content,
|
||||
Indicator,
|
||||
Item,
|
||||
Link,
|
||||
List,
|
||||
Trigger,
|
||||
Viewport,
|
||||
//
|
||||
Root as NavigationMenuRoot,
|
||||
Content as NavigationMenuContent,
|
||||
Indicator as NavigationMenuIndicator,
|
||||
Item as NavigationMenuItem,
|
||||
Link as NavigationMenuLink,
|
||||
List as NavigationMenuList,
|
||||
Trigger as NavigationMenuTrigger,
|
||||
Viewport as NavigationMenuViewport,
|
||||
Root,
|
||||
Content,
|
||||
Indicator,
|
||||
Item,
|
||||
Link,
|
||||
List,
|
||||
Trigger,
|
||||
Viewport,
|
||||
//
|
||||
Root as NavigationMenuRoot,
|
||||
Content as NavigationMenuContent,
|
||||
Indicator as NavigationMenuIndicator,
|
||||
Item as NavigationMenuItem,
|
||||
Link as NavigationMenuLink,
|
||||
List as NavigationMenuList,
|
||||
Trigger as NavigationMenuTrigger,
|
||||
Viewport as NavigationMenuViewport
|
||||
};
|
||||
|
||||
@@ -10,12 +10,6 @@
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
bind:this={ref}
|
||||
data-slot="page-action"
|
||||
class={cn('ui:flex ui:items-center ui:gap-2', className)}
|
||||
{...restProps}
|
||||
>
|
||||
<div bind:this={ref} data-slot="page-action" class={cn('ui:flex ui:items-center ui:gap-2', className)} {...restProps}>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -18,4 +18,3 @@
|
||||
>
|
||||
{@render children?.()}
|
||||
</p>
|
||||
|
||||
|
||||
@@ -5,153 +5,150 @@ import * as zxcvbnCommonPackage from '@zxcvbn-ts/language-common';
|
||||
import * as zxcvbnEnPackage from '@zxcvbn-ts/language-en';
|
||||
|
||||
const passwordOptions = {
|
||||
translations: zxcvbnEnPackage.translations,
|
||||
graphs: zxcvbnCommonPackage.adjacencyGraphs,
|
||||
dictionary: {
|
||||
...zxcvbnCommonPackage.dictionary,
|
||||
...zxcvbnEnPackage.dictionary
|
||||
}
|
||||
translations: zxcvbnEnPackage.translations,
|
||||
graphs: zxcvbnCommonPackage.adjacencyGraphs,
|
||||
dictionary: {
|
||||
...zxcvbnCommonPackage.dictionary,
|
||||
...zxcvbnEnPackage.dictionary
|
||||
}
|
||||
};
|
||||
|
||||
zxcvbnOptions.setOptions(passwordOptions);
|
||||
|
||||
type PasswordRootStateProps = WritableBoxedValues<{
|
||||
hidden: boolean;
|
||||
hidden: boolean;
|
||||
}> &
|
||||
ReadableBoxedValues<{
|
||||
minScore: number;
|
||||
}>;
|
||||
ReadableBoxedValues<{
|
||||
minScore: number;
|
||||
}>;
|
||||
|
||||
type PasswordState = {
|
||||
value: string;
|
||||
copyMounted: boolean;
|
||||
toggleMounted: boolean;
|
||||
strengthMounted: boolean;
|
||||
tainted: boolean;
|
||||
value: string;
|
||||
copyMounted: boolean;
|
||||
toggleMounted: boolean;
|
||||
strengthMounted: boolean;
|
||||
tainted: boolean;
|
||||
};
|
||||
|
||||
const defaultPasswordState: PasswordState = {
|
||||
value: '',
|
||||
copyMounted: false,
|
||||
toggleMounted: false,
|
||||
strengthMounted: false,
|
||||
tainted: false
|
||||
value: '',
|
||||
copyMounted: false,
|
||||
toggleMounted: false,
|
||||
strengthMounted: false,
|
||||
tainted: false
|
||||
};
|
||||
|
||||
class PasswordRootState {
|
||||
passwordState = $state(defaultPasswordState);
|
||||
passwordState = $state(defaultPasswordState);
|
||||
|
||||
constructor(readonly opts: PasswordRootStateProps) {}
|
||||
constructor(readonly opts: PasswordRootStateProps) {}
|
||||
|
||||
// only re-run when the password changes
|
||||
strength = $derived.by(() => zxcvbn(this.passwordState.value));
|
||||
// only re-run when the password changes
|
||||
strength = $derived.by(() => zxcvbn(this.passwordState.value));
|
||||
}
|
||||
|
||||
type PasswordInputStateProps = WritableBoxedValues<{
|
||||
value: string;
|
||||
value: string;
|
||||
}> &
|
||||
ReadableBoxedValues<{
|
||||
ref: HTMLInputElement | null;
|
||||
}>;
|
||||
ReadableBoxedValues<{
|
||||
ref: HTMLInputElement | null;
|
||||
}>;
|
||||
|
||||
class PasswordInputState {
|
||||
constructor(
|
||||
readonly root: PasswordRootState,
|
||||
readonly opts: PasswordInputStateProps
|
||||
) {
|
||||
watch(
|
||||
() => this.opts.value.current,
|
||||
() => {
|
||||
if (this.root.passwordState.value !== this.opts.value.current) {
|
||||
this.root.passwordState.tainted = true;
|
||||
this.root.passwordState.value = this.opts.value.current;
|
||||
}
|
||||
}
|
||||
);
|
||||
constructor(
|
||||
readonly root: PasswordRootState,
|
||||
readonly opts: PasswordInputStateProps
|
||||
) {
|
||||
watch(
|
||||
() => this.opts.value.current,
|
||||
() => {
|
||||
if (this.root.passwordState.value !== this.opts.value.current) {
|
||||
this.root.passwordState.tainted = true;
|
||||
this.root.passwordState.value = this.opts.value.current;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
$effect(() => {
|
||||
if (!this.root.passwordState.strengthMounted) return;
|
||||
$effect(() => {
|
||||
if (!this.root.passwordState.strengthMounted) return;
|
||||
|
||||
// if the password is empty, we let the `required` attribute handle the validation
|
||||
if (
|
||||
this.root.passwordState.value !== '' &&
|
||||
this.root.strength.score < this.root.opts.minScore.current
|
||||
) {
|
||||
this.opts.ref.current?.setCustomValidity('Password is too weak');
|
||||
} else {
|
||||
this.opts.ref.current?.setCustomValidity('');
|
||||
}
|
||||
});
|
||||
}
|
||||
// if the password is empty, we let the `required` attribute handle the validation
|
||||
if (this.root.passwordState.value !== '' && this.root.strength.score < this.root.opts.minScore.current) {
|
||||
this.opts.ref.current?.setCustomValidity('Password is too weak');
|
||||
} else {
|
||||
this.opts.ref.current?.setCustomValidity('');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
props = $derived.by(() => ({
|
||||
'aria-invalid':
|
||||
this.root.strength.score < this.root.opts.minScore.current &&
|
||||
this.root.passwordState.tainted &&
|
||||
this.root.passwordState.strengthMounted
|
||||
}));
|
||||
props = $derived.by(() => ({
|
||||
'aria-invalid':
|
||||
this.root.strength.score < this.root.opts.minScore.current &&
|
||||
this.root.passwordState.tainted &&
|
||||
this.root.passwordState.strengthMounted
|
||||
}));
|
||||
}
|
||||
|
||||
class PasswordToggleVisibilityState {
|
||||
constructor(readonly root: PasswordRootState) {
|
||||
this.root.passwordState.toggleMounted = true;
|
||||
constructor(readonly root: PasswordRootState) {
|
||||
this.root.passwordState.toggleMounted = true;
|
||||
|
||||
// this way we go back to the correct padding when toggle is unmounted
|
||||
$effect(() => {
|
||||
return () => {
|
||||
this.root.passwordState.toggleMounted = false;
|
||||
};
|
||||
});
|
||||
}
|
||||
// this way we go back to the correct padding when toggle is unmounted
|
||||
$effect(() => {
|
||||
return () => {
|
||||
this.root.passwordState.toggleMounted = false;
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class PasswordCopyState {
|
||||
constructor(readonly root: PasswordRootState) {
|
||||
this.root.passwordState.copyMounted = true;
|
||||
constructor(readonly root: PasswordRootState) {
|
||||
this.root.passwordState.copyMounted = true;
|
||||
|
||||
// this way we go back to the correct padding when copy is unmounted
|
||||
$effect(() => {
|
||||
return () => {
|
||||
this.root.passwordState.copyMounted = false;
|
||||
};
|
||||
});
|
||||
}
|
||||
// this way we go back to the correct padding when copy is unmounted
|
||||
$effect(() => {
|
||||
return () => {
|
||||
this.root.passwordState.copyMounted = false;
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class PasswordStrengthState {
|
||||
constructor(readonly root: PasswordRootState) {
|
||||
this.root.passwordState.strengthMounted = true;
|
||||
constructor(readonly root: PasswordRootState) {
|
||||
this.root.passwordState.strengthMounted = true;
|
||||
|
||||
$effect(() => {
|
||||
return () => {
|
||||
this.root.passwordState.strengthMounted = false;
|
||||
};
|
||||
});
|
||||
}
|
||||
$effect(() => {
|
||||
return () => {
|
||||
this.root.passwordState.strengthMounted = false;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
get strength() {
|
||||
return this.root.strength;
|
||||
}
|
||||
get strength() {
|
||||
return this.root.strength;
|
||||
}
|
||||
}
|
||||
|
||||
const ctx = new Context<PasswordRootState>('password-root-state');
|
||||
|
||||
export function usePassword(props: PasswordRootStateProps) {
|
||||
return ctx.set(new PasswordRootState(props));
|
||||
return ctx.set(new PasswordRootState(props));
|
||||
}
|
||||
|
||||
export function usePasswordInput(props: PasswordInputStateProps) {
|
||||
return new PasswordInputState(ctx.get(), props);
|
||||
return new PasswordInputState(ctx.get(), props);
|
||||
}
|
||||
|
||||
export function usePasswordToggleVisibility() {
|
||||
return new PasswordToggleVisibilityState(ctx.get());
|
||||
return new PasswordToggleVisibilityState(ctx.get());
|
||||
}
|
||||
|
||||
export function usePasswordCopy() {
|
||||
return new PasswordCopyState(ctx.get());
|
||||
return new PasswordCopyState(ctx.get());
|
||||
}
|
||||
|
||||
export function usePasswordStrength() {
|
||||
return new PasswordStrengthState(ctx.get());
|
||||
return new PasswordStrengthState(ctx.get());
|
||||
}
|
||||
|
||||
@@ -1,47 +1,40 @@
|
||||
import type {
|
||||
WithChildren,
|
||||
WithoutChildren,
|
||||
Meter as MeterPrimitive,
|
||||
Toggle as TogglePrimitive
|
||||
} from 'bits-ui';
|
||||
import type { WithChildren, WithoutChildren, Meter as MeterPrimitive, Toggle as TogglePrimitive } from 'bits-ui';
|
||||
import type { HTMLAttributes, HTMLInputAttributes } from 'svelte/elements';
|
||||
import type { CopyButtonProps } from '../copy-button/types';
|
||||
import type { ZxcvbnResult } from '@zxcvbn-ts/core';
|
||||
|
||||
export type PasswordRootPropsWithoutHTML = WithChildren<{
|
||||
ref?: HTMLDivElement | null;
|
||||
hidden?: boolean;
|
||||
/** The minimum acceptable score for a password. (0-4)
|
||||
*
|
||||
* @default 3
|
||||
*/
|
||||
minScore?: 0 | 1 | 2 | 3 | 4;
|
||||
ref?: HTMLDivElement | null;
|
||||
hidden?: boolean;
|
||||
/** The minimum acceptable score for a password. (0-4)
|
||||
*
|
||||
* @default 3
|
||||
*/
|
||||
minScore?: 0 | 1 | 2 | 3 | 4;
|
||||
}>;
|
||||
|
||||
export type PasswordRootProps = WithoutChildren<HTMLAttributes<HTMLDivElement>> &
|
||||
PasswordRootPropsWithoutHTML;
|
||||
export type PasswordRootProps = WithoutChildren<HTMLAttributes<HTMLDivElement>> & PasswordRootPropsWithoutHTML;
|
||||
|
||||
export type PasswordInputPropsWithoutHTML = WithChildren<{
|
||||
ref?: HTMLInputElement | null;
|
||||
value?: string;
|
||||
ref?: HTMLInputElement | null;
|
||||
value?: string;
|
||||
}>;
|
||||
|
||||
export type PasswordInputProps = Omit<
|
||||
WithoutChildren<HTMLInputAttributes>,
|
||||
'type' | 'files' | 'aria-invalid' | 'value'
|
||||
WithoutChildren<HTMLInputAttributes>,
|
||||
'type' | 'files' | 'aria-invalid' | 'value'
|
||||
> &
|
||||
PasswordInputPropsWithoutHTML;
|
||||
PasswordInputPropsWithoutHTML;
|
||||
|
||||
export type PasswordToggleVisibilityProps = Omit<
|
||||
TogglePrimitive.RootProps,
|
||||
'children' | 'pressed' | 'aria-label' | 'tabindex'
|
||||
TogglePrimitive.RootProps,
|
||||
'children' | 'pressed' | 'aria-label' | 'tabindex'
|
||||
>;
|
||||
|
||||
export type PasswordCopyButtonProps = Omit<CopyButtonProps, 'children' | 'text'>;
|
||||
|
||||
export type PasswordStrengthPropsWithoutHTML = {
|
||||
strength?: ZxcvbnResult;
|
||||
strength?: ZxcvbnResult;
|
||||
};
|
||||
|
||||
export type PasswordStrengthProps = PasswordStrengthPropsWithoutHTML &
|
||||
WithoutChildren<MeterPrimitive.RootProps>;
|
||||
export type PasswordStrengthProps = PasswordStrengthPropsWithoutHTML & WithoutChildren<MeterPrimitive.RootProps>;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Root from "./separator.svelte";
|
||||
import Root from './separator.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
//
|
||||
Root as Separator,
|
||||
Root,
|
||||
//
|
||||
Root as Separator
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Root from "./textarea.svelte";
|
||||
import Root from './textarea.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
//
|
||||
Root as Textarea,
|
||||
Root,
|
||||
//
|
||||
Root as Textarea
|
||||
};
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
import Root from './toggle.svelte';
|
||||
export {
|
||||
toggleVariants,
|
||||
type ToggleSize,
|
||||
type ToggleVariant,
|
||||
type ToggleVariants
|
||||
} from './toggle.svelte';
|
||||
export { toggleVariants, type ToggleSize, type ToggleVariant, type ToggleVariants } from './toggle.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
//
|
||||
Root as Toggle
|
||||
Root,
|
||||
//
|
||||
Root as Toggle
|
||||
};
|
||||
|
||||
@@ -2,4 +2,3 @@ import DomainInput from './domain-input.svelte';
|
||||
|
||||
export { DomainInput };
|
||||
export { DomainInput as default };
|
||||
|
||||
|
||||
@@ -8,12 +8,7 @@
|
||||
tooltipSide?: 'top' | 'bottom' | 'left' | 'right';
|
||||
}
|
||||
|
||||
let {
|
||||
tooltip,
|
||||
tooltipSide = 'top',
|
||||
children,
|
||||
...buttonProps
|
||||
}: Props = $props();
|
||||
let { tooltip, tooltipSide = 'top', children, ...buttonProps }: Props = $props();
|
||||
</script>
|
||||
|
||||
{#if tooltip}
|
||||
@@ -34,4 +29,3 @@
|
||||
{@render children?.()}
|
||||
</Button>
|
||||
{/if}
|
||||
|
||||
|
||||
@@ -2,4 +2,3 @@ import IconButton from './icon-button.svelte';
|
||||
|
||||
export { IconButton };
|
||||
export { IconButton as default };
|
||||
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
import type { CropArea } from 'svelte-easy-crop';
|
||||
|
||||
export const getFileFromUrl = async (url: string, fileName = 'cropped.png'): Promise<File> => {
|
||||
// Fetch the file data from the URL
|
||||
const response = await fetch(url);
|
||||
// Fetch the file data from the URL
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch resource: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch resource: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
|
||||
// Convert the response into a Blob
|
||||
const blob = await response.blob();
|
||||
// Convert the response into a Blob
|
||||
const blob = await response.blob();
|
||||
|
||||
// Create and return a File. You can set a custom type if needed.
|
||||
return new File([blob], fileName, { type: blob.type });
|
||||
// Create and return a File. You can set a custom type if needed.
|
||||
return new File([blob], fileName, { type: blob.type });
|
||||
};
|
||||
|
||||
const createImage = (url: string): Promise<HTMLImageElement> => {
|
||||
return new Promise<HTMLImageElement>((resolve, reject) => {
|
||||
const image = new Image();
|
||||
image.addEventListener('load', () => resolve(image));
|
||||
image.addEventListener('error', (error) => reject(error));
|
||||
image.setAttribute('crossOrigin', 'anonymous'); // needed to avoid cross-origin issues on CodeSandbox
|
||||
image.src = url;
|
||||
});
|
||||
return new Promise<HTMLImageElement>((resolve, reject) => {
|
||||
const image = new Image();
|
||||
image.addEventListener('load', () => resolve(image));
|
||||
image.addEventListener('error', (error) => reject(error));
|
||||
image.setAttribute('crossOrigin', 'anonymous'); // needed to avoid cross-origin issues on CodeSandbox
|
||||
image.src = url;
|
||||
});
|
||||
};
|
||||
|
||||
const getRadianAngle = (degreeValue: number) => {
|
||||
return (degreeValue * Math.PI) / 180;
|
||||
return (degreeValue * Math.PI) / 180;
|
||||
};
|
||||
|
||||
/** Gets the cropped image from the src using the cropped area
|
||||
@@ -36,50 +36,46 @@ const getRadianAngle = (degreeValue: number) => {
|
||||
* @param rotation
|
||||
* @returns
|
||||
*/
|
||||
export const getCroppedImg = async (
|
||||
imageSrc: string,
|
||||
pixelCrop: CropArea,
|
||||
rotation = 0
|
||||
): Promise<string> => {
|
||||
const image = await createImage(imageSrc);
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
export const getCroppedImg = async (imageSrc: string, pixelCrop: CropArea, rotation = 0): Promise<string> => {
|
||||
const image = await createImage(imageSrc);
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
if (!ctx) {
|
||||
throw new Error('Error getting 2d rendering context');
|
||||
}
|
||||
if (!ctx) {
|
||||
throw new Error('Error getting 2d rendering context');
|
||||
}
|
||||
|
||||
const maxSize = Math.max(image.width, image.height);
|
||||
const safeArea = 2 * ((maxSize / 2) * Math.sqrt(2));
|
||||
const maxSize = Math.max(image.width, image.height);
|
||||
const safeArea = 2 * ((maxSize / 2) * Math.sqrt(2));
|
||||
|
||||
// set each dimensions to double largest dimension to allow for a safe area for the
|
||||
// image to rotate in without being clipped by canvas context
|
||||
canvas.width = safeArea;
|
||||
canvas.height = safeArea;
|
||||
// set each dimensions to double largest dimension to allow for a safe area for the
|
||||
// image to rotate in without being clipped by canvas context
|
||||
canvas.width = safeArea;
|
||||
canvas.height = safeArea;
|
||||
|
||||
// translate canvas context to a central location on image to allow rotating around the center.
|
||||
ctx.translate(safeArea / 2, safeArea / 2);
|
||||
ctx.rotate(getRadianAngle(rotation));
|
||||
ctx.translate(-safeArea / 2, -safeArea / 2);
|
||||
// translate canvas context to a central location on image to allow rotating around the center.
|
||||
ctx.translate(safeArea / 2, safeArea / 2);
|
||||
ctx.rotate(getRadianAngle(rotation));
|
||||
ctx.translate(-safeArea / 2, -safeArea / 2);
|
||||
|
||||
// draw rotated image and store data.
|
||||
ctx.drawImage(image, safeArea / 2 - image.width * 0.5, safeArea / 2 - image.height * 0.5);
|
||||
const data = ctx.getImageData(0, 0, safeArea, safeArea);
|
||||
// draw rotated image and store data.
|
||||
ctx.drawImage(image, safeArea / 2 - image.width * 0.5, safeArea / 2 - image.height * 0.5);
|
||||
const data = ctx.getImageData(0, 0, safeArea, safeArea);
|
||||
|
||||
// set canvas width to final desired crop size - this will clear existing context
|
||||
canvas.width = pixelCrop.width;
|
||||
canvas.height = pixelCrop.height;
|
||||
// set canvas width to final desired crop size - this will clear existing context
|
||||
canvas.width = pixelCrop.width;
|
||||
canvas.height = pixelCrop.height;
|
||||
|
||||
// paste generated rotate image with correct offsets for x,y crop values.
|
||||
ctx.putImageData(
|
||||
data,
|
||||
Math.round(0 - safeArea / 2 + image.width * 0.5 - pixelCrop.x),
|
||||
Math.round(0 - safeArea / 2 + image.height * 0.5 - pixelCrop.y)
|
||||
);
|
||||
// paste generated rotate image with correct offsets for x,y crop values.
|
||||
ctx.putImageData(
|
||||
data,
|
||||
Math.round(0 - safeArea / 2 + image.width * 0.5 - pixelCrop.x),
|
||||
Math.round(0 - safeArea / 2 + image.height * 0.5 - pixelCrop.y)
|
||||
);
|
||||
|
||||
return new Promise((resolve) => {
|
||||
canvas.toBlob((file) => {
|
||||
resolve(URL.createObjectURL(file!));
|
||||
}, 'image/png');
|
||||
});
|
||||
return new Promise((resolve) => {
|
||||
canvas.toBlob((file) => {
|
||||
resolve(URL.createObjectURL(file!));
|
||||
}, 'image/png');
|
||||
});
|
||||
};
|
||||
|
||||
@@ -2,4 +2,3 @@ import InputField from './input-field.svelte';
|
||||
|
||||
export { InputField };
|
||||
export { InputField as default };
|
||||
|
||||
|
||||
@@ -2,4 +2,3 @@ import Password from './password.svelte';
|
||||
|
||||
export { Password };
|
||||
export { Password as default };
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
bind:value
|
||||
{disabled}
|
||||
aria-invalid={ariaInvalid}
|
||||
autocomplete={autocomplete}
|
||||
{autocomplete}
|
||||
{...restProps}
|
||||
/>
|
||||
<InputGroup.Addon align="inline-end">
|
||||
@@ -77,4 +77,3 @@
|
||||
</Tooltip.Provider>
|
||||
</InputGroup.Addon>
|
||||
</InputGroup.Root>
|
||||
|
||||
|
||||
@@ -2,4 +2,3 @@ import Search from './search.svelte';
|
||||
|
||||
export { Search };
|
||||
export { Search as default };
|
||||
|
||||
|
||||
@@ -4,13 +4,13 @@ import List from './underline-tabs-list.svelte';
|
||||
import Trigger from './underline-tabs-trigger.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
Content,
|
||||
List,
|
||||
Trigger,
|
||||
//
|
||||
Root as UnderlineTabs,
|
||||
Content as UnderlineTabsContent,
|
||||
List as UnderlineTabsList,
|
||||
Trigger as UnderlineTabsTrigger
|
||||
Root,
|
||||
Content,
|
||||
List,
|
||||
Trigger,
|
||||
//
|
||||
Root as UnderlineTabs,
|
||||
Content as UnderlineTabsContent,
|
||||
List as UnderlineTabsList,
|
||||
Trigger as UnderlineTabsTrigger
|
||||
};
|
||||
|
||||
@@ -4,85 +4,85 @@ import type { Tabs as TabsPrimitive } from 'bits-ui';
|
||||
import { crossfade } from 'svelte/transition';
|
||||
|
||||
export const [send, receive] = crossfade({
|
||||
duration: (d) => Math.sqrt(d * 200)
|
||||
duration: (d) => Math.sqrt(d * 200)
|
||||
});
|
||||
|
||||
type UnderlineTabsRootProps = WritableBoxedValues<{
|
||||
value: string;
|
||||
value: string;
|
||||
}> &
|
||||
ReadableBoxedValues<{
|
||||
id: string;
|
||||
}>;
|
||||
ReadableBoxedValues<{
|
||||
id: string;
|
||||
}>;
|
||||
|
||||
class UnderlineTabsRootState {
|
||||
hoveredTab = $state<string | null>(null);
|
||||
isHovered = $state(false);
|
||||
constructor(readonly opts: UnderlineTabsRootProps) {
|
||||
this.hoveredTab = this.opts.value.current;
|
||||
}
|
||||
hoveredTab = $state<string | null>(null);
|
||||
isHovered = $state(false);
|
||||
constructor(readonly opts: UnderlineTabsRootProps) {
|
||||
this.hoveredTab = this.opts.value.current;
|
||||
}
|
||||
}
|
||||
|
||||
type UnderlineTabsTriggerProps = ReadableBoxedValues<{
|
||||
value: string;
|
||||
onmouseenter: TabsPrimitive.TriggerProps['onmouseenter'];
|
||||
onmouseleave: TabsPrimitive.TriggerProps['onmouseleave'];
|
||||
onfocus: TabsPrimitive.TriggerProps['onfocus'];
|
||||
onblur: TabsPrimitive.TriggerProps['onblur'];
|
||||
value: string;
|
||||
onmouseenter: TabsPrimitive.TriggerProps['onmouseenter'];
|
||||
onmouseleave: TabsPrimitive.TriggerProps['onmouseleave'];
|
||||
onfocus: TabsPrimitive.TriggerProps['onfocus'];
|
||||
onblur: TabsPrimitive.TriggerProps['onblur'];
|
||||
}>;
|
||||
|
||||
class UnderlineTabsTriggerState {
|
||||
constructor(
|
||||
readonly opts: UnderlineTabsTriggerProps,
|
||||
readonly rootState: UnderlineTabsRootState
|
||||
) {}
|
||||
constructor(
|
||||
readonly opts: UnderlineTabsTriggerProps,
|
||||
readonly rootState: UnderlineTabsRootState
|
||||
) {}
|
||||
|
||||
handleFocus() {
|
||||
this.rootState.isHovered = true;
|
||||
this.rootState.hoveredTab = this.opts.value.current;
|
||||
}
|
||||
handleFocus() {
|
||||
this.rootState.isHovered = true;
|
||||
this.rootState.hoveredTab = this.opts.value.current;
|
||||
}
|
||||
|
||||
handleBlur() {
|
||||
if (this.rootState.hoveredTab === this.opts.value.current) {
|
||||
this.rootState.isHovered = false;
|
||||
}
|
||||
}
|
||||
handleBlur() {
|
||||
if (this.rootState.hoveredTab === this.opts.value.current) {
|
||||
this.rootState.isHovered = false;
|
||||
}
|
||||
}
|
||||
|
||||
onmouseenter(e: Parameters<NonNullable<TabsPrimitive.TriggerProps['onmouseenter']>>[0]) {
|
||||
this.handleFocus();
|
||||
this.opts.onmouseenter.current?.(e);
|
||||
}
|
||||
onmouseenter(e: Parameters<NonNullable<TabsPrimitive.TriggerProps['onmouseenter']>>[0]) {
|
||||
this.handleFocus();
|
||||
this.opts.onmouseenter.current?.(e);
|
||||
}
|
||||
|
||||
onmouseleave(e: Parameters<NonNullable<TabsPrimitive.TriggerProps['onmouseleave']>>[0]) {
|
||||
this.handleBlur();
|
||||
this.opts.onmouseleave.current?.(e);
|
||||
}
|
||||
onmouseleave(e: Parameters<NonNullable<TabsPrimitive.TriggerProps['onmouseleave']>>[0]) {
|
||||
this.handleBlur();
|
||||
this.opts.onmouseleave.current?.(e);
|
||||
}
|
||||
|
||||
onfocus(e: Parameters<NonNullable<TabsPrimitive.TriggerProps['onfocus']>>[0]) {
|
||||
this.handleFocus();
|
||||
this.opts.onfocus.current?.(e);
|
||||
}
|
||||
onfocus(e: Parameters<NonNullable<TabsPrimitive.TriggerProps['onfocus']>>[0]) {
|
||||
this.handleFocus();
|
||||
this.opts.onfocus.current?.(e);
|
||||
}
|
||||
|
||||
onblur(e: Parameters<NonNullable<TabsPrimitive.TriggerProps['onblur']>>[0]) {
|
||||
this.handleBlur();
|
||||
onblur(e: Parameters<NonNullable<TabsPrimitive.TriggerProps['onblur']>>[0]) {
|
||||
this.handleBlur();
|
||||
|
||||
this.opts.onblur.current?.(e);
|
||||
}
|
||||
this.opts.onblur.current?.(e);
|
||||
}
|
||||
|
||||
props = $derived.by(() => ({
|
||||
value: this.opts.value.current,
|
||||
onmouseenter: this.onmouseenter.bind(this),
|
||||
onmouseleave: this.onmouseleave.bind(this),
|
||||
onfocus: this.onfocus.bind(this),
|
||||
onblur: this.onblur.bind(this)
|
||||
}));
|
||||
props = $derived.by(() => ({
|
||||
value: this.opts.value.current,
|
||||
onmouseenter: this.onmouseenter.bind(this),
|
||||
onmouseleave: this.onmouseleave.bind(this),
|
||||
onfocus: this.onfocus.bind(this),
|
||||
onblur: this.onblur.bind(this)
|
||||
}));
|
||||
}
|
||||
|
||||
const ctx = new Context<UnderlineTabsRootState>('underline-tabs-root-ctx');
|
||||
|
||||
export function useUnderlineTabs(opts: UnderlineTabsRootProps) {
|
||||
return ctx.set(new UnderlineTabsRootState(opts));
|
||||
return ctx.set(new UnderlineTabsRootState(opts));
|
||||
}
|
||||
|
||||
export function useUnderlineTabsTrigger(opts: UnderlineTabsTriggerProps) {
|
||||
return new UnderlineTabsTriggerState(opts, ctx.get());
|
||||
return new UnderlineTabsTriggerState(opts, ctx.get());
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
type Options = {
|
||||
/** The time before the copied status is reset. */
|
||||
delay: number;
|
||||
/** The time before the copied status is reset. */
|
||||
delay: number;
|
||||
};
|
||||
|
||||
/** Use this hook to copy text to the clipboard and show a copied state.
|
||||
@@ -26,58 +26,58 @@ type Options = {
|
||||
*
|
||||
*/
|
||||
export class UseClipboard {
|
||||
#copiedStatus = $state<'success' | 'failure'>();
|
||||
private delay: number;
|
||||
private timeout: ReturnType<typeof setTimeout> | undefined = undefined;
|
||||
#copiedStatus = $state<'success' | 'failure'>();
|
||||
private delay: number;
|
||||
private timeout: ReturnType<typeof setTimeout> | undefined = undefined;
|
||||
|
||||
constructor({ delay = 500 }: Partial<Options> = {}) {
|
||||
this.delay = delay;
|
||||
}
|
||||
constructor({ delay = 500 }: Partial<Options> = {}) {
|
||||
this.delay = delay;
|
||||
}
|
||||
|
||||
/** Copies the given text to the users clipboard.
|
||||
*
|
||||
* ## Usage
|
||||
* ```ts
|
||||
* clipboard.copy('Hello, World!');
|
||||
* ```
|
||||
*
|
||||
* @param text
|
||||
* @returns
|
||||
*/
|
||||
async copy(text: string) {
|
||||
if (this.timeout) {
|
||||
this.#copiedStatus = undefined;
|
||||
clearTimeout(this.timeout);
|
||||
}
|
||||
/** Copies the given text to the users clipboard.
|
||||
*
|
||||
* ## Usage
|
||||
* ```ts
|
||||
* clipboard.copy('Hello, World!');
|
||||
* ```
|
||||
*
|
||||
* @param text
|
||||
* @returns
|
||||
*/
|
||||
async copy(text: string) {
|
||||
if (this.timeout) {
|
||||
this.#copiedStatus = undefined;
|
||||
clearTimeout(this.timeout);
|
||||
}
|
||||
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
|
||||
this.#copiedStatus = 'success';
|
||||
this.#copiedStatus = 'success';
|
||||
|
||||
this.timeout = setTimeout(() => {
|
||||
this.#copiedStatus = undefined;
|
||||
}, this.delay);
|
||||
} catch {
|
||||
// an error can occur when not in the browser or if the user hasn't given clipboard access
|
||||
this.#copiedStatus = 'failure';
|
||||
this.timeout = setTimeout(() => {
|
||||
this.#copiedStatus = undefined;
|
||||
}, this.delay);
|
||||
} catch {
|
||||
// an error can occur when not in the browser or if the user hasn't given clipboard access
|
||||
this.#copiedStatus = 'failure';
|
||||
|
||||
this.timeout = setTimeout(() => {
|
||||
this.#copiedStatus = undefined;
|
||||
}, this.delay);
|
||||
}
|
||||
this.timeout = setTimeout(() => {
|
||||
this.#copiedStatus = undefined;
|
||||
}, this.delay);
|
||||
}
|
||||
|
||||
return this.#copiedStatus;
|
||||
}
|
||||
return this.#copiedStatus;
|
||||
}
|
||||
|
||||
/** true when the user has just copied to the clipboard. */
|
||||
get copied() {
|
||||
return this.#copiedStatus === 'success';
|
||||
}
|
||||
/** true when the user has just copied to the clipboard. */
|
||||
get copied() {
|
||||
return this.#copiedStatus === 'success';
|
||||
}
|
||||
|
||||
/** Indicates whether a copy has occurred
|
||||
* and gives a status of either `success` or `failure`. */
|
||||
get status() {
|
||||
return this.#copiedStatus;
|
||||
}
|
||||
/** Indicates whether a copy has occurred
|
||||
* and gives a status of either `success` or `failure`. */
|
||||
get status() {
|
||||
return this.#copiedStatus;
|
||||
}
|
||||
}
|
||||
|
||||
+3807
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user