From 27e568745aff91f521620d2e0f17d6e1ed86732e Mon Sep 17 00:00:00 2001 From: Esteban Borai Date: Thu, 27 Apr 2023 01:44:42 -0400 Subject: [PATCH 1/2] feat: provide notifications implementation and example --- .eslintrc.cjs | 58 ++++----- .prettierrc | 14 +-- README.md | 1 - package.json | 108 +++++++++-------- src/app.d.ts | 12 +- src/app.html | 18 +-- src/index.test.ts | 6 +- src/lib/components/NotificationList.svelte | 26 +++++ src/lib/index.js | 1 - src/lib/index.ts | 9 ++ src/lib/stores/notifications.ts | 129 +++++++++++++++++++++ src/routes/+page.svelte | 19 ++- svelte.config.js | 18 +-- tsconfig.json | 24 ++-- vite.config.ts | 8 +- 15 files changed, 317 insertions(+), 134 deletions(-) create mode 100644 src/lib/components/NotificationList.svelte delete mode 100644 src/lib/index.js create mode 100644 src/lib/index.ts create mode 100644 src/lib/stores/notifications.ts diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 11b2844..e5db2f8 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -1,31 +1,31 @@ module.exports = { - root: true, - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:svelte/recommended', - 'prettier' - ], - parser: '@typescript-eslint/parser', - plugins: ['@typescript-eslint'], - ignorePatterns: ['*.cjs'], - parserOptions: { - sourceType: 'module', - ecmaVersion: 2020, - extraFileExtensions: ['.svelte'] - }, - env: { - browser: true, - es2017: true, - node: true - }, - overrides: [ - { - files: ['*.svelte'], - parser: 'svelte-eslint-parser', - parserOptions: { - parser: '@typescript-eslint/parser' - } - } - ] + root: true, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:svelte/recommended', + 'prettier' + ], + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint'], + ignorePatterns: ['*.cjs'], + parserOptions: { + sourceType: 'module', + ecmaVersion: 2020, + extraFileExtensions: ['.svelte'] + }, + env: { + browser: true, + es2017: true, + node: true + }, + overrides: [ + { + files: ['*.svelte'], + parser: 'svelte-eslint-parser', + parserOptions: { + parser: '@typescript-eslint/parser' + } + } + ] }; diff --git a/.prettierrc b/.prettierrc index a77fdde..52753cc 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,9 +1,9 @@ { - "useTabs": true, - "singleQuote": true, - "trailingComma": "none", - "printWidth": 100, - "plugins": ["prettier-plugin-svelte"], - "pluginSearchDirs": ["."], - "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] + "useTabs": false, + "singleQuote": true, + "trailingComma": "none", + "printWidth": 80, + "plugins": ["prettier-plugin-svelte"], + "pluginSearchDirs": ["."], + "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] } diff --git a/README.md b/README.md index b0bd999..fdba330 100644 --- a/README.md +++ b/README.md @@ -26,4 +26,3 @@ npm run dev ``` ## Contributing - diff --git a/package.json b/package.json index 1ee6143..d6de3c9 100644 --- a/package.json +++ b/package.json @@ -1,52 +1,60 @@ { - "name": "@whizzes/svelte-notifications", - "version": "0.0.1", - "scripts": { - "dev": "vite dev", - "build": "vite build && npm run package", - "preview": "vite preview", - "package": "svelte-kit sync && svelte-package && publint", - "prepublishOnly": "npm run package", - "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", - "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", - "test:unit": "vitest", - "lint": "prettier --plugin-search-dir . --check . && eslint .", - "format": "prettier --plugin-search-dir . --write ." - }, - "exports": { - ".": { - "types": "./dist/index.d.ts", - "svelte": "./dist/index.js" - } - }, - "files": [ - "dist", - "!dist/**/*.test.*", - "!dist/**/*.spec.*" - ], - "peerDependencies": { - "svelte": "^3.54.0" - }, - "devDependencies": { - "@sveltejs/adapter-auto": "^2.0.0", - "@sveltejs/kit": "^1.5.0", - "@sveltejs/package": "^2.0.0", - "@typescript-eslint/eslint-plugin": "^5.45.0", - "@typescript-eslint/parser": "^5.45.0", - "eslint": "^8.28.0", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-svelte": "^2.26.0", - "prettier": "^2.8.0", - "prettier-plugin-svelte": "^2.8.1", - "publint": "^0.1.9", - "svelte": "^3.54.0", - "svelte-check": "^3.0.1", - "tslib": "^2.4.1", - "typescript": "^5.0.0", - "vite": "^4.3.0", - "vitest": "^0.25.3" - }, - "svelte": "./dist/index.js", - "types": "./dist/index.d.ts", - "type": "module" + "name": "@whizzes/svelte-notifications", + "version": "0.0.1", + "scripts": { + "dev": "vite dev", + "build": "vite build && npm run package", + "preview": "vite preview", + "package": "svelte-kit sync && svelte-package && publint", + "prepublishOnly": "npm run package", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", + "test:unit": "vitest", + "lint": "prettier --plugin-search-dir . --check . && eslint .", + "format": "prettier --plugin-search-dir . --write ." + }, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "svelte": "./dist/index.js" + } + }, + "files": [ + "dist", + "!dist/**/*.test.*", + "!dist/**/*.spec.*" + ], + "peerDependencies": { + "svelte": "^3.54.0" + }, + "devDependencies": { + "@sveltejs/adapter-auto": "^2.0.0", + "@sveltejs/kit": "^1.5.0", + "@sveltejs/package": "^2.0.0", + "@typescript-eslint/eslint-plugin": "^5.45.0", + "@typescript-eslint/parser": "^5.45.0", + "eslint": "^8.28.0", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-svelte": "^2.26.0", + "prettier": "^2.8.0", + "prettier-plugin-svelte": "^2.8.1", + "publint": "^0.1.9", + "svelte": "^3.54.0", + "svelte-check": "^3.0.1", + "tslib": "^2.4.1", + "typescript": "^5.0.0", + "vite": "^4.3.0", + "vitest": "^0.25.3" + }, + "svelte": "./dist/index.js", + "types": "./dist/index.d.ts", + "type": "module", + "homepage": "https://github.com/whizzes/svelte-notifications", + "bugs": { + "url": "https://github.com/whizzes/svelte-notifications/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/whizzes/svelte-notifications.git" + } } diff --git a/src/app.d.ts b/src/app.d.ts index f59b884..899c7e8 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -1,12 +1,12 @@ // See https://kit.svelte.dev/docs/types#app // for information about these interfaces declare global { - namespace App { - // interface Error {} - // interface Locals {} - // interface PageData {} - // interface Platform {} - } + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface Platform {} + } } export {}; diff --git a/src/app.html b/src/app.html index 2f83674..5c77cd7 100644 --- a/src/app.html +++ b/src/app.html @@ -1,12 +1,12 @@ - - - - - %sveltekit.head% - - -
%sveltekit.body%
- + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ diff --git a/src/index.test.ts b/src/index.test.ts index e07cbbd..964d287 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect } from 'vitest'; describe('sum test', () => { - it('adds 1 + 2 to equal 3', () => { - expect(1 + 2).toBe(3); - }); + it('adds 1 + 2 to equal 3', () => { + expect(1 + 2).toBe(3); + }); }); diff --git a/src/lib/components/NotificationList.svelte b/src/lib/components/NotificationList.svelte new file mode 100644 index 0000000..a2508bf --- /dev/null +++ b/src/lib/components/NotificationList.svelte @@ -0,0 +1,26 @@ + + + diff --git a/src/lib/index.js b/src/lib/index.js deleted file mode 100644 index 47d3c46..0000000 --- a/src/lib/index.js +++ /dev/null @@ -1 +0,0 @@ -// Reexport your entry components here diff --git a/src/lib/index.ts b/src/lib/index.ts new file mode 100644 index 0000000..b226cdb --- /dev/null +++ b/src/lib/index.ts @@ -0,0 +1,9 @@ +export { default as NotificationList } from '$lib/components/NotificationList.svelte'; +export { default as notifications } from '$lib/stores/notifications'; + +export type { + Notification, + NotificationStoreMethods, + NotificationStore, + NotificationKind +} from '$lib/stores/notifications'; diff --git a/src/lib/stores/notifications.ts b/src/lib/stores/notifications.ts new file mode 100644 index 0000000..791eae2 --- /dev/null +++ b/src/lib/stores/notifications.ts @@ -0,0 +1,129 @@ +import { get, writable } from 'svelte/store'; + +import type { Readable } from 'svelte/store'; + +export type NotificationStoreMethods = { + sorted(): Notification[]; + close(id: string): void; + notifySuccess(message: string, title?: string): void; + notifyFailure(message: string, title?: string): void; + notifyWarning(message: string, title?: string): void; +}; + +export type NotificationStore = { + notifications: Record; +}; + +export enum NotificationKind { + Failure, + Success, + Warning +} + +export type Notification = { + id: string; + time: number; + kind: NotificationKind; + title?: string; + message: string; +}; + +const DEFAULT_TIMEOUT_SECONDS = 5; + +export function createNotificationStore() { + const timeouts: Record = {}; + const notificationStore = writable({ + notifications: {} + }); + const { subscribe, update } = notificationStore; + + const newId = () => Math.random().toString(16).slice(2); + + const newNotification = ( + kind: NotificationKind, + message: string, + title?: string + ): Notification => ({ + id: newId(), + time: Math.floor(Date.now() / 1000), + kind, + title, + message + }); + + const close = (id: string): void => { + update((current) => { + const notifications = { ...current.notifications }; + + delete notifications[id]; + return { + ...current, + notifications + }; + }); + }; + + const closeWithTimeout = (id: string): void => { + clearTimeout(timeouts[id]); + close(id); + }; + + const append = (notification: Notification): void => { + update((current) => { + const notifications = { ...current.notifications }; + + notifications[notification.id] = notification; + return { + ...current, + notifications + }; + }); + }; + + const appendWithTimeout = (notification: Notification): void => { + append(notification); + + const timeout = setTimeout(() => { + closeWithTimeout(notification.id); + }, DEFAULT_TIMEOUT_SECONDS * 1000); + + timeouts[notification.id] = timeout; + }; + + const notifySuccess = (message: string, title?: string) => + appendWithTimeout( + newNotification(NotificationKind.Success, message, title || 'Success') + ); + + const notifyFailure = (message: string, title?: string) => + appendWithTimeout( + newNotification(NotificationKind.Failure, message, title || 'Error') + ); + + const notifyWarning = (message: string, title?: string) => + appendWithTimeout( + newNotification(NotificationKind.Warning, message, title || 'Warning') + ); + + const sorted = () => { + const { notifications } = get(notificationStore); + const array = Object.values(notifications); + + return array.sort((a, b) => b.time - a.time); + }; + + return { + sorted, + close, + subscribe, + notifySuccess, + notifyFailure, + notifyWarning + }; +} + +const notificationStore = + createNotificationStore() as unknown as Readable & + NotificationStoreMethods; + +export default notificationStore; diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 0a45b69..3d3c7ff 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,3 +1,16 @@ -

Welcome to your library project

-

Create your package using @sveltejs/package and preview/showcase your work with SvelteKit

-

Visit kit.svelte.dev to read the documentation

+ + + + + +
  • + {notification.title} +

    {notification.message}

    +
  • +
    diff --git a/svelte.config.js b/svelte.config.js index 1cf26a0..ecaf275 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -3,16 +3,16 @@ import { vitePreprocess } from '@sveltejs/kit/vite'; /** @type {import('@sveltejs/kit').Config} */ const config = { - // Consult https://kit.svelte.dev/docs/integrations#preprocessors - // for more information about preprocessors - preprocess: vitePreprocess(), + // Consult https://kit.svelte.dev/docs/integrations#preprocessors + // for more information about preprocessors + preprocess: vitePreprocess(), - kit: { - // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list. - // If your environment is not supported or you settled on a specific environment, switch out the adapter. - // See https://kit.svelte.dev/docs/adapters for more information about adapters. - adapter: adapter() - } + kit: { + // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list. + // If your environment is not supported or you settled on a specific environment, switch out the adapter. + // See https://kit.svelte.dev/docs/adapters for more information about adapters. + adapter: adapter() + } }; export default config; diff --git a/tsconfig.json b/tsconfig.json index f56aae6..9a9b2ea 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,14 +1,14 @@ { - "extends": "./.svelte-kit/tsconfig.json", - "compilerOptions": { - "allowJs": true, - "checkJs": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "sourceMap": true, - "strict": true, - "moduleResolution": "NodeNext" - } + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "moduleResolution": "NodeNext" + } } diff --git a/vite.config.ts b/vite.config.ts index 37b6a84..0131ff9 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -2,8 +2,8 @@ import { sveltekit } from '@sveltejs/kit/vite'; import { defineConfig } from 'vitest/config'; export default defineConfig({ - plugins: [sveltekit()], - test: { - include: ['src/**/*.{test,spec}.{js,ts}'] - } + plugins: [sveltekit()], + test: { + include: ['src/**/*.{test,spec}.{js,ts}'] + } }); From 5e7d03a92aaed2ff7b4e3053b40b4b9ce5c49eab Mon Sep 17 00:00:00 2001 From: Esteban Borai Date: Thu, 27 Apr 2023 01:46:23 -0400 Subject: [PATCH 2/2] fix: provide a `test` command --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d6de3c9..4bf3e28 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "prepublishOnly": "npm run package", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", - "test:unit": "vitest", + "test": "vitest", "lint": "prettier --plugin-search-dir . --check . && eslint .", "format": "prettier --plugin-search-dir . --write ." },