-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
670 additions
and
291 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
170 changes: 67 additions & 103 deletions
170
packages/components/dismissable/dist/Dismissable.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,124 +1,88 @@ | ||
<script>import { onMount } from 'svelte'; | ||
import { navigating } from '$app/stores'; | ||
import { DEV } from 'esm-env'; | ||
/** | ||
* Used to identify the content being dismissed in browser storage. | ||
*/ | ||
export let key; | ||
/** | ||
* The delay in ms before the content shows up. | ||
*/ | ||
export let timeout = 0; | ||
/** | ||
* The date of the last update of the content. | ||
*/ | ||
export let lastUpdatedAt = null; | ||
/** | ||
* The maximum age of the dismissal in seconds. | ||
* The content will show up again after this time has past. | ||
*/ | ||
export let maxAge = 0; | ||
/** | ||
* Wether to use `sessionStorage` or `localStorage`. | ||
*/ | ||
export let browserStorage = 'local'; | ||
/** | ||
* Wether to close the content when navigating to another page. | ||
*/ | ||
export let closeOnNav = false; | ||
<script lang="ts">import { onMount } from "svelte"; | ||
import { navigating } from "$app/stores"; | ||
import { DEV } from "esm-env"; | ||
let { | ||
key, | ||
timeout = 0, | ||
lastUpdatedAt = null, | ||
maxAge = 0, | ||
browserStorage = "local", | ||
closeOnNav = false, | ||
children | ||
} = $props(); | ||
const storageKey = `${key}-dismissed`; | ||
const storage = () => window[`${browserStorage}Storage`]; | ||
const FOREVER = 'true'; | ||
let open = false; | ||
$: dismissed = false; | ||
const FOREVER = "true"; | ||
let open = $state(false); | ||
let dismissed = $state(false); | ||
const getExpiryDate = () => { | ||
if (maxAge) { | ||
return new Date(Date.now() - maxAge * 1000); | ||
} | ||
return null; | ||
if (maxAge) { | ||
return new Date(Date.now() - maxAge * 1e3); | ||
} | ||
return null; | ||
}; | ||
const stringToValidDate = (value) => { | ||
if (!value) { | ||
return null; | ||
} | ||
const date = new Date(value); | ||
if (date && isNaN(date.getTime())) { | ||
if (DEV) { | ||
console.warn(`Invalid date: ${value}`); | ||
} | ||
return null; | ||
if (!value) { | ||
return null; | ||
} | ||
const date = new Date(value); | ||
if (date && isNaN(date.getTime())) { | ||
if (DEV) { | ||
console.warn(`Invalid date: ${value}`); | ||
} | ||
return date; | ||
return null; | ||
} | ||
return date; | ||
}; | ||
const checkIfDismissed = (value) => { | ||
// Not in storage, so not dismissed | ||
if (!value) { | ||
return false; | ||
} | ||
// The default value 'true' means it has been dismissed 'forever' | ||
if (value === FOREVER) { | ||
return true; | ||
} | ||
// If we have a value other than 'true', it must be the last dismiss date | ||
const lastDismissed = stringToValidDate(value); | ||
// If it was dismissed before the last update, it is not dismissed anymore | ||
if (lastDismissed && lastUpdatedAt && lastDismissed.getTime() < lastUpdatedAt.getTime()) { | ||
return false; | ||
} | ||
// We check if the dismissal is expired | ||
const expiryDate = getExpiryDate(); | ||
const isExpired = lastDismissed && expiryDate && lastDismissed.getTime() < expiryDate.getTime(); | ||
if (isExpired) { | ||
return false; | ||
} | ||
// If we got here, it is dismissed | ||
if (!value) { | ||
return false; | ||
} | ||
if (value === FOREVER) { | ||
return true; | ||
} | ||
const lastDismissed = stringToValidDate(value); | ||
if (lastDismissed && lastUpdatedAt && lastDismissed.getTime() < lastUpdatedAt.getTime()) { | ||
return false; | ||
} | ||
const expiryDate = getExpiryDate(); | ||
const isExpired = lastDismissed && expiryDate && lastDismissed.getTime() < expiryDate.getTime(); | ||
if (isExpired) { | ||
return false; | ||
} | ||
return true; | ||
}; | ||
/** | ||
* A function to close the popup without persistance. | ||
*/ | ||
export const close = () => { | ||
open = false; | ||
open = false; | ||
}; | ||
/** | ||
* A function to dismiss the popup for the provided maxAge. | ||
*/ | ||
export const dismiss = () => { | ||
close(); | ||
// If an expiry date is needed, the value is the date. Otherwise, the value remains 'true' | ||
const storageValue = maxAge || lastUpdatedAt ? new Date().toISOString() : FOREVER; | ||
storage().setItem(storageKey, storageValue); | ||
close(); | ||
const storageValue = maxAge || lastUpdatedAt ? (/* @__PURE__ */ new Date()).toISOString() : FOREVER; | ||
storage().setItem(storageKey, storageValue); | ||
}; | ||
/** | ||
* A function to check if the popup is dismissed. | ||
*/ | ||
export const isDismissed = () => dismissed; | ||
onMount(() => { | ||
// Check if previously dismissed | ||
dismissed = checkIfDismissed(storage().getItem(storageKey)); | ||
if (dismissed) { | ||
return; | ||
} | ||
// Clear the storage | ||
storage().removeItem(storageKey); | ||
// Show after timeout | ||
const timer = setTimeout(() => (open = true), timeout); | ||
// Maybe close on navigation | ||
let navUnsubscribe; | ||
if (closeOnNav) { | ||
navUnsubscribe = navigating.subscribe((nav) => { | ||
if (nav) { | ||
open = false; | ||
} | ||
}); | ||
} | ||
return () => { | ||
clearTimeout(timer); | ||
navUnsubscribe?.(); | ||
}; | ||
dismissed = checkIfDismissed(storage().getItem(storageKey)); | ||
if (dismissed) { | ||
return; | ||
} | ||
storage().removeItem(storageKey); | ||
const timer = setTimeout(() => open = true, timeout); | ||
let navUnsubscribe; | ||
if (closeOnNav) { | ||
navUnsubscribe = navigating.subscribe((nav) => { | ||
if (nav) { | ||
open = false; | ||
} | ||
}); | ||
} | ||
return () => { | ||
clearTimeout(timer); | ||
navUnsubscribe?.(); | ||
}; | ||
}); | ||
</script> | ||
|
||
{#if open} | ||
<slot {close} {dismiss} {dismissed} /> | ||
{@render children?.({ close, dismiss, dismissed })} | ||
{/if} |
97 changes: 45 additions & 52 deletions
97
packages/components/dismissable/dist/Dismissable.svelte.d.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,53 +1,46 @@ | ||
import { SvelteComponent } from "svelte"; | ||
/// <reference types="svelte" /> | ||
import type { Maybe } from '@288-toolkit/types'; | ||
declare const __propDef: { | ||
props: { | ||
/** | ||
* Used to identify the content being dismissed in browser storage. | ||
*/ key: string; | ||
/** | ||
* The delay in ms before the content shows up. | ||
*/ timeout?: number | undefined; | ||
/** | ||
* The date of the last update of the content. | ||
*/ lastUpdatedAt?: Maybe<Date> | undefined; | ||
/** | ||
* The maximum age of the dismissal in seconds. | ||
* The content will show up again after this time has past. | ||
*/ maxAge?: number | undefined; | ||
/** | ||
* Wether to use `sessionStorage` or `localStorage`. | ||
*/ browserStorage?: "session" | "local" | undefined; | ||
/** | ||
* Wether to close the content when navigating to another page. | ||
*/ closeOnNav?: boolean | undefined; | ||
/** | ||
* A function to close the popup without persistance. | ||
*/ close?: (() => void) | undefined; | ||
/** | ||
* A function to dismiss the popup for the provided maxAge. | ||
*/ dismiss?: (() => void) | undefined; | ||
/** | ||
* A function to check if the popup is dismissed. | ||
*/ isDismissed?: (() => boolean) | undefined; | ||
}; | ||
events: { | ||
[evt: string]: CustomEvent<any>; | ||
}; | ||
slots: { | ||
default: { | ||
close: () => void; | ||
dismiss: () => void; | ||
dismissed: boolean; | ||
}; | ||
}; | ||
}; | ||
export type DismissableProps = typeof __propDef.props; | ||
export type DismissableEvents = typeof __propDef.events; | ||
export type DismissableSlots = typeof __propDef.slots; | ||
export default class Dismissable extends SvelteComponent<DismissableProps, DismissableEvents, DismissableSlots> { | ||
get close(): () => void; | ||
get dismiss(): () => void; | ||
get isDismissed(): () => boolean; | ||
} | ||
export {}; | ||
declare const Dismissable: import("svelte").Component<{ | ||
/** | ||
* Used to identify the content being dismissed in browser storage. | ||
*/ | ||
key: string; | ||
/** | ||
* The delay in ms before the content shows up. | ||
*/ | ||
timeout?: number | undefined; | ||
/** | ||
* The date of the last update of the content. | ||
*/ | ||
lastUpdatedAt?: Maybe<Date> | undefined; | ||
/** | ||
* The maximum age of the dismissal in seconds. | ||
* The content will show up again after this time has past. | ||
*/ | ||
maxAge?: number | undefined; | ||
/** | ||
* Wether to use `sessionStorage` or `localStorage`. | ||
*/ | ||
browserStorage?: "session" | "local" | undefined; | ||
/** | ||
* Wether to close the content when navigating to another page. | ||
*/ | ||
closeOnNav?: boolean | undefined; | ||
children?: import("svelte").Snippet<[{ | ||
close: () => void; | ||
dismiss: () => void; | ||
dismissed: boolean; | ||
}]> | undefined; | ||
}, { | ||
/** | ||
* A function to close the popup without persistance. | ||
*/ close: () => void; | ||
/** | ||
* A function to dismiss the popup for the provided maxAge. | ||
*/ dismiss: () => void; | ||
/** | ||
* A function to check if the popup is dismissed. | ||
*/ isDismissed: () => boolean; | ||
}, "">; | ||
type Dismissable = ReturnType<typeof Dismissable>; | ||
export default Dismissable; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<!doctype html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="utf-8" /> | ||
<link rel="icon" href="%sveltekit.assets%/favicon.png" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1" /> | ||
%sveltekit.head% | ||
</head> | ||
<body data-sveltekit-preload-data="hover"> | ||
<div style="display: contents">%sveltekit.body%</div> | ||
</body> | ||
</html> |
Oops, something went wrong.