From aca0f0e123d815844b71a6fc53eec77c5a11cad2 Mon Sep 17 00:00:00 2001 From: Noel De Martin Date: Thu, 28 May 2026 16:46:53 +0200 Subject: [PATCH 01/11] #769 Implement design system button --- .storybook/main.js | 51 + .storybook/preview.js | 2 + config/babel.mjs | 26 + config/icons.mjs | 27 + config/postcss.mjs | 12 + config/webpack.mjs | 32 + package-lock.json | 2084 +++++++++++++++-- package.json | 13 +- .../components/button/Button.stories.ts | 62 + .../components/button/Button.styles.css | 66 + src/design-system/components/button/Button.ts | 24 + src/design-system/components/button/index.ts | 4 + src/design-system/shims.d.ts | 6 + src/design-system/styles/common.styles.css | 1 + src/design-system/styles/variables.css | 21 + src/storybook/index.ts | 31 + tsconfig.json | 3 +- webpack.config.mjs | 28 +- 18 files changed, 2292 insertions(+), 201 deletions(-) create mode 100644 config/babel.mjs create mode 100644 config/icons.mjs create mode 100644 config/postcss.mjs create mode 100644 config/webpack.mjs create mode 100644 src/design-system/components/button/Button.stories.ts create mode 100644 src/design-system/components/button/Button.styles.css create mode 100644 src/design-system/components/button/Button.ts create mode 100644 src/design-system/components/button/index.ts create mode 100644 src/design-system/shims.d.ts create mode 100644 src/design-system/styles/common.styles.css create mode 100644 src/design-system/styles/variables.css create mode 100644 src/storybook/index.ts diff --git a/.storybook/main.js b/.storybook/main.js index a80bd7872..f49f9709c 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -1,3 +1,14 @@ +import path from 'path' +import Icons from 'unplugin-icons/webpack' +import { fileURLToPath } from 'url' + +import iconsConfig from '../config/icons.mjs' +import postCSSConfig from '../config/postcss.mjs' +import { litDecoratorsLoaderOptions, resolvePathsUsingDecorators } from '../config/babel.mjs' +import { excludePathsFromRules } from '../config/webpack.mjs' + +const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..') + export default { stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'], @@ -37,6 +48,46 @@ export default { $rdf: 'rdflib' } + // Configure Lit decorators + const pathsUsingDecorators = resolvePathsUsingDecorators(projectRoot) + + config.module.rules.unshift({ + test: /\.(mjs|js|ts|tsx)$/, + include: pathsUsingDecorators, + use: { + loader: 'babel-loader', + options: litDecoratorsLoaderOptions, + } + }) + + excludePathsFromRules(config.module?.rules, pathsUsingDecorators) + + // Configure icons + config.plugins.push(Icons(iconsConfig)) + + // Configure component styles + const litCssPattern = /\.styles\.css$/ + + config.module.rules = config.module.rules.map(rule => { + if (rule?.test?.test?.('component.css')) { + return { + ...rule, + exclude: [ + ...(Array.isArray(rule.exclude) ? rule.exclude : rule.exclude ? [rule.exclude] : []), + litCssPattern + ] + } + } + + return rule + }) + + config.module.rules.push({ + test: litCssPattern, + loader: 'lit-css-loader', + options: postCSSConfig + }) + return config } } diff --git a/.storybook/preview.js b/.storybook/preview.js index 996229f9d..657e06130 100644 --- a/.storybook/preview.js +++ b/.storybook/preview.js @@ -1,3 +1,5 @@ +import '../src/design-system/styles/variables.css' + // For backward compatibility, provide rdflib and solid-logic as globals import * as rdflib from 'rdflib' import * as solidLogic from 'solid-logic' diff --git a/config/babel.mjs b/config/babel.mjs new file mode 100644 index 000000000..d35434f32 --- /dev/null +++ b/config/babel.mjs @@ -0,0 +1,26 @@ +import path from 'path' + +/** + * This file contains config options for babel using Lit decorators. + * + * @see https://lit.dev/docs/components/decorators/#using-decorators-with-babel + */ + +const pathsUsingDecorators = ['src/design-system'] + +export const litDecoratorsLoaderOptions = { + cacheDirectory: true, + assumptions: { + setPublicClassFields: false + }, + plugins: [ + '@babel/plugin-transform-class-static-block', + ['@babel/plugin-transform-typescript', { allowDeclareFields: true }], + ['@babel/plugin-proposal-decorators', { version: '2023-05' }], + ['@babel/plugin-transform-class-properties', { loose: true }] + ] +} + +export function resolvePathsUsingDecorators (projectRoot) { + return pathsUsingDecorators.map((_path) => path.resolve(projectRoot, _path)) +} diff --git a/config/icons.mjs b/config/icons.mjs new file mode 100644 index 000000000..b869fa46e --- /dev/null +++ b/config/icons.mjs @@ -0,0 +1,27 @@ +const compiler = { + compiler (svg, collection, icon) { + const id = `icon-${collection}-${icon}` + const className = id.replace(/-/g, '') + + return ` + export default class ${className} extends HTMLElement { + constructor() { + super() + this.attachShadow({ mode: 'open' }).innerHTML = ${JSON.stringify('' + svg)} + } + } + + customElements.define('${id}', ${className}) + ` + }, +} + +/** @type {import('unplugin-icons').Options} */ +export default { + scale: 1, + compiler, + iconCustomizer (_, __, props) { + props.width = '100%' + props.height = '100%' + } +} diff --git a/config/postcss.mjs b/config/postcss.mjs new file mode 100644 index 000000000..0880f95fe --- /dev/null +++ b/config/postcss.mjs @@ -0,0 +1,12 @@ +import PostCSS from 'postcss' +import TailwindCSS from '@tailwindcss/postcss' + +const cssProcessor = PostCSS([TailwindCSS()]) + +export default { + async transform (css, { filePath }) { + const result = await cssProcessor.process(css, { from: filePath }) + + return result.css + } +} diff --git a/config/webpack.mjs b/config/webpack.mjs new file mode 100644 index 000000000..efaf6d79d --- /dev/null +++ b/config/webpack.mjs @@ -0,0 +1,32 @@ +function asArray (value) { + if (!value) { + return [] + } + + return Array.isArray(value) ? value : [value] +} + +function ruleUsesSwcLoader (rule) { + const uses = rule.use ? asArray(rule.use) : [] + + return uses.some((use) => String(use?.loader ?? use).includes('swc-loader')) +} + +export function excludePathsFromRules (rule, paths) { + if (!rule) { + return + } + + if (Array.isArray(rule)) { + rule.forEach((entry) => excludePathsFromRules(entry, paths)) + return + } + + if (ruleUsesSwcLoader(rule)) { + rule.exclude = [...asArray(rule.exclude), ...paths] + } + + if (rule.oneOf) { + excludePathsFromRules(rule.oneOf, paths) + } +} diff --git a/package-lock.json b/package-lock.json index 62e48124a..2482751cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,16 +16,20 @@ "mime-types": "^3.0.2", "pane-registry": "^3.1.1", "solid-namespace": "^0.5.4", + "tailwindcss": "^4.3.0", "uuid": "^14.0.0" }, "devDependencies": { "@babel/cli": "^7.28.6", "@babel/core": "^7.29.0", + "@babel/plugin-proposal-decorators": "^7.29.7", + "@babel/plugin-transform-class-properties": "^7.29.7", "@babel/plugin-transform-runtime": "^7.29.0", "@babel/preset-env": "^7.29.5", "@babel/preset-typescript": "^7.28.5", "@babel/runtime": "^7.29.2", "@eslint/js": "^9.39.4", + "@iconify-json/lucide": "^1.2.110", "@mdx-js/react": "^3.1.1", "@storybook/addon-actions": "8.6.18", "@storybook/addon-docs": "8.6.18", @@ -35,6 +39,7 @@ "@storybook/addon-webpack5-compiler-swc": "^3.0.0", "@storybook/html": "8.6.18", "@storybook/html-webpack5": "8.6.18", + "@tailwindcss/postcss": "^4.3.0", "@testing-library/dom": "^10.4.1", "@testing-library/user-event": "^13.5.0", "@types/jest": "^30.0.0", @@ -43,6 +48,7 @@ "@typescript-eslint/parser": "^8.59.3", "babel-jest": "^30.4.1", "babel-loader": "^10.1.1", + "css-loader": "^7.1.4", "eslint": "^9.39.4", "eslint-import-resolver-typescript": "^4.4.4", "eslint-plugin-import": "^2.32.0", @@ -55,8 +61,10 @@ "jest": "^30.4.2", "jest-environment-jsdom": "^30.4.1", "jsdom": "^28.1.0", + "lit-css-loader": "^4.0.1", "neostandard": "^0.13.0", "nock": "^15.0.0", + "postcss": "^8.5.15", "react": "^17.0.2", "react-dom": "^17.0.2", "react-is": "^17.0.2", @@ -65,6 +73,7 @@ "terser-webpack-plugin": "^5.6.0", "typedoc": "^0.28.19", "typescript": "^5.9.3", + "unplugin-icons": "^23.0.1", "webpack": "^5.106.2", "webpack-cli": "^7.0.2" }, @@ -83,6 +92,33 @@ "dev": true, "license": "MIT" }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@antfu/install-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz", + "integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "package-manager-detector": "^1.3.0", + "tinyexec": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/@asamuzakjp/css-color": { "version": "5.1.10", "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.10.tgz", @@ -625,6 +661,24 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.29.7.tgz", + "integrity": "sha512-EtU0Hi3GvrTqD56xKmZvV/uCXK2ZbwVNPNLAquVItcAZpUhkXwWlo3Fmj0c2LxgSf2I8IDULeAepwNP1OefLXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/plugin-syntax-decorators": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-proposal-private-property-in-object": { "version": "7.21.0-placeholder-for-preset-env.2", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", @@ -693,6 +747,22 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.29.7.tgz", + "integrity": "sha512-9MTTLbF39X6sqM92JPEsoI7++26hjZvzkxKZy64aMhWLH2mPkJ/Q3AV4QLmls3R14FpSpkOwQQfUh962JGQxxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-import-assertions": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.29.7.tgz", @@ -2019,6 +2089,13 @@ "specificity": "bin/cli.js" } }, + "node_modules/@colordx/core": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@colordx/core/-/core-5.4.3.tgz", + "integrity": "sha512-kIxYSfA5T8HXjav55UaaH/o/cKivF6jCCGIb8eqtcsfI46wsvlSiT8jMDyrl779qLec3c2c2oHBZo4oAhvbjrQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@csstools/color-helpers": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", @@ -3013,6 +3090,35 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@iconify-json/lucide": { + "version": "1.2.110", + "resolved": "https://registry.npmjs.org/@iconify-json/lucide/-/lucide-1.2.110.tgz", + "integrity": "sha512-rLeHqnZZBxZbprbVwf6uY7HB5GkGVgvT9VujhjvaUEqFDLKZON6zR8K1f8uD1brBwf5TJ0TIvvW8mz5u2XJU+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@iconify/types": "*" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@iconify/utils": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.3.tgz", + "integrity": "sha512-LPKOXPn/zV+zis1oOfGWogaXVpqUybF3ZS6SCZIsz8vg0ivVp9+fVqyYB7xq0aiST/VhUQYGO1qo6uoYSiEJqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@antfu/install-pkg": "^1.1.0", + "@iconify/types": "^2.0.0", + "import-meta-resolve": "^4.2.0" + } + }, "node_modules/@inrupt/oidc-client-ext": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@inrupt/oidc-client-ext/-/oidc-client-ext-4.0.0.tgz", @@ -4033,6 +4139,16 @@ "url": "https://opencollective.com/pkgr" } }, + "node_modules/@pwrs/lit-css": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@pwrs/lit-css/-/lit-css-4.1.0.tgz", + "integrity": "sha512-h4FGHzbiposqASNcdzofgsROa0r27DbpknyImJmMIUvCU4B0M+STLbEW76dnNKjwFB9mQMe1O4YonRUyBIujPg==", + "dev": true, + "license": "ISC", + "dependencies": { + "cssnano": "^7.0.7" + } + }, "node_modules/@rdfjs/types": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@rdfjs/types/-/types-2.0.1.tgz", @@ -4461,6 +4577,42 @@ } } }, + "node_modules/@storybook/builder-webpack5/node_modules/css-loader": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz", + "integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.33", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, "node_modules/@storybook/builder-webpack5/node_modules/semver": { "version": "7.8.0", "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", @@ -5024,129 +5176,400 @@ "@swc/counter": "^0.1.3" } }, - "node_modules/@testing-library/dom": { - "version": "10.4.1", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", - "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "node_modules/@tailwindcss/node": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.0.tgz", + "integrity": "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^5.0.1", - "aria-query": "5.3.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.5.0", - "picocolors": "1.1.1", - "pretty-format": "^27.0.2" - }, - "engines": { - "node": ">=18" + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.21.0", + "jiti": "^2.6.1", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.0" } }, - "node_modules/@testing-library/user-event": { - "version": "13.5.0", - "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.5.0.tgz", - "integrity": "sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==", + "node_modules/@tailwindcss/oxide": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.0.tgz", + "integrity": "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.12.5" - }, "engines": { - "node": ">=10", - "npm": ">=6" + "node": ">= 20" }, - "peerDependencies": { - "@testing-library/dom": ">=7.21.4" - } - }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.0", + "@tailwindcss/oxide-darwin-arm64": "4.3.0", + "@tailwindcss/oxide-darwin-x64": "4.3.0", + "@tailwindcss/oxide-freebsd-x64": "4.3.0", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.0", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.0", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.0", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.0", + "@tailwindcss/oxide-linux-x64-musl": "4.3.0", + "@tailwindcss/oxide-wasm32-wasi": "4.3.0", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.0", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.0" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.0.tgz", + "integrity": "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", "optional": true, - "dependencies": { - "tslib": "^2.4.0" + "os": [ + "android" + ], + "engines": { + "node": ">= 20" } }, - "node_modules/@types/aria-query": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", - "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.0.tgz", + "integrity": "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" } }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.0.tgz", + "integrity": "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" } }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.0.tgz", + "integrity": "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" } }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.0.tgz", + "integrity": "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" } }, - "node_modules/@types/debug": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", - "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.0.tgz", + "integrity": "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@types/ms": "*" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" } }, - "node_modules/@types/eslint": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", - "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.0.tgz", + "integrity": "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" } }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.0.tgz", + "integrity": "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.0.tgz", + "integrity": "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.0.tgz", + "integrity": "sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.10.0", + "@emnapi/runtime": "^1.10.0", + "@emnapi/wasi-threads": "^1.2.1", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.0.tgz", + "integrity": "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.0.tgz", + "integrity": "sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/postcss": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.3.0.tgz", + "integrity": "sha512-Jm05Tjx+9yCLGv5qw1c+84Psds8MnyrEQYCB+FFk2lgGiUjlRqdxke4mVTuYrj2xnVZqKim2Apr5ySuQRYAw/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "4.3.0", + "@tailwindcss/oxide": "4.3.0", + "postcss": "^8.5.10", + "tailwindcss": "4.3.0" + } + }, + "node_modules/@testing-library/dom": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/user-event": { + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.5.0.tgz", + "integrity": "sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", "dev": true, @@ -7145,6 +7568,19 @@ "node": ">=6" } }, + "node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001788", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001788.tgz", @@ -7411,6 +7847,13 @@ "dev": true, "license": "MIT" }, + "node_modules/confbox": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", + "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", + "dev": true, + "license": "MIT" + }, "node_modules/constants-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", @@ -7501,32 +7944,45 @@ "node": ">= 8" } }, + "node_modules/css-declaration-sorter": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.4.0.tgz", + "integrity": "sha512-LTuzjPoyA2vMGKKcaOqKSp7Ub2eGrNfKiZH4LpezxpNrsICGCSFvsQOI29psISxNZtaXibkC2CXzrQ5enMeGGw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14 || ^16 || >=18" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, "node_modules/css-loader": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz", - "integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.4.tgz", + "integrity": "sha512-vv3J9tlOl04WjiMvHQI/9tmIrCxVrj6PFbHemBB1iihpeRbi/I4h033eoFIhwxBBqLhI0KYFS7yvynBFhIZfTw==", "dev": true, "license": "MIT", "dependencies": { "icss-utils": "^5.1.0", - "postcss": "^8.4.33", + "postcss": "^8.4.40", "postcss-modules-extract-imports": "^3.1.0", "postcss-modules-local-by-default": "^4.0.5", "postcss-modules-scope": "^3.2.0", "postcss-modules-values": "^4.0.0", "postcss-value-parser": "^4.2.0", - "semver": "^7.5.4" + "semver": "^7.6.3" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 18.12.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "@rspack/core": "0.x || 1.x", - "webpack": "^5.0.0" + "@rspack/core": "0.x || ^1.0.0 || ^2.0.0-0", + "webpack": "^5.27.0" }, "peerDependenciesMeta": { "@rspack/core": { @@ -7607,39 +8063,154 @@ "node": ">=4" } }, - "node_modules/cssstyle": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-6.2.0.tgz", - "integrity": "sha512-Fm5NvhYathRnXNVndkUsCCuR63DCLVVwGOOwQw782coXFi5HhkXdu289l59HlXZBawsyNccXfWRYvLzcDCdDig==", + "node_modules/cssnano": { + "version": "7.1.9", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-7.1.9.tgz", + "integrity": "sha512-uPR75+5Dk/WJ/YSPR1/YDHdwMM9c5FsaARljfKWgeCKLKOtJ0we21xy/RcCjn53fZnD/f6yYEIZ8pu18+GnbNQ==", "dev": true, "license": "MIT", "dependencies": { - "@asamuzakjp/css-color": "^5.0.1", - "@csstools/css-syntax-patches-for-csstree": "^1.0.28", - "css-tree": "^3.1.0", - "lru-cache": "^11.2.6" + "cssnano-preset-default": "^7.0.17", + "lilconfig": "^3.1.3" }, "engines": { - "node": ">=20" + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/cssnano" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/cssnano-preset-default": { + "version": "7.0.17", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-7.0.17.tgz", + "integrity": "sha512-11qO63A+czwguQFJCaTdICvbaxn0pJzz/XghLlv+OT7WyToDxAMR0Xb3/26/l0y0hQJywwNbj/SLSQlGBHE1OA==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.2", + "css-declaration-sorter": "^7.2.0", + "cssnano-utils": "^5.0.3", + "postcss-calc": "^10.1.1", + "postcss-colormin": "^7.0.10", + "postcss-convert-values": "^7.0.12", + "postcss-discard-comments": "^7.0.8", + "postcss-discard-duplicates": "^7.0.4", + "postcss-discard-empty": "^7.0.3", + "postcss-discard-overridden": "^7.0.3", + "postcss-merge-longhand": "^7.0.7", + "postcss-merge-rules": "^7.0.11", + "postcss-minify-font-values": "^7.0.3", + "postcss-minify-gradients": "^7.0.5", + "postcss-minify-params": "^7.0.9", + "postcss-minify-selectors": "^7.1.2", + "postcss-normalize-charset": "^7.0.3", + "postcss-normalize-display-values": "^7.0.3", + "postcss-normalize-positions": "^7.0.4", + "postcss-normalize-repeat-style": "^7.0.4", + "postcss-normalize-string": "^7.0.3", + "postcss-normalize-timing-functions": "^7.0.3", + "postcss-normalize-unicode": "^7.0.9", + "postcss-normalize-url": "^7.0.3", + "postcss-normalize-whitespace": "^7.0.3", + "postcss-ordered-values": "^7.0.4", + "postcss-reduce-initial": "^7.0.9", + "postcss-reduce-transforms": "^7.0.3", + "postcss-svgo": "^7.1.3", + "postcss-unique-selectors": "^7.0.7" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" } }, - "node_modules/cssstyle/node_modules/lru-cache": { - "version": "11.3.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.5.tgz", - "integrity": "sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==", + "node_modules/cssnano-utils": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-5.0.3.tgz", + "integrity": "sha512-ynIREMICLxkxm7e9bCR9sh75s4Q5drICi0ua1yxo5jH2XPBqSKkl4dOh4EbFqtUmnTMhRffHgYL0EKKkMjtJTg==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "engines": { - "node": "20 || >=22" + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" } }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", "dev": true, "license": "MIT", - "peer": true + "dependencies": { + "css-tree": "~2.2.0" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/cssstyle": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-6.2.0.tgz", + "integrity": "sha512-Fm5NvhYathRnXNVndkUsCCuR63DCLVVwGOOwQw782coXFi5HhkXdu289l59HlXZBawsyNccXfWRYvLzcDCdDig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^5.0.1", + "@csstools/css-syntax-patches-for-csstree": "^1.0.28", + "css-tree": "^3.1.0", + "lru-cache": "^11.2.6" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cssstyle/node_modules/lru-cache": { + "version": "11.3.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.5.tgz", + "integrity": "sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT", + "peer": true }, "node_modules/data-urls": { "version": "7.0.0", @@ -7856,6 +8427,16 @@ "node": ">=6" } }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -8090,14 +8671,14 @@ "license": "MIT" }, "node_modules/enhanced-resolve": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", - "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.22.0.tgz", + "integrity": "sha512-xYcDWrpELkFzz9SpZ3PlI6Eu6eD93Yf0WLDRxikGhWJ3MAir2SNZTIVCVZqZ/NUyx8AdMc2gT9C0gPiw18kG+A==", "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", - "tapable": "^2.3.0" + "tapable": "^2.3.3" }, "engines": { "node": ">=10.13.0" @@ -9194,6 +9775,13 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, + "node_modules/exsolve": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", + "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", + "dev": true, + "license": "MIT" + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -10393,6 +10981,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -12721,6 +13320,16 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, "node_modules/jose": { "version": "5.10.0", "resolved": "https://registry.npmjs.org/jose/-/jose-5.10.0.tgz", @@ -13009,6 +13618,280 @@ "node": ">= 0.8.0" } }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, "node_modules/limit-it": { "version": "3.2.11", "resolved": "https://registry.npmjs.org/limit-it/-/limit-it-3.2.11.tgz", @@ -13047,6 +13930,17 @@ "lit-html": "^3.3.0" } }, + "node_modules/lit-css-loader": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lit-css-loader/-/lit-css-loader-4.0.1.tgz", + "integrity": "sha512-7dU6j9OmkDzcmPTUQSHC6pLudjOVqgzsa3R0/5hMQSjPo1IAuhUEw70fDT1HeqNr2DlIgk0YCKHdDuxudeSoZA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@pwrs/lit-css": "^4.0.0", + "loader-utils": "^3.3.1" + } + }, "node_modules/lit-element": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.2.2.tgz", @@ -13081,15 +13975,43 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "node_modules/loader-utils": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.3.1.tgz", + "integrity": "sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==", "dev": true, "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/local-pkg": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.2.1.tgz", + "integrity": "sha512-++gUqRDEvcnN6Zhqrr+y/CkVEHhlrR96vZn3nZZPYzMcBUyBtTKzB9NadClFIsIVSsu+3i9tfk/erqy9kAmt7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "mlly": "^1.7.4", + "pkg-types": "^2.3.0", + "quansync": "^0.2.11" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, "engines": { "node": ">=10" }, @@ -13111,6 +14033,13 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -13118,6 +14047,13 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true, + "license": "MIT" + }, "node_modules/longest-streak": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", @@ -14217,6 +15153,38 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/mlly": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", + "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.16.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.3" + } + }, + "node_modules/mlly/node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/mlly/node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -14239,9 +15207,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", "dev": true, "funding": [ { @@ -14778,6 +15746,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, "node_modules/oidc-client-ts": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/oidc-client-ts/-/oidc-client-ts-3.5.0.tgz", @@ -14990,6 +15969,13 @@ "integrity": "sha512-+yEXtNdlCs5N0Zy/9uvkifgf/RqnGu0WqP4j9Wu1Us4YReFe1YNBh2Krmf8B1xGxjpYnta63K55QP8bkafnOzA==", "peer": true }, + "node_modules/package-manager-detector": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.6.0.tgz", + "integrity": "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==", + "dev": true, + "license": "MIT" + }, "node_modules/package.json": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/package.json/-/package.json-2.0.1.tgz", @@ -15175,6 +16161,13 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, "node_modules/peowly": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/peowly/-/peowly-1.3.3.tgz", @@ -15312,125 +16305,544 @@ "dev": true, "license": "MIT", "dependencies": { - "p-limit": "^2.2.0" + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-types": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.1.tgz", + "integrity": "sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.2.4", + "exsolve": "^1.0.8", + "pathe": "^2.0.3" + } + }, + "node_modules/polished": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/polished/-/polished-4.3.1.tgz", + "integrity": "sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.17.8" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-calc": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-10.1.1.tgz", + "integrity": "sha512-NYEsLHh8DgG/PRH2+G9BTuUdtf9ViS+vdoQ0YA5OQdGsfN4ztiwtDWNtBl9EKeqNMFnIu8IKZ0cLxEQ5r5KVMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^7.0.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12 || ^20.9 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.38" + } + }, + "node_modules/postcss-colormin": { + "version": "7.0.10", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-7.0.10.tgz", + "integrity": "sha512-yFr6JezOolHLta/buLE71VKPh2mXursp4saVe98/ol8ZnEWhL+racShqPKlvd/DKWLre/39B6HhcMXf7RZ3hxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@colordx/core": "^5.4.3", + "browserslist": "^4.28.2", + "caniuse-api": "^3.0.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-convert-values": { + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-7.0.12.tgz", + "integrity": "sha512-xurKu5qqk4viR3Cp3p4xBR4KfnZm4w4ys6+UBwBmeuBSNkH7+DtLnYOYnOffgtE4yx8sH9S1VZ6RAAvROXzP2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.2", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-discard-comments": { + "version": "7.0.8", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-7.0.8.tgz", + "integrity": "sha512-CvvS5S9WrXblFXCEJ9nVo+4z+eA7zSC7Z88V1HEJuwlQhlFnYTIjg1xJY+BCUiG2bvICap2tXii4mP22BD108Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^7.1.1" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-7.0.4.tgz", + "integrity": "sha512-VBNn1+EuMZkeGVVtz0gRfbNGtx9IFgAsAV+E2pHtXPrp4qfGBkhTIiAuE/wrb+Y6Pakg9NewAlfTpYIFAWODtw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-discard-empty": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-7.0.3.tgz", + "integrity": "sha512-M2pyjQCU+/7cMHVtL6bKTHjv0lZnPLMpicgr67Dlth7AbuV9gjVTtUqaRwn6Pp6BwSDspUzhz8SaUrRykJU5Dw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-7.0.3.tgz", + "integrity": "sha512-aNovXo9UsZuRNLzHJtp13lHIvinDPfiXBPePpXkSjCbgp++iU2FqE+YxvjIsg6EdyPZsASFbfu+JcBFVsErXIQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-merge-longhand": { + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-7.0.7.tgz", + "integrity": "sha512-b3mfYUxR388u5Pt0HPcVIUtUDn/k15UfTY9M+ORW+meCR6JLNxoZffiYvXyOYQoRYQNZyX/UFkMCM/mNHxe1qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^7.0.11" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-merge-rules": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-7.0.11.tgz", + "integrity": "sha512-SJUPM18g2BmPhf8BVlbwqWz4aK3pLu6u6xjfwEzra7xL6IBR10sUaiB++EzqcVfadPHrKBSMlNdP+XieykhI+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.2", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^5.0.3", + "postcss-selector-parser": "^7.1.1" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-minify-font-values": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-7.0.3.tgz", + "integrity": "sha512-yilG/VOaNI74IylQvAQQxm3/wZVBkXyYUqNUAdxqwtbWUXPsbK1q8Ms0mL83v+f8YicgcyfYCRZtWACUdYajpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-minify-gradients": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-7.0.5.tgz", + "integrity": "sha512-YraROyQRg3BI1+Hg8E05B/JPdnTm8EDSVu4P2BxdM+CRiOyfmou809+chGIqo6fQqwjPGQ947nbGncSjmTU1WQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@colordx/core": "^5.4.3", + "cssnano-utils": "^5.0.3", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-minify-params": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-7.0.9.tgz", + "integrity": "sha512-R8itbB8BhlpoYyBm1ou0dD+vJnQ3F6adQipR4UnkCHUwlo+S9WXJaDRg1RHjC8YVAtIdrQzSWvJl40HnGDTKjA==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.2", + "cssnano-utils": "^5.0.3", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-minify-selectors": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-7.1.2.tgz", + "integrity": "sha512-aQtrEWKwqafNlExcKHQvPGsXR2+vlUqqJtf5XsCQcgsSb5PL4wlujWBYDJuWsP4UnQX1YHDHU8qRlD+1PzTQ+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.1", + "caniuse-api": "^3.0.0", + "cssesc": "^3.0.0", + "postcss-selector-parser": "^7.1.1" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz", + "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^7.0.0", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", + "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", + "dev": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-7.0.3.tgz", + "integrity": "sha512-NoBfZu8PR4c2NlmjvrqQTzCzLY79hwcSRgNQ3ZiNK0ABzf9kYKloE/jNj+/8GQY1wsm8pRRgANk6ydLH8cwo0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-normalize-display-values": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-7.0.3.tgz", + "integrity": "sha512-ldsCX0QIt05pKIOobZtVQ48wXJecr+czw4+e1/YjVhLMqslShgpVxgPtI2CefURR8oyVoYaU/l829MMwExDMLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-normalize-positions": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-7.0.4.tgz", + "integrity": "sha512-VEvlpeGd3Ju1Hqa/oN4jaP3+ms4laYwkEL9N9u+B6k54PZjXbW1n6wI+aVprf1BQXlCYpS5+1pl/7/vHiKgARg==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-7.0.4.tgz", + "integrity": "sha512-6mPKlY/8cSaDHxX502wERADarJsccwlky6yIrOapHH2ZgfoKAV94SbiTKfKEs4EEpdazuc3J72WsqeYk7hp9+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-normalize-string": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-7.0.3.tgz", + "integrity": "sha512-HnEQPUchi1eznmDKEYrKUTqrprEq97SrpUYClgUkv7V2zRODD9DFoUsYU+m9ZOetmD5ku7fEMZB/lwy8IT6xVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": ">=8" + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" } }, - "node_modules/polished": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/polished/-/polished-4.3.1.tgz", - "integrity": "sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==", + "node_modules/postcss-normalize-timing-functions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-7.0.3.tgz", + "integrity": "sha512-zmEzHdvpZBZu0OKlbJSfgASQvaayyAoVuWtvyr34IJ/LyS+DaOKvvR3EvFJ9RWWtNIx+CMvO125OVophaxNYew==", "dev": true, "license": "MIT", "dependencies": { - "@babel/runtime": "^7.17.8" + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": ">=10" + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" } }, - "node_modules/possible-typed-array-names": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", - "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "node_modules/postcss-normalize-unicode": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-7.0.9.tgz", + "integrity": "sha512-DRAdWfeh/TjmhLJsw91vdiWCnUod9iwvM7xyS02/nF/sLsCR3A8l3pztrSUrWG8DSBqfX7yEk9FM0USaVJ2mSg==", "dev": true, "license": "MIT", + "dependencies": { + "browserslist": "^4.28.2", + "postcss-value-parser": "^4.2.0" + }, "engines": { - "node": ">= 0.4" + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" } }, - "node_modules/postcss": { - "version": "8.5.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", - "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", + "node_modules/postcss-normalize-url": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-7.0.3.tgz", + "integrity": "sha512-CL93wmloq5qsffmFv+bw24MIRbmhHrp53qoh1LDAb/5TtjWEXI/np4xcP/Gw9oWCb2XyWnqHYLDUwiKRoJBA1Q==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14" + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" } }, - "node_modules/postcss-modules-extract-imports": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", - "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", + "node_modules/postcss-normalize-whitespace": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-7.0.3.tgz", + "integrity": "sha512-FdHjjn+Ht5Z2ZRjNOmeCbNq6lq09sUYKpmlF/Aq0XjVNSLTL6fmHlA/3swN2wP2caY9GV/tjSDcIIyS7aN7W0A==", "dev": true, - "license": "ISC", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, "engines": { - "node": "^10 || ^12 || >= 14" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "peerDependencies": { - "postcss": "^8.1.0" + "postcss": "^8.5.13" } }, - "node_modules/postcss-modules-local-by-default": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz", - "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", + "node_modules/postcss-ordered-values": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-7.0.4.tgz", + "integrity": "sha512-nubSi49hDHQk4E8KIj+IbLY8Bg+8OcSUEhgyolgM+atnOvXjV7EjaR6bac4YGZoFyPa9mWoAF3EaYbWdFkKqVg==", "dev": true, "license": "MIT", "dependencies": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^7.0.0", - "postcss-value-parser": "^4.1.0" + "cssnano-utils": "^5.0.3", + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >= 14" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "peerDependencies": { - "postcss": "^8.1.0" + "postcss": "^8.5.13" } }, - "node_modules/postcss-modules-scope": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", - "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", + "node_modules/postcss-reduce-initial": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-7.0.9.tgz", + "integrity": "sha512-ztTNPdIxXTxtBcG03E9u8v44M4ElXbMIRT7pf2onlquGula0Y83nKKxqM22FA/hMgkfCjN7ohevkVlaNwI8iOQ==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "postcss-selector-parser": "^7.0.0" + "browserslist": "^4.28.2", + "caniuse-api": "^3.0.0" }, "engines": { - "node": "^10 || ^12 || >= 14" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "peerDependencies": { - "postcss": "^8.1.0" + "postcss": "^8.5.13" } }, - "node_modules/postcss-modules-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "node_modules/postcss-reduce-transforms": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-7.0.3.tgz", + "integrity": "sha512-FXsnN9ZwcZTT8Yf8cAHA8qIGUXcX6WfLd9JoYhrdDfmvsVhhfqkkv7m4AC3rwFOfz+GzkUa87OCKF9dUcicd+g==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "icss-utils": "^5.0.0" + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >= 14" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "peerDependencies": { - "postcss": "^8.1.0" + "postcss": "^8.5.13" } }, "node_modules/postcss-selector-parser": { @@ -15447,6 +16859,39 @@ "node": ">=4" } }, + "node_modules/postcss-svgo": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-7.1.3.tgz", + "integrity": "sha512-2QfoFOYMcj8lwcVEf9WeTlkVIAm7u2QvOEhMzkQU3KUhhGX/l8hVV9EtjMv4iq3E9iI3OeeMN0YoMLbGusuigw==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "svgo": "^4.0.1" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >= 18" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-unique-selectors": { + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-7.0.7.tgz", + "integrity": "sha512-d+sCkaRnSefghOUdH8CMJZV9yUQhj2ojpe8Nw/lA+LV1UOfeleGkLTl6XdCFFSai9UJ+DJPb69FFuqthXYsY8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^7.1.1" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", @@ -15608,6 +17053,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/quansync": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz", + "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, "node_modules/r-json": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/r-json/-/r-json-1.3.1.tgz", @@ -16229,6 +17691,16 @@ "dev": true, "license": "MIT" }, + "node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, "node_modules/saxes": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", @@ -16883,6 +18355,23 @@ "webpack": "^5.0.0" } }, + "node_modules/stylehacks": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-7.0.11.tgz", + "integrity": "sha512-iODNfhXVLqc5LADs+Y6Oh5wJuK5ZcHbVng8aiK3y9pjMQdc5hLrBW0eFU6FtnpNrE6PoEg/MmFTU4waotj5WNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.2", + "postcss-selector-parser": "^7.1.1" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -16908,6 +18397,118 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/svgo": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.1.tgz", + "integrity": "sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^11.1.0", + "css-select": "^5.1.0", + "css-tree": "^3.0.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.1.1", + "sax": "^1.5.0" + }, + "bin": { + "svgo": "bin/svgo.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/svgo/node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/svgo/node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/svgo/node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/svgo/node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/svgo/node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/svgo/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/swc-loader": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/swc-loader/-/swc-loader-0.2.7.tgz", @@ -16945,10 +18546,16 @@ "url": "https://opencollective.com/synckit" } }, + "node_modules/tailwindcss": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.0.tgz", + "integrity": "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==", + "license": "MIT" + }, "node_modules/tapable": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.2.tgz", - "integrity": "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", "dev": true, "license": "MIT", "engines": { @@ -17208,6 +18815,16 @@ "dev": true, "license": "MIT" }, + "node_modules/tinyexec": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.2.tgz", + "integrity": "sha512-M/Q0B2cp4K7kynaT/vnED1j8TlLY+Pp7C6Wl2bl/7u/F0mUVwdyOpwomQb8JpYLitHUssAJRmLZdMCGsrx7i+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/tinyglobby": { "version": "0.2.16", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", @@ -17682,6 +19299,13 @@ "dev": true, "license": "MIT" }, + "node_modules/ufo": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", + "dev": true, + "license": "MIT" + }, "node_modules/ul": { "version": "5.2.16", "resolved": "https://registry.npmjs.org/ul/-/ul-5.2.16.tgz", @@ -17889,6 +19513,72 @@ "node": ">=14.0.0" } }, + "node_modules/unplugin-icons": { + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/unplugin-icons/-/unplugin-icons-23.0.1.tgz", + "integrity": "sha512-rv0XEJepajKzDLvRUWASM8K+8+/CCfZn2jtogXqg6RIp7kpatRc/aFrVJn8ANQA09e++lPEEv9yX8cC9enc+QQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@antfu/install-pkg": "^1.1.0", + "@iconify/utils": "^3.1.0", + "local-pkg": "^1.1.2", + "obug": "^2.1.1", + "unplugin": "^2.3.11" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@svgr/core": ">=7.0.0", + "@svgx/core": "^1.0.1", + "@vue/compiler-sfc": "^3.0.2", + "svelte": "^3.0.0 || ^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "@svgr/core": { + "optional": true + }, + "@svgx/core": { + "optional": true + }, + "@vue/compiler-sfc": { + "optional": true + }, + "svelte": { + "optional": true + } + } + }, + "node_modules/unplugin-icons/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/unplugin-icons/node_modules/unplugin": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.11.tgz", + "integrity": "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "acorn": "^8.15.0", + "picomatch": "^4.0.3", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": ">=18.12.0" + } + }, "node_modules/unrs-resolver": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", diff --git a/package.json b/package.json index ab604504f..43183fdf6 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "import": "./dist/solid-ui.esm.js", "require": "./dist/solid-ui.js" }, + "./design-system.css": "./src/design-system/styles/variables.css", "./components/header": { "types": "./dist/components/header/index.d.ts", "import": "./dist/components/header/index.esm.js", @@ -99,7 +100,8 @@ "files": [ "dist/", "README.md", - "LICENSE" + "LICENSE", + "src/design-system/styles/variables.css" ], "scripts": { "clean": "rm -rf ./dist ./src/versionInfo.ts ./docs/api .tsbuildinfo", @@ -163,6 +165,7 @@ "mime-types": "^3.0.2", "pane-registry": "^3.1.1", "solid-namespace": "^0.5.4", + "tailwindcss": "^4.3.0", "uuid": "^14.0.0" }, "peerDependencies": { @@ -172,11 +175,14 @@ "devDependencies": { "@babel/cli": "^7.28.6", "@babel/core": "^7.29.0", + "@babel/plugin-proposal-decorators": "^7.29.7", + "@babel/plugin-transform-class-properties": "^7.29.7", "@babel/plugin-transform-runtime": "^7.29.0", "@babel/preset-env": "^7.29.5", "@babel/preset-typescript": "^7.28.5", "@babel/runtime": "^7.29.2", "@eslint/js": "^9.39.4", + "@iconify-json/lucide": "^1.2.110", "@mdx-js/react": "^3.1.1", "@storybook/addon-actions": "8.6.18", "@storybook/addon-docs": "8.6.18", @@ -186,6 +192,7 @@ "@storybook/addon-webpack5-compiler-swc": "^3.0.0", "@storybook/html": "8.6.18", "@storybook/html-webpack5": "8.6.18", + "@tailwindcss/postcss": "^4.3.0", "@testing-library/dom": "^10.4.1", "@testing-library/user-event": "^13.5.0", "@types/jest": "^30.0.0", @@ -194,6 +201,7 @@ "@typescript-eslint/parser": "^8.59.3", "babel-jest": "^30.4.1", "babel-loader": "^10.1.1", + "css-loader": "^7.1.4", "eslint": "^9.39.4", "eslint-import-resolver-typescript": "^4.4.4", "eslint-plugin-import": "^2.32.0", @@ -206,8 +214,10 @@ "jest": "^30.4.2", "jest-environment-jsdom": "^30.4.1", "jsdom": "^28.1.0", + "lit-css-loader": "^4.0.1", "neostandard": "^0.13.0", "nock": "^15.0.0", + "postcss": "^8.5.15", "react": "^17.0.2", "react-dom": "^17.0.2", "react-is": "^17.0.2", @@ -216,6 +226,7 @@ "terser-webpack-plugin": "^5.6.0", "typedoc": "^0.28.19", "typescript": "^5.9.3", + "unplugin-icons": "^23.0.1", "webpack": "^5.106.2", "webpack-cli": "^7.0.2" }, diff --git a/src/design-system/components/button/Button.stories.ts b/src/design-system/components/button/Button.stories.ts new file mode 100644 index 000000000..1ccfabb5f --- /dev/null +++ b/src/design-system/components/button/Button.stories.ts @@ -0,0 +1,62 @@ +import { html, nothing } from 'lit' +import { unsafeHTML } from 'lit/directives/unsafe-html.js' +import { defineControlOptions, defineStoryRender } from '../../../storybook' + +import '~icons/lucide/check' +import '~icons/lucide/plus' +import { BUTTON_VARIANTS } from './Button' + +const ICON_OPTIONS = defineControlOptions([['Check', 'check'], ['Plus', 'plus'], ['None', null]]) + +const meta = { + title: 'Design System/Button', + args: { + text: 'Save Changes', + variant: 'primary', + leftIcon: 'None', + rightIcon: 'None', + }, + argTypes: { + variant: { + control: 'select', + options: BUTTON_VARIANTS, + }, + leftIcon: ICON_OPTIONS.control, + rightIcon: ICON_OPTIONS.control, + text: { control: 'text' }, + }, +} as const + +const render = defineStoryRender(({ text, variant, leftIcon, rightIcon }) => { + const resolvedLeftIcon = ICON_OPTIONS.resolve(leftIcon) + const resolvedRightIcon = ICON_OPTIONS.resolve(rightIcon) + + return html` + + ${resolvedLeftIcon ? unsafeHTML(``) : nothing} + ${text} + ${resolvedRightIcon ? unsafeHTML(``) : nothing} + + ` +}) + +export default meta + +export const Primary = { render } + +export const Secondary = { + render, + args: { + text: 'Cancel', + variant: 'secondary' + }, +} + +export const Tertiary = { + render, + args: { + text: 'Add More', + variant: 'tertiary', + leftIcon: 'Plus', + }, +} diff --git a/src/design-system/components/button/Button.styles.css b/src/design-system/components/button/Button.styles.css new file mode 100644 index 000000000..9ec06c9b5 --- /dev/null +++ b/src/design-system/components/button/Button.styles.css @@ -0,0 +1,66 @@ +@import '../../styles/common.styles.css'; + +:host { + display: inline-flex; + + button { + background: var(--solid-ui-color-primary); + padding: 10px 20px; + border-radius: 5px; + font-size: var(--solid-ui-font-size-lg); + font-weight: 600; + color: var(--solid-ui-color-white); + border: 1px solid var(--solid-ui-color-primary); + display: flex; + justify-content: center; + align-items: center; + gap: 5px; + flex: 1; + + &:disabled { + opacity: 0.5; + cursor: not-allowed; + } + + &:hover:not(:disabled) { + background: var(--solid-ui-color-tertiary); + border-color: var(--solid-ui-color-tertiary); + } + + ::slotted([slot="left-icon"]), ::slotted([slot="right-icon"]) { + width: var(--solid-ui-font-size-xl); + height: var(--solid-ui-font-size-xl); + } + } +} + +:host([variant='secondary']) button { + background: var(--solid-ui-color-white); + border-color: var(--solid-ui-color-slate-400); + color: var(--solid-ui-color-gray-800); + + &:hover { + background: var(--solid-ui-color-tertiary); + border-color: var(--solid-ui-color-tertiary); + color: var(--solid-ui-color-white); + } +} + +:host([variant='tertiary']) button { + background: transparent; + border-color: transparent; + color: var(--solid-ui-color-primary); + font-size: var(--solid-ui-font-size-sm); + padding: 0; + border-radius: 0; + + &:hover { + color: var(--solid-ui-color-tertiary); + border-bottom: 1px solid var(--solid-ui-color-tertiary); + } + + ::slotted([slot="left-icon"]), ::slotted([slot="right-icon"]) { + width: var(--solid-ui-font-size-md); + height: var(--solid-ui-font-size-md); + } +} diff --git a/src/design-system/components/button/Button.ts b/src/design-system/components/button/Button.ts new file mode 100644 index 000000000..dcf95a128 --- /dev/null +++ b/src/design-system/components/button/Button.ts @@ -0,0 +1,24 @@ +import { html, LitElement } from 'lit' +import { customElement, property } from 'lit/decorators.js' +import styles from './Button.styles.css' + +export const BUTTON_VARIANTS = ['primary', 'secondary', 'tertiary'] as const +export type ButtonVariant = typeof BUTTON_VARIANTS[number] + +@customElement('solid-ui-button') +export default class Button extends LitElement { + static styles = styles + + @property({ type: String, reflect: true }) + accessor variant: ButtonVariant = 'primary' + + render () { + return html` + + ` + } +} diff --git a/src/design-system/components/button/index.ts b/src/design-system/components/button/index.ts new file mode 100644 index 000000000..5adea3b44 --- /dev/null +++ b/src/design-system/components/button/index.ts @@ -0,0 +1,4 @@ +import Button, { BUTTON_VARIANTS, type ButtonVariant } from './Button' + +export { Button, BUTTON_VARIANTS, type ButtonVariant } +export default Button diff --git a/src/design-system/shims.d.ts b/src/design-system/shims.d.ts new file mode 100644 index 000000000..0a15fbeb1 --- /dev/null +++ b/src/design-system/shims.d.ts @@ -0,0 +1,6 @@ +declare module '*.styles.css' { + import { CSSResult } from 'lit' + + const content: CSSResult + export default content +} diff --git a/src/design-system/styles/common.styles.css b/src/design-system/styles/common.styles.css new file mode 100644 index 000000000..59ea07269 --- /dev/null +++ b/src/design-system/styles/common.styles.css @@ -0,0 +1 @@ +@import "tailwindcss/preflight.css"; diff --git a/src/design-system/styles/variables.css b/src/design-system/styles/variables.css new file mode 100644 index 000000000..837c461d8 --- /dev/null +++ b/src/design-system/styles/variables.css @@ -0,0 +1,21 @@ +:root { + --solid-ui-color-primary: #7c4dff; + --solid-ui-color-tertiary: #083575; + --solid-ui-color-white: #ffffff; + + --solid-ui-color-gray-100: #f3f4f6; + --solid-ui-color-gray-400: #99a1af; + --solid-ui-color-gray-500: #6a7282; + --solid-ui-color-gray-600: #4a5565; + --solid-ui-color-gray-700: #364153; + --solid-ui-color-gray-800: #101828; + + --solid-ui-color-slate-50: #f8fafc; + --solid-ui-color-slate-200: #e2e8f0; + --solid-ui-color-slate-400: #90a1b9; + + --solid-ui-font-size-sm: 0.875rem; /* 14px */ + --solid-ui-font-size-md: 1rem; /* 16px */ + --solid-ui-font-size-lg: 1.125rem; /* 18px */ + --solid-ui-font-size-xl: 1.25rem; /* 20px */ +} diff --git a/src/storybook/index.ts b/src/storybook/index.ts new file mode 100644 index 000000000..1ae9e21c3 --- /dev/null +++ b/src/storybook/index.ts @@ -0,0 +1,31 @@ +import { render, TemplateResult } from 'lit' + +export type ControlOptions = [TLabel, TValue][] + +export type GetStoryArgs = { + [K in keyof T]: T[K] extends { options: ArrayLike } ? TValue : T[K] extends { control: 'text' } ? string : never +} + +function renderStorybook (content: TemplateResult) { + const container = document.createElement('div') + + render(content, container) + + return container +} + +export function defineStoryRender (renderStory: (args: GetStoryArgs) => TemplateResult) { + return (args: GetStoryArgs) => renderStorybook(renderStory(args)) +} + +export function defineControlOptions (options: T) { + return { + control: { + type: 'select', + options: options.map(([label]) => label) as T[number][0][], + }, + resolve (value: T[number][0]) { + return options.find(([label]) => label === value)?.[1] as T[number][1] + } + } +} diff --git a/tsconfig.json b/tsconfig.json index 20ab8849e..c557f221a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -63,7 +63,7 @@ "declarations.d.ts" ] /* List of folders to include type definitions from. */, // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ /* Source Map Options */ @@ -75,6 +75,7 @@ /* Experimental Options */ // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + "useDefineForClassFields": true, }, "include": [ "src/**/*", diff --git a/webpack.config.mjs b/webpack.config.mjs index fb61a25c8..3068850ca 100644 --- a/webpack.config.mjs +++ b/webpack.config.mjs @@ -1,7 +1,14 @@ import path from 'path' import TerserPlugin from 'terser-webpack-plugin' +import Icons from 'unplugin-icons/webpack' import { componentEntries } from './scripts/component-manifest.mjs' +import iconsConfig from './config/icons.mjs' +import postCSSConfig from './config/postcss.mjs' +import { resolvePathsUsingDecorators, litDecoratorsLoaderOptions } from './config/babel.mjs' + +const pathsUsingDecorators = resolvePathsUsingDecorators(process.cwd()) + const externalsBase = { fs: 'null', 'node-fetch': 'fetch', @@ -57,13 +64,27 @@ const common = { rules: [ { test: /\.(mjs|js|ts)$/, - exclude: /(node_modules|bower_components|dist)/, + include: pathsUsingDecorators, + use: { + loader: 'babel-loader', + options: litDecoratorsLoaderOptions + } + }, { + test: /\.(mjs|js|ts)$/, + exclude: [ + /(node_modules|bower_components|dist)/, + ...pathsUsingDecorators + ], use: { loader: 'babel-loader', options: { cacheDirectory: true } } + }, { + test: /\.styles\.css$/, + loader: 'lit-css-loader', + options: postCSSConfig }, { test: /\.sparql$/i, type: 'asset/source' @@ -71,7 +92,10 @@ const common = { test: /\.ttl$/i, type: 'asset/source' }] - } + }, + plugins: [ + Icons(iconsConfig) + ] } // UMD Minified, rdflib external From 3265bb10cc9d91fe52140f65b4aa9a2d45afa1d5 Mon Sep 17 00:00:00 2001 From: Noel De Martin Date: Fri, 29 May 2026 08:11:05 +0200 Subject: [PATCH 02/11] #769 Implement Web Components Auth system This has been implemented as a swappable interface to support other environments easily, and particularly in order to eventually converge on an interoperable system for all Web Component libraries in the Solid ecosystem --- .gitignore | 1 - config/babel.mjs | 2 +- package-lock.json | 10 +++++++ package.json | 1 + .../components/guard/Guard.stories.ts | 27 +++++++++++++++++ src/primitives/components/guard/Guard.ts | 24 +++++++++++++++ src/primitives/components/guard/index.ts | 4 +++ src/primitives/lib/WebComponent.ts | 7 +++++ src/primitives/lib/auth/Account.ts | 9 ++++++ src/primitives/lib/auth/NoopAuth.ts | 8 +++++ src/primitives/lib/auth/context.ts | 10 +++++++ src/storybook/auth/StorybookAuth.ts | 6 ++++ src/storybook/components/StorybookProvider.ts | 30 +++++++++++++++++++ src/storybook/index.ts | 21 +++++++++++-- src/storybook/stubs.ts | 18 +++++++++++ src/utils/objects.ts | 6 ++++ 16 files changed, 179 insertions(+), 5 deletions(-) create mode 100644 src/primitives/components/guard/Guard.stories.ts create mode 100644 src/primitives/components/guard/Guard.ts create mode 100644 src/primitives/components/guard/index.ts create mode 100644 src/primitives/lib/WebComponent.ts create mode 100644 src/primitives/lib/auth/Account.ts create mode 100644 src/primitives/lib/auth/NoopAuth.ts create mode 100644 src/primitives/lib/auth/context.ts create mode 100644 src/storybook/auth/StorybookAuth.ts create mode 100644 src/storybook/components/StorybookProvider.ts create mode 100644 src/storybook/stubs.ts create mode 100644 src/utils/objects.ts diff --git a/.gitignore b/.gitignore index 9b1a4436b..92168dbd6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ node_modules dist -lib src/versionInfo.ts .idea .vscode diff --git a/config/babel.mjs b/config/babel.mjs index d35434f32..16823dcac 100644 --- a/config/babel.mjs +++ b/config/babel.mjs @@ -6,7 +6,7 @@ import path from 'path' * @see https://lit.dev/docs/components/decorators/#using-decorators-with-babel */ -const pathsUsingDecorators = ['src/design-system'] +const pathsUsingDecorators = ['src/design-system', 'src/primitives', 'src/storybook'] export const litDecoratorsLoaderOptions = { cacheDirectory: true, diff --git a/package-lock.json b/package-lock.json index 2482751cd..ef1c7aa10 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "3.1.1", "license": "MIT", "dependencies": { + "@lit/context": "^1.1.6", "@noble/curves": "^2.2.0", "@noble/hashes": "^2.2.0", "escape-html": "^1.0.3", @@ -3997,6 +3998,15 @@ "integrity": "sha512-Aou5UdlSpr5whQe8AA/bZG0jMj96CoJIWbGfZ91qieWu5AWUMKw8VR/pAkQkJYvBNhmCcWnZlyyk5oze8JIqYA==", "license": "BSD-3-Clause" }, + "node_modules/@lit/context": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@lit/context/-/context-1.1.6.tgz", + "integrity": "sha512-M26qDE6UkQbZA2mQ3RjJ3Gzd8TxP+/0obMgE5HfkfLhEEyYE3Bui4A5XHiGPjy0MUGAyxB3QgVuw2ciS0kHn6A==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit/reactive-element": "^1.6.2 || ^2.1.0" + } + }, "node_modules/@lit/reactive-element": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.1.2.tgz", diff --git a/package.json b/package.json index 43183fdf6..8761cc894 100644 --- a/package.json +++ b/package.json @@ -158,6 +158,7 @@ }, "homepage": "https://github.com/SolidOS/solid-ui", "dependencies": { + "@lit/context": "^1.1.6", "@noble/curves": "^2.2.0", "@noble/hashes": "^2.2.0", "escape-html": "^1.0.3", diff --git a/src/primitives/components/guard/Guard.stories.ts b/src/primitives/components/guard/Guard.stories.ts new file mode 100644 index 000000000..dba112523 --- /dev/null +++ b/src/primitives/components/guard/Guard.stories.ts @@ -0,0 +1,27 @@ +import { html } from 'lit' +import { defineAuthStoryRender } from '../../../storybook' + +import './Guard' +import { USER_OPTIONS } from '../../../storybook/stubs' + +const meta = { + title: 'Primitives/Guard', + args: { + user: 'Alice', + }, + argTypes: { + user: USER_OPTIONS.control, + } +} as const + +const render = defineAuthStoryRender(() => html` + + Guest content + Logged in content + +`) + +export const Primary = { render } +export const Guest = { render, args: { user: 'Guest' } } + +export default meta diff --git a/src/primitives/components/guard/Guard.ts b/src/primitives/components/guard/Guard.ts new file mode 100644 index 000000000..2a540f2fb --- /dev/null +++ b/src/primitives/components/guard/Guard.ts @@ -0,0 +1,24 @@ +import { consume } from '@lit/context' +import { html } from 'lit' +import { customElement } from 'lit/decorators.js' + +import WebComponent from '../../lib/WebComponent' +import { authContext, AuthContext, DEFAULT_AUTH_CONTEXT } from '../../lib/auth/context' + +@customElement('solid-guard') +export default class Guard extends WebComponent { + @consume({ context: authContext, subscribe: true }) + private accessor auth: AuthContext = DEFAULT_AUTH_CONTEXT + + protected render () { + if (!this.auth.account) { + return html` + + ` + } + + return html` + + ` + } +} diff --git a/src/primitives/components/guard/index.ts b/src/primitives/components/guard/index.ts new file mode 100644 index 000000000..209195aef --- /dev/null +++ b/src/primitives/components/guard/index.ts @@ -0,0 +1,4 @@ +import Guard from './Guard' + +export { Guard } +export default Guard diff --git a/src/primitives/lib/WebComponent.ts b/src/primitives/lib/WebComponent.ts new file mode 100644 index 000000000..20746bf3b --- /dev/null +++ b/src/primitives/lib/WebComponent.ts @@ -0,0 +1,7 @@ +import { html, LitElement } from 'lit' + +export default abstract class WebComponent extends LitElement { + protected render () { + return html`` + } +} diff --git a/src/primitives/lib/auth/Account.ts b/src/primitives/lib/auth/Account.ts new file mode 100644 index 000000000..16f29fbf2 --- /dev/null +++ b/src/primitives/lib/auth/Account.ts @@ -0,0 +1,9 @@ +export default class Account { + public readonly webId: string + public readonly avatarUrl?: string + + constructor (webId: string, avatarUrl?: string) { + this.webId = webId + this.avatarUrl = avatarUrl + } +} diff --git a/src/primitives/lib/auth/NoopAuth.ts b/src/primitives/lib/auth/NoopAuth.ts new file mode 100644 index 000000000..73ef3ec23 --- /dev/null +++ b/src/primitives/lib/auth/NoopAuth.ts @@ -0,0 +1,8 @@ +import { AuthContext } from './context' +import Account from './Account' + +export default class NoopAuth implements AuthContext { + get account (): Account | null { + throw new Error('Can\'t use auth, missing context provider') + } +} diff --git a/src/primitives/lib/auth/context.ts b/src/primitives/lib/auth/context.ts new file mode 100644 index 000000000..a90758622 --- /dev/null +++ b/src/primitives/lib/auth/context.ts @@ -0,0 +1,10 @@ +import { createContext } from '@lit/context' +import NoopAuth from './NoopAuth' +import Account from './Account' + +export interface AuthContext { + account: Account | null; +} + +export const DEFAULT_AUTH_CONTEXT = new NoopAuth() +export const authContext = createContext(Symbol('auth')) diff --git a/src/storybook/auth/StorybookAuth.ts b/src/storybook/auth/StorybookAuth.ts new file mode 100644 index 000000000..d877140b6 --- /dev/null +++ b/src/storybook/auth/StorybookAuth.ts @@ -0,0 +1,6 @@ +import { AuthContext } from '../../primitives/lib/auth/context' +import Account from '../../primitives/lib/auth/Account' + +export default class StorybookAuth implements AuthContext { + public account: Account | null = null +} diff --git a/src/storybook/components/StorybookProvider.ts b/src/storybook/components/StorybookProvider.ts new file mode 100644 index 000000000..5e41a9669 --- /dev/null +++ b/src/storybook/components/StorybookProvider.ts @@ -0,0 +1,30 @@ +import { provide } from '@lit/context' +import { customElement, property } from 'lit/decorators.js' +import StorybookAuth from '../auth/StorybookAuth' +import Account from '../../primitives/lib/auth/Account' +import { authContext } from '../../primitives/lib/auth/context' +import WebComponent from '../../primitives/lib/WebComponent' + +@customElement('storybook-provider') +export class StorybookProvider extends WebComponent { + @property({ type: String, reflect: true }) + accessor webId: string | undefined + + @property({ type: String, reflect: true }) + accessor avatarUrl: string | undefined + + @provide({ context: authContext }) + private accessor auth = new StorybookAuth() + + willUpdate (changedProperties: Map) { + super.willUpdate(changedProperties) + + if (!this.webId) { + this.auth.account = null + + return + } + + this.auth.account = new Account(this.webId, this.avatarUrl) + } +} diff --git a/src/storybook/index.ts b/src/storybook/index.ts index 1ae9e21c3..c1fa21033 100644 --- a/src/storybook/index.ts +++ b/src/storybook/index.ts @@ -1,4 +1,7 @@ -import { render, TemplateResult } from 'lit' +import { html, render, TemplateResult } from 'lit' +import { USER_OPTIONS, users } from './stubs' + +import './components/StorybookProvider' export type ControlOptions = [TLabel, TValue][] @@ -6,10 +9,14 @@ export type GetStoryArgs = { [K in keyof T]: T[K] extends { options: ArrayLike } ? TValue : T[K] extends { control: 'text' } ? string : never } -function renderStorybook (content: TemplateResult) { +function renderStorybook (content: TemplateResult, user: ReturnType = null) { const container = document.createElement('div') - render(content, container) + render(html` + + ${content} + + `, container) return container } @@ -18,6 +25,14 @@ export function defineStoryRender (renderStory: (args: GetStor return (args: GetStoryArgs) => renderStorybook(renderStory(args)) } +export function defineAuthStoryRender (renderStory: (args: GetStoryArgs) => TemplateResult) { + return (args: GetStoryArgs) => { + const resolvedUser = USER_OPTIONS.resolve(args.user as keyof typeof users) + + return renderStorybook(renderStory(args), resolvedUser) + } +} + export function defineControlOptions (options: T) { return { control: { diff --git a/src/storybook/stubs.ts b/src/storybook/stubs.ts new file mode 100644 index 000000000..89bb914c4 --- /dev/null +++ b/src/storybook/stubs.ts @@ -0,0 +1,18 @@ +import { defineControlOptions } from '.' +import { objectEntries } from '../utils/objects' + +export const users = { + Alice: { + webId: 'https://alice.example/profile/card#me', + avatarUrl: 'https://placecats.com/300/200', + }, + Bob: { + webId: 'https://bob.example/profile/card#me', + avatarUrl: undefined, + }, +} as const + +export const USER_OPTIONS = defineControlOptions([ + ...objectEntries(users).map(([label, value]) => [label, value]) as [keyof typeof users | 'Guest', typeof users[keyof typeof users] | null][], + ['Guest', null], +]) diff --git a/src/utils/objects.ts b/src/utils/objects.ts new file mode 100644 index 000000000..90b9679b0 --- /dev/null +++ b/src/utils/objects.ts @@ -0,0 +1,6 @@ +export type GetObjectKeys = T extends Record ? Key : keyof T +export type GetObjectValues = T extends Record ? Value : never + +export const objectEntries = Object.entries.bind(Object) as [0]>( + obj: T +) => [GetObjectKeys, GetObjectValues][] From 1e1b8455195966aa223100c6264f24a078c99c45 Mon Sep 17 00:00:00 2001 From: Noel De Martin Date: Fri, 29 May 2026 08:24:31 +0200 Subject: [PATCH 03/11] #769 Implement action auth components --- .storybook/preview.js | 1 + package.json | 4 ++- .../login-button/LoginButton.stories.ts | 35 +++++++++++++++++++ .../login-button/LoginButton.styles.css | 1 + .../components/login-button/LoginButton.ts | 33 +++++++++++++++++ .../components/login-button/index.ts | 4 +++ .../logout-button/LogoutButton.stories.ts | 35 +++++++++++++++++++ .../logout-button/LogoutButton.styles.css | 1 + .../components/logout-button/LogoutButton.ts | 33 +++++++++++++++++ .../components/logout-button/index.ts | 4 +++ .../signup-button/SignupButton.stories.ts | 35 +++++++++++++++++++ .../signup-button/SignupButton.styles.css | 1 + .../components/signup-button/SignupButton.ts | 33 +++++++++++++++++ .../components/signup-button/index.ts | 4 +++ src/primitives/lib/WebComponent.ts | 30 ++++++++++++++++ src/primitives/lib/auth/NoopAuth.ts | 12 +++++++ src/primitives/lib/auth/context.ts | 3 ++ src/primitives/styles/common.styles.css | 17 +++++++++ src/primitives/styles/variables.css | 7 ++++ src/storybook/auth/StorybookAuth.ts | 12 +++++++ 20 files changed, 304 insertions(+), 1 deletion(-) create mode 100644 src/primitives/components/login-button/LoginButton.stories.ts create mode 100644 src/primitives/components/login-button/LoginButton.styles.css create mode 100644 src/primitives/components/login-button/LoginButton.ts create mode 100644 src/primitives/components/login-button/index.ts create mode 100644 src/primitives/components/logout-button/LogoutButton.stories.ts create mode 100644 src/primitives/components/logout-button/LogoutButton.styles.css create mode 100644 src/primitives/components/logout-button/LogoutButton.ts create mode 100644 src/primitives/components/logout-button/index.ts create mode 100644 src/primitives/components/signup-button/SignupButton.stories.ts create mode 100644 src/primitives/components/signup-button/SignupButton.styles.css create mode 100644 src/primitives/components/signup-button/SignupButton.ts create mode 100644 src/primitives/components/signup-button/index.ts create mode 100644 src/primitives/styles/common.styles.css create mode 100644 src/primitives/styles/variables.css diff --git a/.storybook/preview.js b/.storybook/preview.js index 657e06130..1704e2a63 100644 --- a/.storybook/preview.js +++ b/.storybook/preview.js @@ -1,4 +1,5 @@ import '../src/design-system/styles/variables.css' +import '../src/primitives/styles/variables.css' // For backward compatibility, provide rdflib and solid-logic as globals import * as rdflib from 'rdflib' diff --git a/package.json b/package.json index 8761cc894..506c5c0ca 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "require": "./dist/solid-ui.js" }, "./design-system.css": "./src/design-system/styles/variables.css", + "./primitives.css": "./src/primitives/styles/variables.css", "./components/header": { "types": "./dist/components/header/index.d.ts", "import": "./dist/components/header/index.esm.js", @@ -101,7 +102,8 @@ "dist/", "README.md", "LICENSE", - "src/design-system/styles/variables.css" + "src/design-system/styles/variables.css", + "src/primitives/styles/variables.css" ], "scripts": { "clean": "rm -rf ./dist ./src/versionInfo.ts ./docs/api .tsbuildinfo", diff --git a/src/primitives/components/login-button/LoginButton.stories.ts b/src/primitives/components/login-button/LoginButton.stories.ts new file mode 100644 index 000000000..72fdc9326 --- /dev/null +++ b/src/primitives/components/login-button/LoginButton.stories.ts @@ -0,0 +1,35 @@ +import { html } from 'lit' +import { defineStoryRender } from '../../../storybook' + +import '~icons/lucide/log-in' + +import './LoginButton' + +const meta = { + title: 'Primitives/LoginButton', +} as const + +export const Primary = { + render: defineStoryRender(() => html``) +} + +export const CustomLabel = { + render: defineStoryRender(() => html` + + Sign In + + + `) +} + +export const CustomTrigger = { + render: defineStoryRender(() => html` + + + + `) +} + +export default meta diff --git a/src/primitives/components/login-button/LoginButton.styles.css b/src/primitives/components/login-button/LoginButton.styles.css new file mode 100644 index 000000000..328931896 --- /dev/null +++ b/src/primitives/components/login-button/LoginButton.styles.css @@ -0,0 +1 @@ +@import '../../styles/common.styles.css'; diff --git a/src/primitives/components/login-button/LoginButton.ts b/src/primitives/components/login-button/LoginButton.ts new file mode 100644 index 000000000..e28e828e5 --- /dev/null +++ b/src/primitives/components/login-button/LoginButton.ts @@ -0,0 +1,33 @@ +import { consume } from '@lit/context' +import { html } from 'lit' +import { customElement } from 'lit/decorators.js' + +import WebComponent from '../../lib/WebComponent' +import { authContext, AuthContext, DEFAULT_AUTH_CONTEXT } from '../../lib/auth/context' +import styles from './LoginButton.styles.css' + +@customElement('solid-login-button') +export default class LoginButton extends WebComponent { + static styles = styles + + @consume({ context: authContext, subscribe: true }) + private accessor auth: AuthContext = DEFAULT_AUTH_CONTEXT + + protected render () { + return html` + + + + ` + } + + private onClick (e: MouseEvent) { + e.preventDefault() + + this.auth.login() + } +} diff --git a/src/primitives/components/login-button/index.ts b/src/primitives/components/login-button/index.ts new file mode 100644 index 000000000..7e58a4a2b --- /dev/null +++ b/src/primitives/components/login-button/index.ts @@ -0,0 +1,4 @@ +import LoginButton from './LoginButton' + +export { LoginButton } +export default LoginButton diff --git a/src/primitives/components/logout-button/LogoutButton.stories.ts b/src/primitives/components/logout-button/LogoutButton.stories.ts new file mode 100644 index 000000000..3b58c81ed --- /dev/null +++ b/src/primitives/components/logout-button/LogoutButton.stories.ts @@ -0,0 +1,35 @@ +import { html } from 'lit' +import { defineStoryRender } from '../../../storybook' + +import '~icons/lucide/log-out' + +import './LogoutButton' + +const meta = { + title: 'Primitives/LogoutButton', +} as const + +export const Primary = { + render: defineStoryRender(() => html``) +} + +export const CustomLabel = { + render: defineStoryRender(() => html` + + Sign Out + + + `) +} + +export const CustomTrigger = { + render: defineStoryRender(() => html` + + + + `) +} + +export default meta diff --git a/src/primitives/components/logout-button/LogoutButton.styles.css b/src/primitives/components/logout-button/LogoutButton.styles.css new file mode 100644 index 000000000..328931896 --- /dev/null +++ b/src/primitives/components/logout-button/LogoutButton.styles.css @@ -0,0 +1 @@ +@import '../../styles/common.styles.css'; diff --git a/src/primitives/components/logout-button/LogoutButton.ts b/src/primitives/components/logout-button/LogoutButton.ts new file mode 100644 index 000000000..ac8208ec0 --- /dev/null +++ b/src/primitives/components/logout-button/LogoutButton.ts @@ -0,0 +1,33 @@ +import { consume } from '@lit/context' +import { html } from 'lit' +import { customElement } from 'lit/decorators.js' + +import WebComponent from '../../lib/WebComponent' +import { authContext, AuthContext, DEFAULT_AUTH_CONTEXT } from '../../lib/auth/context' +import styles from './LogoutButton.styles.css' + +@customElement('solid-logout-button') +export default class LogoutButton extends WebComponent { + static styles = styles + + @consume({ context: authContext, subscribe: true }) + private accessor auth: AuthContext = DEFAULT_AUTH_CONTEXT + + protected render () { + return html` + + + + ` + } + + private onClick (e: MouseEvent) { + e.preventDefault() + + this.auth.logout() + } +} diff --git a/src/primitives/components/logout-button/index.ts b/src/primitives/components/logout-button/index.ts new file mode 100644 index 000000000..62ce9f736 --- /dev/null +++ b/src/primitives/components/logout-button/index.ts @@ -0,0 +1,4 @@ +import LogoutButton from './LogoutButton' + +export { LogoutButton } +export default LogoutButton diff --git a/src/primitives/components/signup-button/SignupButton.stories.ts b/src/primitives/components/signup-button/SignupButton.stories.ts new file mode 100644 index 000000000..4f684814c --- /dev/null +++ b/src/primitives/components/signup-button/SignupButton.stories.ts @@ -0,0 +1,35 @@ +import { html } from 'lit' +import { defineStoryRender } from '../../../storybook' + +import '~icons/lucide/user-plus' + +import './SignupButton' + +const meta = { + title: 'Primitives/SignupButton', +} as const + +export const Primary = { + render: defineStoryRender(() => html``) +} + +export const CustomLabel = { + render: defineStoryRender(() => html` + + Register + + + `) +} + +export const CustomTrigger = { + render: defineStoryRender(() => html` + + + + `) +} + +export default meta diff --git a/src/primitives/components/signup-button/SignupButton.styles.css b/src/primitives/components/signup-button/SignupButton.styles.css new file mode 100644 index 000000000..328931896 --- /dev/null +++ b/src/primitives/components/signup-button/SignupButton.styles.css @@ -0,0 +1 @@ +@import '../../styles/common.styles.css'; diff --git a/src/primitives/components/signup-button/SignupButton.ts b/src/primitives/components/signup-button/SignupButton.ts new file mode 100644 index 000000000..e1befa16c --- /dev/null +++ b/src/primitives/components/signup-button/SignupButton.ts @@ -0,0 +1,33 @@ +import { consume } from '@lit/context' +import { html } from 'lit' +import { customElement } from 'lit/decorators.js' + +import WebComponent from '../../lib/WebComponent' +import { authContext, AuthContext, DEFAULT_AUTH_CONTEXT } from '../../lib/auth/context' +import styles from './SignupButton.styles.css' + +@customElement('solid-signup-button') +export default class SignupButton extends WebComponent { + static styles = styles + + @consume({ context: authContext, subscribe: true }) + private accessor auth: AuthContext = DEFAULT_AUTH_CONTEXT + + protected render () { + return html` + + + + ` + } + + private onClick (e: MouseEvent) { + e.preventDefault() + + this.auth.signup() + } +} diff --git a/src/primitives/components/signup-button/index.ts b/src/primitives/components/signup-button/index.ts new file mode 100644 index 000000000..b7a47192d --- /dev/null +++ b/src/primitives/components/signup-button/index.ts @@ -0,0 +1,4 @@ +import SignupButton from './SignupButton' + +export { SignupButton } +export default SignupButton diff --git a/src/primitives/lib/WebComponent.ts b/src/primitives/lib/WebComponent.ts index 20746bf3b..67ab98b6d 100644 --- a/src/primitives/lib/WebComponent.ts +++ b/src/primitives/lib/WebComponent.ts @@ -1,7 +1,37 @@ import { html, LitElement } from 'lit' export default abstract class WebComponent extends LitElement { + static states?: Record + + protected internals?: ElementInternals + + protected willUpdate (changedProperties: Map) { + super.willUpdate(changedProperties) + + const states = this.static().states + + if (!states) { + return + } + + this.internals ??= this.attachInternals() + + for (const [state, condition] of Object.entries(states)) { + const matches = condition(this) + + if (matches && !this.internals.states.has(state)) { + this.internals.states.add(state) + } else if (!matches && this.internals.states.has(state)) { + this.internals.states.delete(state) + } + } + } + protected render () { return html`` } + + private static (): typeof WebComponent { + return this.constructor as typeof WebComponent + } } diff --git a/src/primitives/lib/auth/NoopAuth.ts b/src/primitives/lib/auth/NoopAuth.ts index 73ef3ec23..01c78a524 100644 --- a/src/primitives/lib/auth/NoopAuth.ts +++ b/src/primitives/lib/auth/NoopAuth.ts @@ -5,4 +5,16 @@ export default class NoopAuth implements AuthContext { get account (): Account | null { throw new Error('Can\'t use auth, missing context provider') } + + async login () { + throw new Error('Can\'t use auth, missing context provider') + } + + async signup () { + throw new Error('Can\'t use auth, missing context provider') + } + + async logout () { + throw new Error('Can\'t use auth, missing context provider') + } } diff --git a/src/primitives/lib/auth/context.ts b/src/primitives/lib/auth/context.ts index a90758622..4f5eb9f8b 100644 --- a/src/primitives/lib/auth/context.ts +++ b/src/primitives/lib/auth/context.ts @@ -4,6 +4,9 @@ import Account from './Account' export interface AuthContext { account: Account | null; + login(loginUrl?: string): Promise; + signup(): Promise; + logout(): Promise; } export const DEFAULT_AUTH_CONTEXT = new NoopAuth() diff --git a/src/primitives/styles/common.styles.css b/src/primitives/styles/common.styles.css new file mode 100644 index 000000000..454db61a0 --- /dev/null +++ b/src/primitives/styles/common.styles.css @@ -0,0 +1,17 @@ +@import 'tailwindcss/preflight.css'; + +button { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 10px 20px; + border-radius: 5px; + gap: 5px; + background: var(--solid-color-primary); + color: var(--solid-color-primary-foreground); + + &:hover { + background: var(--solid-color-primary-hover); + border-color: var(--solid-color-primary-hover); + } +} diff --git a/src/primitives/styles/variables.css b/src/primitives/styles/variables.css new file mode 100644 index 000000000..96e061b4c --- /dev/null +++ b/src/primitives/styles/variables.css @@ -0,0 +1,7 @@ +:root { + --solid-color-gray-100: #f8fafc; + --solid-color-gray-200: #cbd5e1; + --solid-color-primary: #7c4dff; + --solid-color-primary-foreground: #ffffff; + --solid-color-primary-hover: #6a3de8; +} diff --git a/src/storybook/auth/StorybookAuth.ts b/src/storybook/auth/StorybookAuth.ts index d877140b6..a684e2e48 100644 --- a/src/storybook/auth/StorybookAuth.ts +++ b/src/storybook/auth/StorybookAuth.ts @@ -3,4 +3,16 @@ import Account from '../../primitives/lib/auth/Account' export default class StorybookAuth implements AuthContext { public account: Account | null = null + + async login (loginUrl?: string) { + alert(loginUrl ? `Log in to ${loginUrl}!` : 'Log in!') + } + + async signup () { + alert('Sign up!') + } + + async logout () { + alert('Log out!') + } } From 9779714ad33923da82de150517a8e77c662cb52e Mon Sep 17 00:00:00 2001 From: Noel De Martin Date: Fri, 29 May 2026 08:47:15 +0200 Subject: [PATCH 04/11] #769 Soften CSS resets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prompt: We're using the tailwindcss preflight resets in @src/primitives/styles/common.styles.css, but they have an overly aggressive selector that resets the margin and padding of any element. This is a problem because styling web component elements using :host in their own styles take less precedence in the situations were nested components are rendered. Use the patch-package utility to modify the built-in preflight and remove the padding and margin resets in the * selector. Instead, create a new rule that only resets padding and margin for built-in elements. Summary of Changes: 1. Modified Preflight Styles: Updated preflight.css to: • Remove margin: 0; and padding: 0; from the universal * selector rule. • Create a new rule resetting margins and paddings only for standard built-in HTML tags and pseudo-elements (such as ::after , ::before , ::backdrop , ::file-selector-button ). 2. Generated Patch: Ran npx patch-package tailwindcss to generate the patch file at tailwindcss+4.3.0.patch. 3. Staged and Verified: • Verified that npm run typecheck && npm run build and npm run test compile and pass without issues. • Staged the patch file along with the formatting tweak in package.json. Co-Authored-By: Antigravity CLI (Gemini 3.5 Flash) --- package-lock.json | 175 ++++++++++++++++++++++++++++++++ package.json | 6 +- patches/tailwindcss+4.3.0.patch | 136 +++++++++++++++++++++++++ 3 files changed, 315 insertions(+), 2 deletions(-) create mode 100644 patches/tailwindcss+4.3.0.patch diff --git a/package-lock.json b/package-lock.json index ef1c7aa10..e8d213c6a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "": { "name": "solid-ui", "version": "3.1.1", + "hasInstallScript": true, "license": "MIT", "dependencies": { "@lit/context": "^1.1.6", @@ -65,6 +66,7 @@ "lit-css-loader": "^4.0.1", "neostandard": "^0.13.0", "nock": "^15.0.0", + "patch-package": "^8.0.1", "postcss": "^8.5.15", "react": "^17.0.2", "react-dom": "^17.0.2", @@ -6688,6 +6690,13 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true, + "license": "BSD-2-Clause" + }, "node_modules/abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -9900,6 +9909,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/find-yarn-workspace-root": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz", + "integrity": "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "micromatch": "^4.0.2" + } + }, "node_modules/flat": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", @@ -13469,6 +13488,26 @@ "dev": true, "license": "MIT" }, + "node_modules/json-stable-stringify": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.3.0.tgz", + "integrity": "sha512-qtYiSSFlwot9XHtF9bD9c7rwKjr+RecWT//ZnPvSmEjpV5mmPOCN4j8UjY5hbjNkOwZ/jQv3J6R1/pL7RwgMsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "isarray": "^2.0.5", + "jsonify": "^0.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", @@ -13509,6 +13548,16 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", + "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", + "dev": true, + "license": "Public Domain", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/jsonld": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/jsonld/-/jsonld-9.0.0.tgz", @@ -13591,6 +13640,16 @@ "node": ">=0.10.0" } }, + "node_modules/klaw-sync": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", + "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.11" + } + }, "node_modules/ky": { "version": "1.14.3", "resolved": "https://registry.npmjs.org/ky/-/ky-1.14.3.tgz", @@ -15093,6 +15152,20 @@ ], "license": "MIT" }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/mime-db": { "version": "1.54.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", @@ -16094,6 +16167,108 @@ "tslib": "^2.0.3" } }, + "node_modules/patch-package": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.1.tgz", + "integrity": "sha512-VsKRIA8f5uqHQ7NGhwIna6Bx6D9s/1iXlA1hthBVBEbkq+t4kXD0HHt+rJhf/Z+Ci0F/HCB2hvn0qLdLG+Qxlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@yarnpkg/lockfile": "^1.1.0", + "chalk": "^4.1.2", + "ci-info": "^3.7.0", + "cross-spawn": "^7.0.3", + "find-yarn-workspace-root": "^2.0.0", + "fs-extra": "^10.0.0", + "json-stable-stringify": "^1.0.2", + "klaw-sync": "^6.0.0", + "minimist": "^1.2.6", + "open": "^7.4.2", + "semver": "^7.5.3", + "slash": "^2.0.0", + "tmp": "^0.2.4", + "yaml": "^2.2.2" + }, + "bin": { + "patch-package": "index.js" + }, + "engines": { + "node": ">=14", + "npm": ">5" + } + }, + "node_modules/patch-package/node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/patch-package/node_modules/open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/patch-package/node_modules/semver": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", + "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/patch-package/node_modules/tmp": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz", + "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/patch-package/node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, "node_modules/path-browserify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", diff --git a/package.json b/package.json index 506c5c0ca..ada87a1b2 100644 --- a/package.json +++ b/package.json @@ -103,7 +103,7 @@ "README.md", "LICENSE", "src/design-system/styles/variables.css", - "src/primitives/styles/variables.css" + "src/primitives/styles/variables.css" ], "scripts": { "clean": "rm -rf ./dist ./src/versionInfo.ts ./docs/api .tsbuildinfo", @@ -133,7 +133,8 @@ "preversion": "npm run sync-component-exports && npm run lint && npm run typecheck && npm test", "postpublish": "git push origin main --follow-tags", "storybook": "storybook dev -p 6006", - "build-storybook": "storybook build --output-dir ./examples/storybook" + "build-storybook": "storybook build --output-dir ./examples/storybook", + "postinstall": "patch-package" }, "repository": { "type": "git", @@ -220,6 +221,7 @@ "lit-css-loader": "^4.0.1", "neostandard": "^0.13.0", "nock": "^15.0.0", + "patch-package": "^8.0.1", "postcss": "^8.5.15", "react": "^17.0.2", "react-dom": "^17.0.2", diff --git a/patches/tailwindcss+4.3.0.patch b/patches/tailwindcss+4.3.0.patch new file mode 100644 index 000000000..cff5694f0 --- /dev/null +++ b/patches/tailwindcss+4.3.0.patch @@ -0,0 +1,136 @@ +diff --git a/node_modules/tailwindcss/preflight.css b/node_modules/tailwindcss/preflight.css +index 753e79e..1279492 100644 +--- a/node_modules/tailwindcss/preflight.css ++++ b/node_modules/tailwindcss/preflight.css +@@ -10,11 +10,129 @@ + ::backdrop, + ::file-selector-button { + box-sizing: border-box; /* 1 */ +- margin: 0; /* 2 */ +- padding: 0; /* 2 */ + border: 0 solid; /* 3 */ + } + ++a, ++abbr, ++address, ++area, ++article, ++aside, ++audio, ++b, ++base, ++bdi, ++bdo, ++blockquote, ++body, ++br, ++button, ++canvas, ++caption, ++cite, ++code, ++col, ++colgroup, ++data, ++datalist, ++dd, ++del, ++details, ++dfn, ++dialog, ++div, ++dl, ++dt, ++em, ++embed, ++fieldset, ++figcaption, ++figure, ++footer, ++form, ++h1, ++h2, ++h3, ++h4, ++h5, ++h6, ++head, ++header, ++hgroup, ++hr, ++html, ++i, ++iframe, ++img, ++input, ++ins, ++kbd, ++label, ++legend, ++li, ++link, ++main, ++map, ++mark, ++menu, ++meta, ++meter, ++nav, ++noscript, ++object, ++ol, ++optgroup, ++option, ++output, ++p, ++picture, ++pre, ++progress, ++q, ++rp, ++rt, ++ruby, ++s, ++samp, ++script, ++search, ++section, ++select, ++slot, ++small, ++source, ++span, ++strong, ++style, ++sub, ++summary, ++sup, ++table, ++tbody, ++td, ++template, ++textarea, ++tfoot, ++th, ++thead, ++time, ++title, ++tr, ++track, ++u, ++ul, ++var, ++video, ++wbr, ++::after, ++::before, ++::backdrop, ++::file-selector-button { ++ margin: 0; ++ padding: 0; ++} ++ + /* + 1. Use a consistent sensible line-height in all browsers. + 2. Prevent adjustments of font size after orientation changes in iOS. From f573f2fdc5989e6539d601220ebe72aed81fd46d Mon Sep 17 00:00:00 2001 From: Noel De Martin Date: Fri, 29 May 2026 09:00:23 +0200 Subject: [PATCH 05/11] #769 Dialog System Implement a generic dialog system, see https://noeldemartin.com/blog/the-problems-with-modals-and-how-to-solve-them --- .../dialog-provider/DialogProvider.ts | 21 +++++++++ .../components/dialog-provider/index.ts | 4 ++ .../components/dialog/Dialog.styles.css | 12 ++++++ src/design-system/components/dialog/Dialog.ts | 43 +++++++++++++++++++ src/design-system/components/dialog/index.ts | 4 ++ .../components/dialogs-root/DialogsRoot.ts | 34 +++++++++++++++ .../components/dialogs-root/index.ts | 4 ++ src/design-system/lib/dialogs/Dialog.ts | 12 ++++++ src/design-system/lib/dialogs/context.ts | 7 +++ .../lib/dialogs/events/close-dialog.ts | 15 +++++++ .../lib/dialogs/events/show-dialog.ts | 17 ++++++++ src/design-system/lib/dialogs/index.ts | 7 +++ src/design-system/lib/ids.ts | 3 ++ src/design-system/styles/common.styles.css | 12 ++++++ src/storybook/components/StorybookProvider.ts | 10 +++++ 15 files changed, 205 insertions(+) create mode 100644 src/design-system/components/dialog-provider/DialogProvider.ts create mode 100644 src/design-system/components/dialog-provider/index.ts create mode 100644 src/design-system/components/dialog/Dialog.styles.css create mode 100644 src/design-system/components/dialog/Dialog.ts create mode 100644 src/design-system/components/dialog/index.ts create mode 100644 src/design-system/components/dialogs-root/DialogsRoot.ts create mode 100644 src/design-system/components/dialogs-root/index.ts create mode 100644 src/design-system/lib/dialogs/Dialog.ts create mode 100644 src/design-system/lib/dialogs/context.ts create mode 100644 src/design-system/lib/dialogs/events/close-dialog.ts create mode 100644 src/design-system/lib/dialogs/events/show-dialog.ts create mode 100644 src/design-system/lib/dialogs/index.ts create mode 100644 src/design-system/lib/ids.ts diff --git a/src/design-system/components/dialog-provider/DialogProvider.ts b/src/design-system/components/dialog-provider/DialogProvider.ts new file mode 100644 index 000000000..83d2ad38c --- /dev/null +++ b/src/design-system/components/dialog-provider/DialogProvider.ts @@ -0,0 +1,21 @@ +import { provide } from '@lit/context' +import { customElement, property } from 'lit/decorators.js' +import WebComponent from '../../../primitives/lib/WebComponent' +import { DialogContext, dialogContext } from '../../lib/dialogs/context' + +@customElement('solid-ui-dialog-provider') +export default class DialogProvider extends WebComponent { + @property({ type: String, reflect: true }) + accessor dialogId: string | undefined + + @provide({ context: dialogContext }) + private accessor dialog: DialogContext = { id: '' } + + protected willUpdate (changedProperties: Map) { + super.willUpdate(changedProperties) + + if (changedProperties.has('dialogId') && this.dialogId) { + this.dialog = { id: this.dialogId } + } + } +} diff --git a/src/design-system/components/dialog-provider/index.ts b/src/design-system/components/dialog-provider/index.ts new file mode 100644 index 000000000..5aac8dfcd --- /dev/null +++ b/src/design-system/components/dialog-provider/index.ts @@ -0,0 +1,4 @@ +import DialogProvider from './DialogProvider' + +export { DialogProvider } +export default DialogProvider diff --git a/src/design-system/components/dialog/Dialog.styles.css b/src/design-system/components/dialog/Dialog.styles.css new file mode 100644 index 000000000..7b3a41ac6 --- /dev/null +++ b/src/design-system/components/dialog/Dialog.styles.css @@ -0,0 +1,12 @@ +@import "../../styles/common.styles.css"; + +dialog { + margin: auto; + border-radius: 5px; + box-shadow: 0px 4px 16px 0px rgba(0, 0, 0, 0.35); + min-width: 343px; +} + +dialog:not([open]) { + display: none; +} diff --git a/src/design-system/components/dialog/Dialog.ts b/src/design-system/components/dialog/Dialog.ts new file mode 100644 index 000000000..354ee2f7d --- /dev/null +++ b/src/design-system/components/dialog/Dialog.ts @@ -0,0 +1,43 @@ +import { consume } from '@lit/context' +import { html } from 'lit' +import { customElement, query } from 'lit/decorators.js' +import WebComponent from '../../../primitives/lib/WebComponent' +import styles from './Dialog.styles.css' +import { DialogContext, dialogContext } from '../../lib/dialogs/context' +import { CloseDialogEvent } from '../../lib/dialogs/events/close-dialog' + +@customElement('solid-ui-dialog') +export default class Dialog extends WebComponent { + static styles = styles + + @query('dialog') + private accessor nativeDialog: HTMLDialogElement | null = null + + @consume({ context: dialogContext, subscribe: true }) + private accessor context!: DialogContext + + public close () { + if (!this.context) { + throw new Error('Dialog context missing') + } + + window.dispatchEvent(new CloseDialogEvent(this.context.id)) + } + + protected firstUpdated () { + if (!this.nativeDialog) { + return + } + + this.nativeDialog.showModal() + this.nativeDialog.addEventListener('close', () => this.close()) + } + + protected render () { + return html` + + + + ` + } +} diff --git a/src/design-system/components/dialog/index.ts b/src/design-system/components/dialog/index.ts new file mode 100644 index 000000000..d83af8e56 --- /dev/null +++ b/src/design-system/components/dialog/index.ts @@ -0,0 +1,4 @@ +import Dialog from './Dialog' + +export { Dialog } +export default Dialog diff --git a/src/design-system/components/dialogs-root/DialogsRoot.ts b/src/design-system/components/dialogs-root/DialogsRoot.ts new file mode 100644 index 000000000..0e700df94 --- /dev/null +++ b/src/design-system/components/dialogs-root/DialogsRoot.ts @@ -0,0 +1,34 @@ +import { html } from 'lit' +import { customElement, state } from 'lit/decorators.js' +import WebComponent from '../../../primitives/lib/WebComponent' +import Dialog from '../../lib/dialogs/Dialog' +import { ShowDialogEvent } from '../../lib/dialogs/events/show-dialog' + +import '../dialog-provider/DialogProvider' +import { CloseDialogEvent } from '../../lib/dialogs/events/close-dialog' + +@customElement('solid-ui-dialogs-root') +export default class DialogsRoot extends WebComponent { + @state() + private accessor dialogs: Dialog[] = [] + + connectedCallback (): void { + super.connectedCallback() + + window.addEventListener(ShowDialogEvent.eventName, (event) => { + event.stopPropagation() + + this.dialogs = [...this.dialogs, event.dialog] + }) + + window.addEventListener(CloseDialogEvent.eventName, (event) => { + event.stopPropagation() + + this.dialogs = this.dialogs.filter(dialog => dialog.id !== event.id) + }) + } + + protected render () { + return html`${this.dialogs.map(dialog => html`${dialog.template}`)}` + } +} diff --git a/src/design-system/components/dialogs-root/index.ts b/src/design-system/components/dialogs-root/index.ts new file mode 100644 index 000000000..d7f134888 --- /dev/null +++ b/src/design-system/components/dialogs-root/index.ts @@ -0,0 +1,4 @@ +import DialogsRoot from './DialogsRoot' + +export { DialogsRoot } +export default DialogsRoot diff --git a/src/design-system/lib/dialogs/Dialog.ts b/src/design-system/lib/dialogs/Dialog.ts new file mode 100644 index 000000000..1972cede3 --- /dev/null +++ b/src/design-system/lib/dialogs/Dialog.ts @@ -0,0 +1,12 @@ +import { TemplateResult } from 'lit' +import { generateId } from '../ids' + +export default class Dialog { + public readonly id: string + public readonly template: TemplateResult + + constructor (template: TemplateResult) { + this.id = generateId() + this.template = template + } +} diff --git a/src/design-system/lib/dialogs/context.ts b/src/design-system/lib/dialogs/context.ts new file mode 100644 index 000000000..5b10f53f3 --- /dev/null +++ b/src/design-system/lib/dialogs/context.ts @@ -0,0 +1,7 @@ +import { createContext } from '@lit/context' + +export interface DialogContext { + readonly id: string +} + +export const dialogContext = createContext(Symbol('dialog')) diff --git a/src/design-system/lib/dialogs/events/close-dialog.ts b/src/design-system/lib/dialogs/events/close-dialog.ts new file mode 100644 index 000000000..07ff43168 --- /dev/null +++ b/src/design-system/lib/dialogs/events/close-dialog.ts @@ -0,0 +1,15 @@ +const EVENT_NAME = 'solid-ui:close-dialog' as const + +export class CloseDialogEvent extends Event { + static readonly eventName = EVENT_NAME + + constructor (public id: string) { + super(CloseDialogEvent.eventName, { bubbles: true, composed: true }) + } +} + +declare global { + interface GlobalEventHandlersEventMap { + [EVENT_NAME]: CloseDialogEvent + } +} diff --git a/src/design-system/lib/dialogs/events/show-dialog.ts b/src/design-system/lib/dialogs/events/show-dialog.ts new file mode 100644 index 000000000..225b5b1a2 --- /dev/null +++ b/src/design-system/lib/dialogs/events/show-dialog.ts @@ -0,0 +1,17 @@ +import Dialog from '../Dialog' + +const EVENT_NAME = 'solid-ui:show-dialog' as const + +export class ShowDialogEvent extends Event { + static readonly eventName = EVENT_NAME + + constructor (public dialog: Dialog) { + super(ShowDialogEvent.eventName, { bubbles: true, composed: true }) + } +} + +declare global { + interface GlobalEventHandlersEventMap { + [EVENT_NAME]: ShowDialogEvent + } +} diff --git a/src/design-system/lib/dialogs/index.ts b/src/design-system/lib/dialogs/index.ts new file mode 100644 index 000000000..b9489fdb4 --- /dev/null +++ b/src/design-system/lib/dialogs/index.ts @@ -0,0 +1,7 @@ +import { TemplateResult } from 'lit' +import { ShowDialogEvent } from './events/show-dialog' +import Dialog from './Dialog' + +export function showDialog (template: TemplateResult) { + document.dispatchEvent(new ShowDialogEvent(new Dialog(template))) +} diff --git a/src/design-system/lib/ids.ts b/src/design-system/lib/ids.ts new file mode 100644 index 000000000..3ffe622f2 --- /dev/null +++ b/src/design-system/lib/ids.ts @@ -0,0 +1,3 @@ +export function generateId () { + return Math.random().toString(36).substring(2, 15) +} diff --git a/src/design-system/styles/common.styles.css b/src/design-system/styles/common.styles.css index 59ea07269..6def71d0d 100644 --- a/src/design-system/styles/common.styles.css +++ b/src/design-system/styles/common.styles.css @@ -1 +1,13 @@ @import "tailwindcss/preflight.css"; + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} diff --git a/src/storybook/components/StorybookProvider.ts b/src/storybook/components/StorybookProvider.ts index 5e41a9669..b305ed727 100644 --- a/src/storybook/components/StorybookProvider.ts +++ b/src/storybook/components/StorybookProvider.ts @@ -1,10 +1,13 @@ import { provide } from '@lit/context' +import { html } from 'lit' import { customElement, property } from 'lit/decorators.js' import StorybookAuth from '../auth/StorybookAuth' import Account from '../../primitives/lib/auth/Account' import { authContext } from '../../primitives/lib/auth/context' import WebComponent from '../../primitives/lib/WebComponent' +import '../../design-system/components/dialogs-root' + @customElement('storybook-provider') export class StorybookProvider extends WebComponent { @property({ type: String, reflect: true }) @@ -27,4 +30,11 @@ export class StorybookProvider extends WebComponent { this.auth.account = new Account(this.webId, this.avatarUrl) } + + protected render () { + return html` + + + ` + } } From c1f6a9f70f2b534d9cbc9cec156b5d6dd12d9b65 Mon Sep 17 00:00:00 2001 From: Noel De Martin Date: Fri, 29 May 2026 09:06:07 +0200 Subject: [PATCH 06/11] #769 Implement Combobox --- .../combobox-option/ComboboxOption.ts | 13 +++ .../components/combobox-option/index.ts | 4 + .../components/combobox/Combobox.stories.ts | 31 ++++++ .../components/combobox/Combobox.styles.css | 68 +++++++++++++ .../components/combobox/Combobox.ts | 99 +++++++++++++++++++ .../components/combobox/index.ts | 4 + .../polyfills/css-anchor-positioning.ts | 8 ++ src/primitives/lib/WebComponent.ts | 16 ++- 8 files changed, 238 insertions(+), 5 deletions(-) create mode 100644 src/design-system/components/combobox-option/ComboboxOption.ts create mode 100644 src/design-system/components/combobox-option/index.ts create mode 100644 src/design-system/components/combobox/Combobox.stories.ts create mode 100644 src/design-system/components/combobox/Combobox.styles.css create mode 100644 src/design-system/components/combobox/Combobox.ts create mode 100644 src/design-system/components/combobox/index.ts create mode 100644 src/design-system/polyfills/css-anchor-positioning.ts diff --git a/src/design-system/components/combobox-option/ComboboxOption.ts b/src/design-system/components/combobox-option/ComboboxOption.ts new file mode 100644 index 000000000..82a619bef --- /dev/null +++ b/src/design-system/components/combobox-option/ComboboxOption.ts @@ -0,0 +1,13 @@ +import { html, nothing } from 'lit' +import { customElement, property } from 'lit/decorators.js' +import WebComponent from '../../../primitives/lib/WebComponent' + +@customElement('solid-ui-combobox-option') +export default class ComboboxOption extends WebComponent { + @property({ type: String, reflect: true }) + accessor value = '' + + protected render () { + return html`${nothing}` + } +} diff --git a/src/design-system/components/combobox-option/index.ts b/src/design-system/components/combobox-option/index.ts new file mode 100644 index 000000000..d1595b9d5 --- /dev/null +++ b/src/design-system/components/combobox-option/index.ts @@ -0,0 +1,4 @@ +import ComboboxOption from './ComboboxOption' + +export { ComboboxOption } +export default ComboboxOption diff --git a/src/design-system/components/combobox/Combobox.stories.ts b/src/design-system/components/combobox/Combobox.stories.ts new file mode 100644 index 000000000..4ac68ccbc --- /dev/null +++ b/src/design-system/components/combobox/Combobox.stories.ts @@ -0,0 +1,31 @@ +import { html } from 'lit' + +import './Combobox' +import '../combobox-option' +import { defineStoryRender } from '../../../storybook' + +const meta = { + title: 'Design System/Combobox', + args: { + label: 'What is the best food?', + options: 'Pizza, Ramen, Tacos' + }, + argTypes: { + label: { control: 'text' }, + options: { control: 'text' }, + }, +} as const + +const render = defineStoryRender(({ label, options }) => { + const parsedOptions = options.split(',').map(option => option.trim()) + + return html` + + ${parsedOptions.map(option => html`${option}`)} + + ` +}) + +export default meta + +export const Primary = { render } diff --git a/src/design-system/components/combobox/Combobox.styles.css b/src/design-system/components/combobox/Combobox.styles.css new file mode 100644 index 000000000..4aa02dfd4 --- /dev/null +++ b/src/design-system/components/combobox/Combobox.styles.css @@ -0,0 +1,68 @@ +@import '../../styles/common.styles.css'; + +:host { + display: inline-flex; + flex-direction: column; + align-items: flex-start; + gap: 5px; + + label { + color: var(--solid-ui-color-gray-600); + font-size: var(--solid-ui-font-size-sm); + font-weight: 500; + } + + .input-wrapper { + position: relative; + anchor-name: --combobox-anchor; + width: 100%; + + input { + border: 1px solid var(--solid-ui-color-gray-400); + border-radius: 5px; + padding: 10px; + padding-right: 40px; + width: 100%; + } + + icon-lucide-chevron-down { + position: absolute; + right: 15px; + top: 50%; + transform: translateY(-50%); + color: var(--solid-ui-color-gray-500); + width: var(--solid-ui-font-size-lg); + height: var(--solid-ui-font-size-lg); + pointer-events: none; + } + } + + [popover]:popover-open { + position: fixed; + top: calc(anchor(bottom) + 5px); + left: anchor(left); + right: auto; + bottom: auto; + min-width: anchor-size(width); + overflow: visible; + position-try-fallbacks: flip-block, flip-inline; + position-anchor: --combobox-anchor; + background: white; + border: 1px solid var(--solid-ui-color-gray-100); + border-radius: 5px; + box-shadow: 0px 4px 16px 0px rgba(0, 0, 0, 0.35); + display: flex; + flex-direction: column; + + [role="option"] { + padding: 12px 8px; + color: var(--solid-ui-color-gray-700); + border-bottom: 1px solid var(--solid-ui-color-gray-100); + + &:hover { + background: rgba(0, 0, 0, 0.05); + } + } + } + +} diff --git a/src/design-system/components/combobox/Combobox.ts b/src/design-system/components/combobox/Combobox.ts new file mode 100644 index 000000000..052e21308 --- /dev/null +++ b/src/design-system/components/combobox/Combobox.ts @@ -0,0 +1,99 @@ +import { html } from 'lit' +import { customElement, property, query, state } from 'lit/decorators.js' +import WebComponent from '../../../primitives/lib/WebComponent' + +import styles from './Combobox.styles.css' +import { generateId } from '../../lib/ids' +import ComboboxOption from '../combobox-option/ComboboxOption' +import '~icons/lucide/chevron-down' + +@customElement('solid-ui-combobox') +export default class Combobox extends WebComponent { + static styles = styles + static formAssociated = true + + @property({ type: String, reflect: true }) + accessor label = '' + + @property({ type: String }) + accessor value = '' + + @query('[popover]') + private accessor popoverElement: HTMLDivElement | null = null + + @query('input') + private accessor inputElement: HTMLInputElement | null = null + + @state() + private accessor filter = '' + + private inputId = `combobox-${generateId()}` + + protected render () { + const options = this.getOptions().filter(option => option.toLowerCase().includes(this.filter)) + + return html` + ${this.label ? html`` : ''} +
+ + +
+
+ ${options.map(option => html`
this.onOptionClick(option)}>${option}
`)} +
+ ` + } + + private getOptions (): string[] { + const options = this.querySelectorAll('solid-ui-combobox-option') + + return Array.from(options).map(option => option.value) + } + + private setValue (value: string) { + this.filter = value.toLowerCase() + this.value = value + + this.getInternals().setFormValue(value) + this.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true })) + } + + private onInputKeyDown (e: KeyboardEvent) { + switch (e.key) { + case 'ArrowDown': + e.preventDefault() + this.popoverElement?.showPopover() + break + case 'Enter': + if (!this.popoverElement?.matches(':popover-open')) { + e.preventDefault() + + this.getInternals().form?.requestSubmit() + } + break + } + } + + private onInputClick (e: MouseEvent) { + e.preventDefault() + + this.popoverElement?.showPopover() + } + + private onInputChange () { + this.setValue(this.inputElement?.value ?? '') + } + + private onOptionClick (option: string) { + this.setValue(option) + + this.popoverElement?.hidePopover() + } +} diff --git a/src/design-system/components/combobox/index.ts b/src/design-system/components/combobox/index.ts new file mode 100644 index 000000000..e6f8198e7 --- /dev/null +++ b/src/design-system/components/combobox/index.ts @@ -0,0 +1,4 @@ +import Combobox from './Combobox' + +export { Combobox } +export default Combobox diff --git a/src/design-system/polyfills/css-anchor-positioning.ts b/src/design-system/polyfills/css-anchor-positioning.ts new file mode 100644 index 000000000..2433c09fc --- /dev/null +++ b/src/design-system/polyfills/css-anchor-positioning.ts @@ -0,0 +1,8 @@ +export {} + +declare global { + interface CSSStyleDeclaration { + anchorName?: string + positionAnchor?: string + } +} diff --git a/src/primitives/lib/WebComponent.ts b/src/primitives/lib/WebComponent.ts index 67ab98b6d..fb44f2910 100644 --- a/src/primitives/lib/WebComponent.ts +++ b/src/primitives/lib/WebComponent.ts @@ -14,15 +14,15 @@ export default abstract class WebComponent extends LitElement { return } - this.internals ??= this.attachInternals() + const internals = this.getInternals() for (const [state, condition] of Object.entries(states)) { const matches = condition(this) - if (matches && !this.internals.states.has(state)) { - this.internals.states.add(state) - } else if (!matches && this.internals.states.has(state)) { - this.internals.states.delete(state) + if (matches && !internals.states.has(state)) { + internals.states.add(state) + } else if (!matches && internals.states.has(state)) { + internals.states.delete(state) } } } @@ -31,6 +31,12 @@ export default abstract class WebComponent extends LitElement { return html`` } + protected getInternals (): ElementInternals { + this.internals ??= this.attachInternals() + + return this.internals + } + private static (): typeof WebComponent { return this.constructor as typeof WebComponent } From 6a120d5a63fe31674a01922f6f721b0859ffd9d5 Mon Sep 17 00:00:00 2001 From: Noel De Martin Date: Fri, 29 May 2026 09:15:41 +0200 Subject: [PATCH 07/11] #769 Implement Menu --- .../components/menu-item/MenuItem.styles.css | 23 ++++++ .../components/menu-item/MenuItem.ts | 20 +++++ .../components/menu-item/index.ts | 4 + .../menu-items/MenuItems.styles.css | 30 +++++++ .../components/menu-items/MenuItems.ts | 36 +++++++++ .../components/menu-items/index.ts | 4 + .../components/menu/Menu.stories.ts | 29 +++++++ src/design-system/components/menu/Menu.ts | 81 +++++++++++++++++++ src/design-system/components/menu/index.ts | 4 + src/design-system/lib/menus/context.ts | 7 ++ 10 files changed, 238 insertions(+) create mode 100644 src/design-system/components/menu-item/MenuItem.styles.css create mode 100644 src/design-system/components/menu-item/MenuItem.ts create mode 100644 src/design-system/components/menu-item/index.ts create mode 100644 src/design-system/components/menu-items/MenuItems.styles.css create mode 100644 src/design-system/components/menu-items/MenuItems.ts create mode 100644 src/design-system/components/menu-items/index.ts create mode 100644 src/design-system/components/menu/Menu.stories.ts create mode 100644 src/design-system/components/menu/Menu.ts create mode 100644 src/design-system/components/menu/index.ts create mode 100644 src/design-system/lib/menus/context.ts diff --git a/src/design-system/components/menu-item/MenuItem.styles.css b/src/design-system/components/menu-item/MenuItem.styles.css new file mode 100644 index 000000000..db22d6a33 --- /dev/null +++ b/src/design-system/components/menu-item/MenuItem.styles.css @@ -0,0 +1,23 @@ +@import '../../styles/common.styles.css'; + +button { + width: 100%; + display: flex; + justify-content: flex-start; + align-items: center; + gap: 5px; + padding: 10px; + border-radius: 5px; + color: var(--solid-ui-color-gray-600); + font-weight: 500; + font-size: var(--solid-ui-font-size-md); + + &:hover { + background-color: var(--solid-ui-color-slate-200); + } + + ::slotted([slot="left-icon"]), ::slotted([slot="right-icon"]) { + width: var(--solid-ui-font-size-xl); + height: var(--solid-ui-font-size-xl); + } +} diff --git a/src/design-system/components/menu-item/MenuItem.ts b/src/design-system/components/menu-item/MenuItem.ts new file mode 100644 index 000000000..884f95c03 --- /dev/null +++ b/src/design-system/components/menu-item/MenuItem.ts @@ -0,0 +1,20 @@ +import { customElement } from 'lit/decorators.js' +import WebComponent from '../../../primitives/lib/WebComponent' +import { html } from 'lit' + +import styles from './MenuItem.styles.css' + +@customElement('solid-ui-menu-item') +export default class MenuItem extends WebComponent { + static styles = styles + + render () { + return html` + + ` + } +} diff --git a/src/design-system/components/menu-item/index.ts b/src/design-system/components/menu-item/index.ts new file mode 100644 index 000000000..aa4bd3ea9 --- /dev/null +++ b/src/design-system/components/menu-item/index.ts @@ -0,0 +1,4 @@ +import MenuItem from './MenuItem' + +export { MenuItem } +export default MenuItem diff --git a/src/design-system/components/menu-items/MenuItems.styles.css b/src/design-system/components/menu-items/MenuItems.styles.css new file mode 100644 index 000000000..ab5058e5d --- /dev/null +++ b/src/design-system/components/menu-items/MenuItems.styles.css @@ -0,0 +1,30 @@ +@import '../../styles/common.styles.css'; + +:host { + position: fixed; + top: calc(anchor(bottom) + 5px); + right: anchor(right); + left: auto; + bottom: auto; + overflow: visible; + position-try-fallbacks: flip-block, flip-inline; + + /* Also set in JS for global tree-scoped reference */ + position-anchor: --menu-anchor; + + /* [popover] resets */ + border: none; + background: transparent; + + div { + min-width: 300px; + background: var(--solid-ui-color-slate-50); + border: 1px solid var(--solid-ui-color-slate-200); + border-radius: 5px; + box-shadow: 0px 4px 16px 0px rgba(0, 0, 0, 0.35); + padding: 10px 5px; + display: flex; + flex-direction: column; + gap: 5px; + } +} diff --git a/src/design-system/components/menu-items/MenuItems.ts b/src/design-system/components/menu-items/MenuItems.ts new file mode 100644 index 000000000..7bf553f84 --- /dev/null +++ b/src/design-system/components/menu-items/MenuItems.ts @@ -0,0 +1,36 @@ +import { consume } from '@lit/context' +import { html } from 'lit' +import { customElement } from 'lit/decorators.js' +import WebComponent from '../../../primitives/lib/WebComponent' +import { menuContext, MenuContext } from '../../lib/menus/context' + +import styles from './MenuItems.styles.css' + +@customElement('solid-ui-menu-items') +export default class MenuItems extends WebComponent { + static styles = styles + + @consume({ context: menuContext, subscribe: true }) + private accessor context!: MenuContext + + connectedCallback () { + super.connectedCallback() + + this.setAttribute('popover', 'auto') + this.setAttribute('role', 'menu') + + this.style.positionAnchor = '--menu-anchor' + + if (this.context) { + this.id = `${this.context.id}-menu-items` + } + } + + render () { + return html` +
+ +
+ ` + } +} diff --git a/src/design-system/components/menu-items/index.ts b/src/design-system/components/menu-items/index.ts new file mode 100644 index 000000000..7faaca136 --- /dev/null +++ b/src/design-system/components/menu-items/index.ts @@ -0,0 +1,4 @@ +import MenuItems from './MenuItems' + +export { MenuItems } +export default MenuItems diff --git a/src/design-system/components/menu/Menu.stories.ts b/src/design-system/components/menu/Menu.stories.ts new file mode 100644 index 000000000..ee1430cbb --- /dev/null +++ b/src/design-system/components/menu/Menu.stories.ts @@ -0,0 +1,29 @@ +import { html } from 'lit' + +import './Menu' +import './../menu-item' +import './../menu-items' +import './../button' +import { defineStoryRender } from '../../../storybook' + +const meta = { + title: 'Design System/Menu', +} as const + +const render = defineStoryRender(() => html` + + + Open Menu + + + + alert('Clicked One!')}>One + alert('Clicked Two!')}>Two + alert('Clicked Three!')}>Three + + +`) + +export default meta + +export const Primary = { render } diff --git a/src/design-system/components/menu/Menu.ts b/src/design-system/components/menu/Menu.ts new file mode 100644 index 000000000..665b2d050 --- /dev/null +++ b/src/design-system/components/menu/Menu.ts @@ -0,0 +1,81 @@ +import { provide } from '@lit/context' +import { html } from 'lit' +import { customElement, queryAssignedElements } from 'lit/decorators.js' +import WebComponent from '../../../primitives/lib/WebComponent' +import MenuItems from '../menu-items' +import { MenuContext, menuContext } from '../../lib/menus/context' +import { generateId } from '../../lib/ids' + +@customElement('solid-ui-menu') +export default class Menu extends WebComponent { + @queryAssignedElements({ slot: 'trigger' }) + private accessor triggers!: HTMLButtonElement[] + + @queryAssignedElements({ selector: '[popover]' }) + private accessor popovers!: MenuItems[] + + @provide({ context: menuContext }) + // @ts-ignore + private accessor context: MenuContext = { id: generateId() } + + private triggerClickedWhilstOpen = false + + protected updated () { + const trigger = this.triggers?.[0] + const popover = this.popovers?.[0] + + if (!trigger || !popover) { + return + } + + trigger.setAttribute('aria-haspopup', 'menu') + trigger.setAttribute('aria-controls', popover.id) + trigger.style.anchorName = '--menu-anchor' + } + + protected render () { + return html` + + + ` + } + + private onTriggerPointerDown (e: PointerEvent) { + e.preventDefault() + + this.triggerClickedWhilstOpen = this.popovers?.[0]?.matches(':popover-open') ?? false + } + + private onTriggerClick (e: MouseEvent) { + e.preventDefault() + + if (this.triggerClickedWhilstOpen) { + this.triggerClickedWhilstOpen = false + + return + } + + this.popovers?.[0]?.togglePopover() + } + + private onMenuClick (e: MouseEvent) { + const target = e.target + const targetIsClickable = + target instanceof HTMLElement && ( + target.tagName === 'BUTTON' || + target.tagName === 'A' || + target.tagName === 'SOLID-UI-MENU-ITEM' || + target.closest('button, a, [role="menuitem"]') + ) + + if (!targetIsClickable) { + return + } + + this.popovers?.[0]?.hidePopover() + } +} diff --git a/src/design-system/components/menu/index.ts b/src/design-system/components/menu/index.ts new file mode 100644 index 000000000..294f64243 --- /dev/null +++ b/src/design-system/components/menu/index.ts @@ -0,0 +1,4 @@ +import Menu from './Menu' + +export { Menu } +export default Menu diff --git a/src/design-system/lib/menus/context.ts b/src/design-system/lib/menus/context.ts new file mode 100644 index 000000000..26ceb0109 --- /dev/null +++ b/src/design-system/lib/menus/context.ts @@ -0,0 +1,7 @@ +import { createContext } from '@lit/context' + +export interface MenuContext { + readonly id: string +} + +export const menuContext = createContext(Symbol('menu')) From bc340fbda974916cb9404a475142dfdfccc6c918 Mon Sep 17 00:00:00 2001 From: Noel De Martin Date: Fri, 29 May 2026 09:23:15 +0200 Subject: [PATCH 08/11] #769 Implement LoginModal --- src/design-system/components/button/Button.ts | 29 +++++- .../dialog-content/DialogContent.styles.css | 6 ++ .../dialog-content/DialogContent.ts | 8 ++ .../components/dialog-content/index.ts | 4 + .../dialog-footer/DialogFooter.styles.css | 10 ++ .../components/dialog-footer/DialogFooter.ts | 8 ++ .../components/dialog-footer/index.ts | 4 + .../dialog-header/DialogHeader.styles.css | 13 +++ .../components/dialog-header/DialogHeader.ts | 8 ++ .../components/dialog-header/index.ts | 4 + .../components/dialog/Dialog.styles.css | 31 +++++++ src/design-system/components/dialog/Dialog.ts | 24 ++++- .../login-modal/LoginModal.stories.ts | 18 ++++ .../login-modal/LoginModal.styles.css | 9 ++ .../components/login-modal/LoginModal.ts | 92 +++++++++++++++++++ .../components/login-modal/index.ts | 4 + src/design-system/styles/variables.css | 2 + 17 files changed, 269 insertions(+), 5 deletions(-) create mode 100644 src/design-system/components/dialog-content/DialogContent.styles.css create mode 100644 src/design-system/components/dialog-content/DialogContent.ts create mode 100644 src/design-system/components/dialog-content/index.ts create mode 100644 src/design-system/components/dialog-footer/DialogFooter.styles.css create mode 100644 src/design-system/components/dialog-footer/DialogFooter.ts create mode 100644 src/design-system/components/dialog-footer/index.ts create mode 100644 src/design-system/components/dialog-header/DialogHeader.styles.css create mode 100644 src/design-system/components/dialog-header/DialogHeader.ts create mode 100644 src/design-system/components/dialog-header/index.ts create mode 100644 src/design-system/components/login-modal/LoginModal.stories.ts create mode 100644 src/design-system/components/login-modal/LoginModal.styles.css create mode 100644 src/design-system/components/login-modal/LoginModal.ts create mode 100644 src/design-system/components/login-modal/index.ts diff --git a/src/design-system/components/button/Button.ts b/src/design-system/components/button/Button.ts index dcf95a128..231c85899 100644 --- a/src/design-system/components/button/Button.ts +++ b/src/design-system/components/button/Button.ts @@ -1,24 +1,47 @@ -import { html, LitElement } from 'lit' +import { html } from 'lit' import { customElement, property } from 'lit/decorators.js' import styles from './Button.styles.css' +import WebComponent from '../../../primitives/lib/WebComponent' export const BUTTON_VARIANTS = ['primary', 'secondary', 'tertiary'] as const export type ButtonVariant = typeof BUTTON_VARIANTS[number] @customElement('solid-ui-button') -export default class Button extends LitElement { +export default class Button extends WebComponent { static styles = styles + static formAssociated = true @property({ type: String, reflect: true }) accessor variant: ButtonVariant = 'primary' + @property({ type: String, reflect: true }) + accessor type = 'button' + + @property({ type: Boolean }) + accessor disabled = false + render () { return html` - ` } + + private onClick () { + switch (this.type) { + case 'submit': + this.getInternals().form?.requestSubmit() + break + case 'reset': + this.getInternals().form?.reset() + break + } + } } diff --git a/src/design-system/components/dialog-content/DialogContent.styles.css b/src/design-system/components/dialog-content/DialogContent.styles.css new file mode 100644 index 000000000..b5abca7b5 --- /dev/null +++ b/src/design-system/components/dialog-content/DialogContent.styles.css @@ -0,0 +1,6 @@ +@import '../../styles/common.styles.css'; + +:host { + display: flex; + padding: 15px; +} diff --git a/src/design-system/components/dialog-content/DialogContent.ts b/src/design-system/components/dialog-content/DialogContent.ts new file mode 100644 index 000000000..f4bc03436 --- /dev/null +++ b/src/design-system/components/dialog-content/DialogContent.ts @@ -0,0 +1,8 @@ +import styles from './DialogContent.styles.css' +import WebComponent from '../../../primitives/lib/WebComponent' +import { customElement } from 'lit/decorators.js' + +@customElement('solid-ui-dialog-content') +export default class DialogContent extends WebComponent { + static styles = styles +} diff --git a/src/design-system/components/dialog-content/index.ts b/src/design-system/components/dialog-content/index.ts new file mode 100644 index 000000000..339835a6c --- /dev/null +++ b/src/design-system/components/dialog-content/index.ts @@ -0,0 +1,4 @@ +import DialogContent from './DialogContent' + +export { DialogContent } +export default DialogContent diff --git a/src/design-system/components/dialog-footer/DialogFooter.styles.css b/src/design-system/components/dialog-footer/DialogFooter.styles.css new file mode 100644 index 000000000..13292ad0c --- /dev/null +++ b/src/design-system/components/dialog-footer/DialogFooter.styles.css @@ -0,0 +1,10 @@ +@import '../../styles/common.styles.css'; + +:host { + display: flex; + justify-content: space-between; + align-items: center; + padding: 15px; + gap: 15px; + border-top: 1px solid var(--solid-ui-color-slate-200); +} diff --git a/src/design-system/components/dialog-footer/DialogFooter.ts b/src/design-system/components/dialog-footer/DialogFooter.ts new file mode 100644 index 000000000..02c02500b --- /dev/null +++ b/src/design-system/components/dialog-footer/DialogFooter.ts @@ -0,0 +1,8 @@ +import styles from './DialogFooter.styles.css' +import WebComponent from '../../../primitives/lib/WebComponent' +import { customElement } from 'lit/decorators.js' + +@customElement('solid-ui-dialog-footer') +export default class DialogFooter extends WebComponent { + static styles = styles +} diff --git a/src/design-system/components/dialog-footer/index.ts b/src/design-system/components/dialog-footer/index.ts new file mode 100644 index 000000000..2a742938d --- /dev/null +++ b/src/design-system/components/dialog-footer/index.ts @@ -0,0 +1,4 @@ +import DialogFooter from './DialogFooter' + +export { DialogFooter } +export default DialogFooter diff --git a/src/design-system/components/dialog-header/DialogHeader.styles.css b/src/design-system/components/dialog-header/DialogHeader.styles.css new file mode 100644 index 000000000..92a941409 --- /dev/null +++ b/src/design-system/components/dialog-header/DialogHeader.styles.css @@ -0,0 +1,13 @@ +@import "../../styles/common.styles.css"; + +:host { + display: flex; + justify-content: space-between; + align-items: center; + padding: 15px; + gap: 15px; + background: var(--solid-ui-color-slate-50); + border-bottom: 1px solid var(--solid-ui-color-slate-200); + font-size: var(--solid-ui-font-size-lg); + font-weight: 500; +} diff --git a/src/design-system/components/dialog-header/DialogHeader.ts b/src/design-system/components/dialog-header/DialogHeader.ts new file mode 100644 index 000000000..1b3d7f3a4 --- /dev/null +++ b/src/design-system/components/dialog-header/DialogHeader.ts @@ -0,0 +1,8 @@ +import styles from './DialogHeader.styles.css' +import WebComponent from '../../../primitives/lib/WebComponent' +import { customElement } from 'lit/decorators.js' + +@customElement('solid-ui-dialog-header') +export default class DialogHeader extends WebComponent { + static styles = styles +} diff --git a/src/design-system/components/dialog-header/index.ts b/src/design-system/components/dialog-header/index.ts new file mode 100644 index 000000000..df57e498a --- /dev/null +++ b/src/design-system/components/dialog-header/index.ts @@ -0,0 +1,4 @@ +import DialogHeader from './DialogHeader' + +export { DialogHeader } +export default DialogHeader diff --git a/src/design-system/components/dialog/Dialog.styles.css b/src/design-system/components/dialog/Dialog.styles.css index 7b3a41ac6..29e2dca11 100644 --- a/src/design-system/components/dialog/Dialog.styles.css +++ b/src/design-system/components/dialog/Dialog.styles.css @@ -5,6 +5,37 @@ dialog { border-radius: 5px; box-shadow: 0px 4px 16px 0px rgba(0, 0, 0, 0.35); min-width: 343px; + + solid-ui-dialog-header { + button { + padding: 5px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 5px; + position: relative; + + icon-lucide-x { + width: 22px; + height: 22px; + color: var(--solid-ui-color-gray-600); + } + + &::after { + content: ""; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: var(--solid-ui-clickable-area); + height: var(--solid-ui-clickable-area); + } + + &:hover { + background: rgba(0, 0, 0, 0.04); + } + } + } } dialog:not([open]) { diff --git a/src/design-system/components/dialog/Dialog.ts b/src/design-system/components/dialog/Dialog.ts index 354ee2f7d..e2e08275e 100644 --- a/src/design-system/components/dialog/Dialog.ts +++ b/src/design-system/components/dialog/Dialog.ts @@ -1,15 +1,21 @@ import { consume } from '@lit/context' -import { html } from 'lit' -import { customElement, query } from 'lit/decorators.js' +import { html, nothing } from 'lit' +import { customElement, property, query } from 'lit/decorators.js' import WebComponent from '../../../primitives/lib/WebComponent' import styles from './Dialog.styles.css' import { DialogContext, dialogContext } from '../../lib/dialogs/context' import { CloseDialogEvent } from '../../lib/dialogs/events/close-dialog' +import '~icons/lucide/x' +import '../dialog-header' + @customElement('solid-ui-dialog') export default class Dialog extends WebComponent { static styles = styles + @property({ type: String, reflect: true }) + accessor title = '' + @query('dialog') private accessor nativeDialog: HTMLDialogElement | null = null @@ -34,9 +40,23 @@ export default class Dialog extends WebComponent { } protected render () { + const header = this.title + ? html` + +

${this.title}

+ +
+ ` + : nothing + return html` + ${header} + ` } diff --git a/src/design-system/components/login-modal/LoginModal.stories.ts b/src/design-system/components/login-modal/LoginModal.stories.ts new file mode 100644 index 000000000..f7b61e697 --- /dev/null +++ b/src/design-system/components/login-modal/LoginModal.stories.ts @@ -0,0 +1,18 @@ +import { html } from 'lit' + +import './LoginModal' +import './../button' +import { defineStoryRender } from '../../../storybook' +import { showDialog } from '../../lib/dialogs' + +const meta = { + title: 'Design System/Login Modal', +} as const + +const render = defineStoryRender(() => html` + showDialog(html``)}>Open +`) + +export default meta + +export const Primary = { render } diff --git a/src/design-system/components/login-modal/LoginModal.styles.css b/src/design-system/components/login-modal/LoginModal.styles.css new file mode 100644 index 000000000..fee0a8077 --- /dev/null +++ b/src/design-system/components/login-modal/LoginModal.styles.css @@ -0,0 +1,9 @@ +@import '../../styles/common.styles.css'; + +solid-ui-combobox { + width: 100%; +} + +solid-ui-button { + flex: 1; +} diff --git a/src/design-system/components/login-modal/LoginModal.ts b/src/design-system/components/login-modal/LoginModal.ts new file mode 100644 index 000000000..351b90f83 --- /dev/null +++ b/src/design-system/components/login-modal/LoginModal.ts @@ -0,0 +1,92 @@ +import { html } from 'lit' +import { consume } from '@lit/context' +import { customElement, property, query, state } from 'lit/decorators.js' +import WebComponent from '../../../primitives/lib/WebComponent' +import { AuthContext, authContext, DEFAULT_AUTH_CONTEXT } from '../../../primitives/lib/auth/context' + +import { getSuggestedIssuers } from 'solid-logic' + +import type Dialog from '../../../design-system/components/dialog' +import type Combobox from '../../../design-system/components/combobox' + +import '~icons/lucide/chevron-down' +import '../../../design-system/components/dialog' +import '../../../design-system/components/dialog-content' +import '../../../design-system/components/dialog-footer' +import '../../../design-system/components/button' +import '../../../design-system/components/combobox' +import '../../../design-system/components/combobox-option' +import styles from './LoginModal.styles.css' + +@customElement('solid-ui-login-modal') +export default class LoginModal extends WebComponent { + static styles = styles + + @property({ type: String, reflect: true }) + accessor issuerUrl = '' + + @state() + private accessor issuerInputValue = '' + + @consume({ context: authContext, subscribe: true }) + private accessor auth: AuthContext = DEFAULT_AUTH_CONTEXT + + @query('solid-ui-dialog') + private accessor dialog: Dialog | null = null + + connectedCallback () { + super.connectedCallback() + + this.issuerInputValue = (typeof localStorage !== 'undefined' && localStorage.getItem('loginIssuer')) || this.issuerUrl || '' + } + + protected render () { + const suggestedIssuers = getSuggestedIssuers() + + return html` + +
+ + + ${suggestedIssuers.map(issuer => html`${issuer.name}`)} + + + + + + Cancel + + + Login + + +
+
+ ` + } + + private async loginToIssuer (issuerUri: string) { + if (!issuerUri) { + return + } + + await this.auth.login(issuerUri) + } + + private onIssuerInputChange (e: Event) { + this.issuerInputValue = (e.target as Combobox).value + } + + private onSubmit (e: Event) { + e.preventDefault() + + this.loginToIssuer(this.issuerInputValue) + } +} diff --git a/src/design-system/components/login-modal/index.ts b/src/design-system/components/login-modal/index.ts new file mode 100644 index 000000000..ec08f18b2 --- /dev/null +++ b/src/design-system/components/login-modal/index.ts @@ -0,0 +1,4 @@ +import LoginModal from './LoginModal' + +export { LoginModal } +export default LoginModal diff --git a/src/design-system/styles/variables.css b/src/design-system/styles/variables.css index 837c461d8..dcbe0d656 100644 --- a/src/design-system/styles/variables.css +++ b/src/design-system/styles/variables.css @@ -18,4 +18,6 @@ --solid-ui-font-size-md: 1rem; /* 16px */ --solid-ui-font-size-lg: 1.125rem; /* 18px */ --solid-ui-font-size-xl: 1.25rem; /* 20px */ + + --solid-ui-clickable-area: 42px; } From a5f1fac1979c1809a465b911722e89b2e64a0b71 Mon Sep 17 00:00:00 2001 From: Noel De Martin Date: Fri, 29 May 2026 09:27:46 +0200 Subject: [PATCH 09/11] #769 Implement Account --- .../components/account/Account.stories.ts | 27 +++++++ .../components/account/Account.styles.css | 36 +++++++++ .../components/account/Account.ts | 81 +++++++++++++++++++ src/design-system/components/account/index.ts | 4 + src/design-system/styles/variables.css | 2 + .../components/avatar/Avatar.stories.ts | 23 ++++++ .../components/avatar/Avatar.styles.css | 26 ++++++ src/primitives/components/avatar/Avatar.ts | 32 ++++++++ src/primitives/components/avatar/index.ts | 4 + src/primitives/lib/auth/NoopAuth.ts | 4 + src/primitives/lib/auth/context.ts | 1 + src/storybook/auth/StorybookAuth.ts | 4 + 12 files changed, 244 insertions(+) create mode 100644 src/design-system/components/account/Account.stories.ts create mode 100644 src/design-system/components/account/Account.styles.css create mode 100644 src/design-system/components/account/Account.ts create mode 100644 src/design-system/components/account/index.ts create mode 100644 src/primitives/components/avatar/Avatar.stories.ts create mode 100644 src/primitives/components/avatar/Avatar.styles.css create mode 100644 src/primitives/components/avatar/Avatar.ts create mode 100644 src/primitives/components/avatar/index.ts diff --git a/src/design-system/components/account/Account.stories.ts b/src/design-system/components/account/Account.stories.ts new file mode 100644 index 000000000..3e63c88fd --- /dev/null +++ b/src/design-system/components/account/Account.stories.ts @@ -0,0 +1,27 @@ +import { html } from 'lit' + +import './Account' +import { USER_OPTIONS } from '../../../storybook/stubs' +import { defineAuthStoryRender } from '../../../storybook' + +const meta = { + title: 'Design System/Account', + args: { + user: 'Alice', + }, + argTypes: { + user: USER_OPTIONS.control, + } +} as const + +const render = defineAuthStoryRender(() => html``) + +export default meta + +export const Primary = { render } +export const Guest = { + render, + args: { + user: 'Guest', + } +} diff --git a/src/design-system/components/account/Account.styles.css b/src/design-system/components/account/Account.styles.css new file mode 100644 index 000000000..abfc05ad4 --- /dev/null +++ b/src/design-system/components/account/Account.styles.css @@ -0,0 +1,36 @@ +@import '../../styles/common.styles.css'; + +:host { + display: inline-flex; + flex-direction: row; + gap: 10px; +} + +:host(:state(loggedIn)) { + --padding: 4px; + --border-width: 1px; + --image-size: 1.875rem; /* 30px */ + + button { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 5px; + background-color: var(--solid-ui-color-body-grey); + padding: var(--padding); + border-radius: calc((var(--image-size) + 2 * var(--padding) + 2 * var(--border-width)) / 2); + color: var(--solid-ui-color-white); + + solid-avatar { + width: var(--image-size); + height: var(--image-size); + border: var(--border-width) solid var(--solid-ui-color-white); + border-radius: 50%; + } + + icon-lucide-chevron-down { + width: 16px; + height: 16px; + } + } +} diff --git a/src/design-system/components/account/Account.ts b/src/design-system/components/account/Account.ts new file mode 100644 index 000000000..a6a26e3e6 --- /dev/null +++ b/src/design-system/components/account/Account.ts @@ -0,0 +1,81 @@ +import { consume } from '@lit/context' +import { html } from 'lit' +import { customElement } from 'lit/decorators.js' +import WebComponent from '../../../primitives/lib/WebComponent' +import { authContext, AuthContext, DEFAULT_AUTH_CONTEXT } from '../../../primitives/lib/auth/context' + +import '../../../design-system/components/button' +import '../../../primitives/components/avatar' +import '../../../primitives/components/login-button' +import '../../../primitives/components/signup-button' +import '../../../primitives/components/logout-button' +import '../menu' +import '../menu-items' +import '../menu-item' +import '~icons/lucide/user' +import '~icons/lucide/log-in' +import '~icons/lucide/chevron-down' +import '~icons/lucide/log-out' +import styles from './Account.styles.css' + +@customElement('solid-ui-account') +export default class Account extends WebComponent { + static styles = styles + static states = { + loggedIn: (component: Account) => !!component.auth.account, + } + + @consume({ context: authContext, subscribe: true }) + private accessor auth: AuthContext = DEFAULT_AUTH_CONTEXT + + private unsubscribeSessionUpdated?: () => void + + connectedCallback () { + super.connectedCallback() + + this.unsubscribeSessionUpdated = this.auth.onSessionUpdated(() => this.requestUpdate()) + } + + disconnectedCallback () { + super.disconnectedCallback() + + this.unsubscribeSessionUpdated?.() + } + + protected render () { + if (!this.auth.account) { + return html` + + + + Log In + + + + + + Sign Up + + + ` + } + + return html` + + + + + + + + Sign out + + + + + ` + } +} diff --git a/src/design-system/components/account/index.ts b/src/design-system/components/account/index.ts new file mode 100644 index 000000000..eb9eac530 --- /dev/null +++ b/src/design-system/components/account/index.ts @@ -0,0 +1,4 @@ +import Account from './Account' + +export { Account } +export default Account diff --git a/src/design-system/styles/variables.css b/src/design-system/styles/variables.css index dcbe0d656..de5b3809f 100644 --- a/src/design-system/styles/variables.css +++ b/src/design-system/styles/variables.css @@ -14,6 +14,8 @@ --solid-ui-color-slate-200: #e2e8f0; --solid-ui-color-slate-400: #90a1b9; + --solid-ui-color-body-grey: #666; + --solid-ui-font-size-sm: 0.875rem; /* 14px */ --solid-ui-font-size-md: 1rem; /* 16px */ --solid-ui-font-size-lg: 1.125rem; /* 18px */ diff --git a/src/primitives/components/avatar/Avatar.stories.ts b/src/primitives/components/avatar/Avatar.stories.ts new file mode 100644 index 000000000..d39ddb5dd --- /dev/null +++ b/src/primitives/components/avatar/Avatar.stories.ts @@ -0,0 +1,23 @@ +import { html } from 'lit' +import { defineAuthStoryRender } from '../../../storybook' + +import './Avatar' +import { USER_OPTIONS } from '../../../storybook/stubs' + +const meta = { + title: 'Primitives/Avatar', + args: { + user: 'Alice', + }, + argTypes: { + user: USER_OPTIONS.control, + } +} as const + +const render = defineAuthStoryRender(() => html``) + +export const Primary = { render } +export const Fallback = { render, args: { user: 'Bob' } } +export const Guest = { render, args: { user: 'Guest' } } + +export default meta diff --git a/src/primitives/components/avatar/Avatar.styles.css b/src/primitives/components/avatar/Avatar.styles.css new file mode 100644 index 000000000..a56aa1cd9 --- /dev/null +++ b/src/primitives/components/avatar/Avatar.styles.css @@ -0,0 +1,26 @@ +:host { + --size: 150px; + + display: inline-flex; + overflow: hidden; + width: var(--size); + height: var(--size); + + img { + width: 100%; + height: 100%; + object-fit: cover; + } +} + +:host(:state(fallback)) { + background: var(--solid-color-gray-100); + justify-content: center; + align-items: center; + + icon-lucide-circle-user { + color: var(--solid-color-gray-200); + width: 50%; + height: 50%; + } +} diff --git a/src/primitives/components/avatar/Avatar.ts b/src/primitives/components/avatar/Avatar.ts new file mode 100644 index 000000000..2292b6f40 --- /dev/null +++ b/src/primitives/components/avatar/Avatar.ts @@ -0,0 +1,32 @@ +import { consume } from '@lit/context' +import { html } from 'lit' +import { customElement } from 'lit/decorators.js' +import WebComponent from '../../lib/WebComponent' +import { authContext, AuthContext, DEFAULT_AUTH_CONTEXT } from '../../lib/auth/context' +import commonStyles from '../../styles/common.styles.css' + +import '~icons/lucide/circle-user' +import styles from './Avatar.styles.css' + +@customElement('solid-avatar') +export default class Avatar extends WebComponent { + static styles = [commonStyles, styles] + static states = { + fallback: (component: Avatar) => !component.auth.account?.avatarUrl, + } + + @consume({ context: authContext, subscribe: true }) + private accessor auth: AuthContext = DEFAULT_AUTH_CONTEXT + + protected render () { + if (!this.auth.account?.avatarUrl) { + return html` + + ` + } + + return html` + + ` + } +} diff --git a/src/primitives/components/avatar/index.ts b/src/primitives/components/avatar/index.ts new file mode 100644 index 000000000..711001e8f --- /dev/null +++ b/src/primitives/components/avatar/index.ts @@ -0,0 +1,4 @@ +import Avatar from './Avatar' + +export { Avatar } +export default Avatar diff --git a/src/primitives/lib/auth/NoopAuth.ts b/src/primitives/lib/auth/NoopAuth.ts index 01c78a524..3f57d8bd7 100644 --- a/src/primitives/lib/auth/NoopAuth.ts +++ b/src/primitives/lib/auth/NoopAuth.ts @@ -17,4 +17,8 @@ export default class NoopAuth implements AuthContext { async logout () { throw new Error('Can\'t use auth, missing context provider') } + + onSessionUpdated () { + return () => undefined + } } diff --git a/src/primitives/lib/auth/context.ts b/src/primitives/lib/auth/context.ts index 4f5eb9f8b..822bb6ad2 100644 --- a/src/primitives/lib/auth/context.ts +++ b/src/primitives/lib/auth/context.ts @@ -7,6 +7,7 @@ export interface AuthContext { login(loginUrl?: string): Promise; signup(): Promise; logout(): Promise; + onSessionUpdated(callback: () => unknown): () => void; } export const DEFAULT_AUTH_CONTEXT = new NoopAuth() diff --git a/src/storybook/auth/StorybookAuth.ts b/src/storybook/auth/StorybookAuth.ts index a684e2e48..b611d3d85 100644 --- a/src/storybook/auth/StorybookAuth.ts +++ b/src/storybook/auth/StorybookAuth.ts @@ -15,4 +15,8 @@ export default class StorybookAuth implements AuthContext { async logout () { alert('Log out!') } + + onSessionUpdated () { + return () => undefined + } } From 875ddd352fa1ef3c70035e42d55444c93a66e582 Mon Sep 17 00:00:00 2001 From: Noel De Martin Date: Fri, 29 May 2026 09:43:19 +0200 Subject: [PATCH 10/11] #769 Enable experimental Account component in Header --- __mocks__/iconsMock.js | 2 + babel.config.mjs | 13 ++++ config/babel.mjs | 8 ++- jest.config.mjs | 1 + .../components/provider/Provider.ts | 33 +++++++++ .../components/provider/index.ts | 4 ++ src/design-system/lib/auth/SolidAuth.ts | 72 +++++++++++++++++++ src/features.ts | 9 +++ src/v2/components/layout/header/Header.ts | 12 ++++ .../components/layout/header/header.test.ts | 2 + 10 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 __mocks__/iconsMock.js create mode 100644 src/design-system/components/provider/Provider.ts create mode 100644 src/design-system/components/provider/index.ts create mode 100644 src/design-system/lib/auth/SolidAuth.ts create mode 100644 src/features.ts diff --git a/__mocks__/iconsMock.js b/__mocks__/iconsMock.js new file mode 100644 index 000000000..cda76637b --- /dev/null +++ b/__mocks__/iconsMock.js @@ -0,0 +1,2 @@ +// Jest mock for icon imports +module.exports = {} diff --git a/babel.config.mjs b/babel.config.mjs index 8f7dc3a86..085b2032c 100644 --- a/babel.config.mjs +++ b/babel.config.mjs @@ -1,3 +1,10 @@ +import path from 'path' +import { fileURLToPath } from 'url' +import { resolvePathsUsingDecorators, litDecoratorsBabelOptions } from './config/babel.mjs' + +const projectRoot = path.dirname(fileURLToPath(import.meta.url)) +const pathsUsingDecorators = resolvePathsUsingDecorators(projectRoot) + export default { presets: [ ['@babel/preset-env', { @@ -9,5 +16,11 @@ export default { ], plugins: [ '@babel/plugin-transform-runtime' + ], + overrides: [ + { + include: pathsUsingDecorators, + ...litDecoratorsBabelOptions, + } ] } diff --git a/config/babel.mjs b/config/babel.mjs index 16823dcac..74604a792 100644 --- a/config/babel.mjs +++ b/config/babel.mjs @@ -8,8 +8,7 @@ import path from 'path' const pathsUsingDecorators = ['src/design-system', 'src/primitives', 'src/storybook'] -export const litDecoratorsLoaderOptions = { - cacheDirectory: true, +export const litDecoratorsBabelOptions = { assumptions: { setPublicClassFields: false }, @@ -21,6 +20,11 @@ export const litDecoratorsLoaderOptions = { ] } +export const litDecoratorsLoaderOptions = { + cacheDirectory: true, + ...litDecoratorsBabelOptions, +} + export function resolvePathsUsingDecorators (projectRoot) { return pathsUsingDecorators.map((_path) => path.resolve(projectRoot, _path)) } diff --git a/jest.config.mjs b/jest.config.mjs index 26b8c33d5..875c85280 100644 --- a/jest.config.mjs +++ b/jest.config.mjs @@ -15,6 +15,7 @@ export default { ], setupFilesAfterEnv: ['./test/helpers/setup.ts'], moduleNameMapper: { + '^~icons/(.*)$': '/__mocks__/iconsMock.js', '^.+\\.css$': '/__mocks__/styleMock.js' }, testMatch: ['**/?(*.)+(spec|test).[tj]s?(x)'], diff --git a/src/design-system/components/provider/Provider.ts b/src/design-system/components/provider/Provider.ts new file mode 100644 index 000000000..fcb0b8f00 --- /dev/null +++ b/src/design-system/components/provider/Provider.ts @@ -0,0 +1,33 @@ +import { provide } from '@lit/context' +import { html } from 'lit' +import { customElement, property } from 'lit/decorators.js' + +import { authContext } from '../../../primitives/lib/auth/context' +import WebComponent from '../../../primitives/lib/WebComponent' +import SolidAuth, { DEFAULT_SIGNUP_URL } from '../../lib/auth/SolidAuth' + +import '../dialogs-root' + +@customElement('solid-ui-provider') +export default class Provider extends WebComponent { + @property({ type: String, reflect: true }) + accessor signupUrl: string | undefined + + @provide({ context: authContext }) + private accessor auth = new SolidAuth() + + protected willUpdate (changedProperties: Map) { + super.willUpdate(changedProperties) + + if (changedProperties.has('signupUrl')) { + this.auth.signupUrl = this.signupUrl ?? DEFAULT_SIGNUP_URL + } + } + + protected render () { + return html` + + + ` + } +} diff --git a/src/design-system/components/provider/index.ts b/src/design-system/components/provider/index.ts new file mode 100644 index 000000000..461a95f14 --- /dev/null +++ b/src/design-system/components/provider/index.ts @@ -0,0 +1,4 @@ +import Provider from './Provider' + +export { Provider } +export default Provider diff --git a/src/design-system/lib/auth/SolidAuth.ts b/src/design-system/lib/auth/SolidAuth.ts new file mode 100644 index 000000000..31e272ec8 --- /dev/null +++ b/src/design-system/lib/auth/SolidAuth.ts @@ -0,0 +1,72 @@ +import { authSession, solidLogicSingleton } from 'solid-logic' +import Account from '../../../primitives/lib/auth/Account' +import { findImage } from '../../../widgets/buttons' +import { AuthContext } from '../../../primitives/lib/auth/context' +import { showDialog } from '../dialogs' +import { html } from 'lit' + +import '../../components/login-modal' + +export const DEFAULT_SIGNUP_URL = 'https://solidproject.org/get_a_pod' + +export default class SolidAuth implements AuthContext { + constructor (public signupUrl: string = DEFAULT_SIGNUP_URL) {} + + get account (): Account | null { + if (!authSession.info?.isLoggedIn || !authSession.info?.webId) { + return null + } + + const webId = authSession.info.webId + const me = solidLogicSingleton.store.sym(webId) + const avatarUrl = findImage(me) ?? undefined + + return new Account(webId, avatarUrl) + } + + async login (loginUrl?: string) { + if (!loginUrl) { + showDialog(html``) + + return + } + + // clear authorization metadata from store + ;(solidLogicSingleton.store.updater as any).flagAuthorizationMetadata() + + const preLoginRedirectHash = new URL(window.location.href).hash + if (preLoginRedirectHash) { + window.localStorage.setItem('preLoginRedirectHash', preLoginRedirectHash) + } + window.localStorage.setItem('loginIssuer', loginUrl) + + const locationUrl = new URL(window.location.href) + + locationUrl.hash = '' + + await authSession.login({ + redirectUrl: locationUrl.href, + oidcIssuer: loginUrl + }) + } + + async signup () { + window.open(this.signupUrl, '_blank', 'noopener,noreferrer') + } + + async logout () { + await authSession.logout() + } + + onSessionUpdated (callback: () => unknown) { + authSession.events.on('login', callback) + authSession.events.on('logout', callback) + authSession.events.on('sessionRestore', callback) + + return () => { + authSession.events.off('login', callback) + authSession.events.off('logout', callback) + authSession.events.off('sessionRestore', callback) + } + } +} diff --git a/src/features.ts b/src/features.ts new file mode 100644 index 000000000..f1bbc610a --- /dev/null +++ b/src/features.ts @@ -0,0 +1,9 @@ +/** + * This file is used to enable or disable experimental features using feature flags. + */ + +const Features = { + DESIGN_SYSTEM_HEADER_ACCOUNT: true, +} + +export default Features diff --git a/src/v2/components/layout/header/Header.ts b/src/v2/components/layout/header/Header.ts index 5a89b35f4..b034d5b89 100644 --- a/src/v2/components/layout/header/Header.ts +++ b/src/v2/components/layout/header/Header.ts @@ -5,6 +5,10 @@ import '../../auth/loginButton/index' import '../../auth/signupButton/index' import { ifDefined } from 'lit/directives/if-defined.js' +import '../../../../design-system/components/account' +import '../../../../design-system/components/provider' +import Features from '../../../../features' + const DEFAULT_HELP_MENU_ICON = '' const DEFAULT_SOLID_ICON_URL = 'https://solidproject.org/assets/img/solid-emblem.svg' const DEFAULT_SIGNUP_URL = 'https://solidproject.org/get_a_pod' @@ -790,6 +794,14 @@ export class Header extends LitElement { } private renderUserArea () { + if (Features.DESIGN_SYSTEM_HEADER_ACCOUNT) { + return html` + + + + ` + } + if (this.authState === 'logged-out') { return this.renderLoggedOutActions() } diff --git a/src/v2/components/layout/header/header.test.ts b/src/v2/components/layout/header/header.test.ts index db621f23b..48a46fbca 100644 --- a/src/v2/components/layout/header/header.test.ts +++ b/src/v2/components/layout/header/header.test.ts @@ -1,8 +1,10 @@ +import Features from '../../../../features' import { Header } from './Header' import './index' describe('SolidUIHeaderElement', () => { beforeEach(() => { + Features.DESIGN_SYSTEM_HEADER_ACCOUNT = false document.body.innerHTML = '' Object.defineProperty(window, 'open', { configurable: true, From b56e25a94b4539b801b7995855d60696b3c93897 Mon Sep 17 00:00:00 2001 From: Noel De Martin Date: Fri, 29 May 2026 09:48:46 +0200 Subject: [PATCH 11/11] #769 Remove legacy storybook files --- src/stories/Buttons.mdx | 69 ------- src/stories/Buttons.stories.js | 129 ------------ src/stories/DateTime.mdx | 64 ------ src/stories/DateTime.stories.js | 56 ----- src/stories/Display.mdx | 32 --- src/stories/Display.stories.js | 43 ---- src/stories/DomManipulation.mdx | 36 ---- src/stories/DomManipulation.stories.js | 66 ------ src/stories/DragAndDrop.mdx | 39 ---- src/stories/DragAndDrop.stories.js | 68 ------- src/stories/Events.mdx | 17 -- src/stories/Events.stories.js | 28 --- src/stories/Header.mdx | 22 -- src/stories/Header.stories.js | 20 -- src/stories/Images.mdx | 95 --------- src/stories/Images.stories.js | 110 ---------- src/stories/Interactive.mdx | 63 ------ src/stories/Interactive.stories.js | 136 ------------- src/stories/Log.mdx | 117 ----------- src/stories/Log.stories.js | 143 ------------- src/stories/Matrix.mdx | 17 -- src/stories/Matrix.stories.js | 73 ------- src/stories/MediaTypes.mdx | 31 --- src/stories/MediaTypes.stories.js | 52 ----- src/stories/Notepad.mdx | 54 ----- src/stories/Notepad.stories.js | 155 -------------- src/stories/RdfUtils.mdx | 47 ----- src/stories/RdfUtils.stories.js | 60 ------ src/stories/Tabs.mdx | 118 ----------- src/stories/Tabs.stories.js | 269 ------------------------- src/stories/Widgets.mdx | 36 ---- src/stories/Widgets.stories.js | 57 ------ src/stories/forms/Fields.mdx | 28 --- src/stories/forms/Fields.stories.js | 161 --------------- src/stories/forms/Forms.mdx | 22 -- src/stories/forms/Forms.stories.js | 56 ----- src/stories/forms/Intro.mdx | 13 -- src/stories/forms/Options.mdx | 19 -- src/stories/forms/Options.stories.js | 239 ---------------------- src/stories/iconForClass.mdx | 29 --- src/stories/iconForClass.stories.js | 31 --- 41 files changed, 2920 deletions(-) delete mode 100644 src/stories/Buttons.mdx delete mode 100644 src/stories/Buttons.stories.js delete mode 100644 src/stories/DateTime.mdx delete mode 100644 src/stories/DateTime.stories.js delete mode 100644 src/stories/Display.mdx delete mode 100644 src/stories/Display.stories.js delete mode 100644 src/stories/DomManipulation.mdx delete mode 100644 src/stories/DomManipulation.stories.js delete mode 100644 src/stories/DragAndDrop.mdx delete mode 100644 src/stories/DragAndDrop.stories.js delete mode 100644 src/stories/Events.mdx delete mode 100644 src/stories/Events.stories.js delete mode 100644 src/stories/Header.mdx delete mode 100644 src/stories/Header.stories.js delete mode 100644 src/stories/Images.mdx delete mode 100644 src/stories/Images.stories.js delete mode 100644 src/stories/Interactive.mdx delete mode 100644 src/stories/Interactive.stories.js delete mode 100644 src/stories/Log.mdx delete mode 100644 src/stories/Log.stories.js delete mode 100644 src/stories/Matrix.mdx delete mode 100644 src/stories/Matrix.stories.js delete mode 100644 src/stories/MediaTypes.mdx delete mode 100644 src/stories/MediaTypes.stories.js delete mode 100644 src/stories/Notepad.mdx delete mode 100644 src/stories/Notepad.stories.js delete mode 100644 src/stories/RdfUtils.mdx delete mode 100644 src/stories/RdfUtils.stories.js delete mode 100644 src/stories/Tabs.mdx delete mode 100644 src/stories/Tabs.stories.js delete mode 100644 src/stories/Widgets.mdx delete mode 100644 src/stories/Widgets.stories.js delete mode 100644 src/stories/forms/Fields.mdx delete mode 100644 src/stories/forms/Fields.stories.js delete mode 100644 src/stories/forms/Forms.mdx delete mode 100644 src/stories/forms/Forms.stories.js delete mode 100644 src/stories/forms/Intro.mdx delete mode 100644 src/stories/forms/Options.mdx delete mode 100644 src/stories/forms/Options.stories.js delete mode 100644 src/stories/iconForClass.mdx delete mode 100644 src/stories/iconForClass.stories.js diff --git a/src/stories/Buttons.mdx b/src/stories/Buttons.mdx deleted file mode 100644 index 7472d023f..000000000 --- a/src/stories/Buttons.mdx +++ /dev/null @@ -1,69 +0,0 @@ -import * as UI from '../../src/index' -import * as ButtonsStories from './Buttons.stories'; - -import { Canvas, Meta, Story } from '@storybook/blocks'; -import { action } from '@storybook/addon-actions' - - - -## Button with text (no border) - - - - - - - -## Button with text (needs border) - - - - - - - -## Continue & Cancel button - - - - - - - -## Delete button - - - - - -## Button with icon - - - - - -## File upload button - - - - - -## Link button - - - - - -## Link icon - - - - - - - -## Remove button - - - - diff --git a/src/stories/Buttons.stories.js b/src/stories/Buttons.stories.js deleted file mode 100644 index 38783fdce..000000000 --- a/src/stories/Buttons.stories.js +++ /dev/null @@ -1,129 +0,0 @@ -import * as UI from '../../src/index' - -import { action } from '@storybook/addon-actions' - -export default { - title: 'Buttons', -} - -export const Primary = { - render: () => - UI.widgets.button(document, undefined, 'Primary', action('clicked')), - name: 'Primary', -} - -export const Secondary = { - render: () => - UI.widgets.button(document, undefined, 'Secondary', action('clicked'), { - buttonColor: 'Secondary', - }), - - name: 'Secondary', -} - -export const PrimaryNeedsBorder = { - render: () => - UI.widgets.button(document, undefined, 'Secondary', action('clicked'), { - needsBorder: true, - }), - - name: 'Primary (needs border)', -} - -export const SecondaryNeedsBorder = { - render: () => - UI.widgets.button(document, undefined, 'Secondary', action('clicked'), { - buttonColor: 'Secondary', - needsBorder: true, - }), - - name: 'Secondary (needs border)', -} - -export const ContinueButton = { - render: () => UI.widgets.continueButton(document, action('clicked')), - name: 'Continue button', -} - -export const CancelButton = { - render: () => UI.widgets.cancelButton(document, action('clicked')), - name: 'Cancel button', -} - -export const DeleteButton = { - render: () => { - const div = document.createElement('div') - const result = UI.widgets.deleteButtonWithCheck( - document, - div, - 'something', - action('deleted') - ) - return div - }, - - name: 'Delete button', -} - -export const ButtonWithIcon = { - render: () => - UI.widgets.button( - document, - 'https://solidproject.org/assets/img/solid-emblem.svg', - 'test', - action('clicked!') - ), - - name: 'Button with icon', -} - -export const FileUploadButton = { - render: () => UI.widgets.fileUploadButtonDiv(document, action('uploaded')), - name: 'File upload button', -} - -export const LinkButton = { - render: () => { - document.outlineManager = { - GotoSubject: action('go to subject'), - } - - return UI.widgets.linkButton( - document, - $rdf.namedNode('http://example.com/') - ) - }, - - name: 'Link button', -} - -export const LinkIcon = { - render: () => - UI.widgets.linkIcon(document, $rdf.namedNode('https://solidproject.org/')), - name: 'Link icon', -} - -export const LinkCustomIcon = { - render: () => - UI.widgets.linkIcon( - document, - $rdf.namedNode('https://solidproject.org/'), - 'https://solidproject.org/favicon.ico' - ), - - name: 'Link custom icon', -} - -export const RemoveButton = { - render: () => { - const div = document.createElement('div') - const p = document.createElement('p') - p.appendChild(document.createTextNode('click x to remove me')) - const button = UI.widgets.removeButton(document, p) - div.appendChild(p) - div.appendChild(button) - return div - }, - - name: 'Remove button', -} diff --git a/src/stories/DateTime.mdx b/src/stories/DateTime.mdx deleted file mode 100644 index 5f348dd37..000000000 --- a/src/stories/DateTime.mdx +++ /dev/null @@ -1,64 +0,0 @@ -import * as UI from '../../src/index' -import * as DateTimeStories from './DateTime.stories'; - -import { Canvas, Meta, Story } from "@storybook/blocks"; - - - -## formatDateTime - -[API docs](https://solidos.github.io/solid-ui/docs/api/modules/_widgets_buttons_.html#formatdatetime) - - - - - - - - - - - - - -## shortDate - -By default, converts e.g. '2020-02-19T19:35:28.557Z' to '19:35' if today is 19 Feb 2020, and to 'Feb 19' if not. - -[API docs](https://solidos.github.io/solid-ui/docs/api/modules/_widgets_buttons_.html#shortdate) - - - - - - - - - - - - - - - - - -## shortTime - -Get a short string representation of the current time - -[API docs](https://solidos.github.io/solid-ui/docs/api/modules/_widgets_buttons_.html#shorttime) - - - - - -## timestamp - -Get a string representation of the current time - -[API docs](https://solidos.github.io/solid-ui/docs/api/modules/_widgets_buttons_.html#timestamp) - - - - diff --git a/src/stories/DateTime.stories.js b/src/stories/DateTime.stories.js deleted file mode 100644 index 7d70dc1d7..000000000 --- a/src/stories/DateTime.stories.js +++ /dev/null @@ -1,56 +0,0 @@ -import * as UI from '../../src/index' - -export default { - title: 'Date & Time', -} - -export const FormatDateTime = { - render: () => - UI.widgets.formatDateTime( - new Date(), - '{FullYear}-{Month}-{Date}T{Hours}:{Minutes}:{Seconds}.{Milliseconds}' - ), - - name: 'formatDateTime', -} - -export const FormatDateTimeDate = { - render: () => UI.widgets.formatDateTime(new Date(), '{Date}.{Month}.{Year}'), - name: 'formatDateTime date', -} - -export const FormatDateTimeTime = { - render: () => - UI.widgets.formatDateTime(new Date(), '{Hours}:{Minutes}:{Seconds}'), - name: 'formatDateTime time', -} - -export const ShortDateNotTodayWithoutTime = { - render: () => UI.widgets.shortDate('2020-01-01T15:43', true), - name: 'shortDate (not today & without time)', -} - -export const ShortDateWithTimeButNotToday = { - render: () => UI.widgets.shortDate('2020-01-01T15:43', false), - name: 'shortDate (with time, but not today)', -} - -export const ShortDateTodayWithoutTime = { - render: () => UI.widgets.shortDate(new Date().toISOString(), true), - name: 'shortDate (today & without time)', -} - -export const ShortDateTodayWithTime = { - render: () => UI.widgets.shortDate(new Date().toISOString(), false), - name: 'shortDate (today & with time)', -} - -export const ShortTime = { - render: () => UI.widgets.shortTime(), - name: 'shortTime', -} - -export const Timestamp = { - render: () => UI.widgets.timestamp(), - name: 'timestamp', -} diff --git a/src/stories/Display.mdx b/src/stories/Display.mdx deleted file mode 100644 index e9cfcfcfc..000000000 --- a/src/stories/Display.mdx +++ /dev/null @@ -1,32 +0,0 @@ -import * as UI from '../../src/index' -import * as DisplayStories from './Display.stories'; - -import { Canvas, Meta, Story } from "@storybook/blocks"; - - - -## complain - -[API docs](https://solidos.github.io/solid-ui/docs/api/modules/_widgets_buttons_.html#complain) - - - - - -## errorMessageBlock - -[API docs](https://solidos.github.io/solid-ui/docs/api/modules/_widgets_error_.html#errormessageblock) - - - - - -## setName - -Sets the best name we have and looks up a better one - -[API docs](https://solidos.github.io/solid-ui/docs/api/modules/_widgets_buttons_.html#setname) - - - - diff --git a/src/stories/Display.stories.js b/src/stories/Display.stories.js deleted file mode 100644 index c99e14518..000000000 --- a/src/stories/Display.stories.js +++ /dev/null @@ -1,43 +0,0 @@ -import * as UI from '../../src/index' - -export default { - title: 'Display', -} - -export const Complain = { - render: () => { - const div = document.createElement('div') - - UI.widgets.complain( - { - div, - dom: document, - }, - 'not good!' - ) - - return div - }, - - name: 'complain', -} - -export const ErrorMessageBlock = { - render: () => { - return UI.widgets.errorMessageBlock(document, 'my error message', '#f0f') - }, - - name: 'errorMessageBlock', -} - -export const SetName = { - render: () => { - const div = document.createElement('div') - const jane = $rdf.namedNode('https://jane.example/person/card#me') - SolidLogic.store.add(jane, UI.ns.foaf('name'), 'Jane Doe') - UI.widgets.setName(div, jane) - return div - }, - - name: 'setName', -} diff --git a/src/stories/DomManipulation.mdx b/src/stories/DomManipulation.mdx deleted file mode 100644 index 7dbb7585a..000000000 --- a/src/stories/DomManipulation.mdx +++ /dev/null @@ -1,36 +0,0 @@ -import * as UI from '../../src/index' -import * as DomManipulationStories from './DomManipulation.stories'; - -import { Canvas, Meta, Story } from "@storybook/blocks"; - - - -## addStyleSheet - -Stick a stylesheet link the document if not already there - -[API docs](https://solidos.github.io/solid-ui/docs/api/modules/_widgets_buttons_.html#addstylesheet) - - - - - -## clearElement - -Remove all the children of an HTML element - -[API docs](https://solidos.github.io/solid-ui/docs/api/modules/_widgets_buttons_.html#clearelement) - - - - - -## refreshTree - -Refresh a DOM tree recursively - -[API docs](https://solidos.github.io/solid-ui/docs/api/modules/_widgets_buttons_.html#refreshtree) - - - - diff --git a/src/stories/DomManipulation.stories.js b/src/stories/DomManipulation.stories.js deleted file mode 100644 index 8bd276d14..000000000 --- a/src/stories/DomManipulation.stories.js +++ /dev/null @@ -1,66 +0,0 @@ -import * as UI from '../../src/index' - -export default { - title: 'DOM manipulation', -} - -export const AddStyleSheet = { - render: () => { - UI.widgets.addStyleSheet( - document, - 'https://linkeddata.github.io/tabulator-firefox/content/tabbedtab.css' - ) - }, - - name: 'addStyleSheet', - withSource: 'open', - - decorators: [ - (Story) => { - Story() - const html = document.querySelector('link').outerHTML - const pre = document.createElement('pre') - const text = document.createTextNode(html) - pre.appendChild(text) - return pre - }, - ], -} - -export const ClearElement = { - render: () => { - let counter = 0 - const div = document.createElement('p') - - setInterval(() => { - if (counter++ % 2 === 0) { - const text = document.createTextNode('Now you see me') - div.appendChild(text) - } else { - UI.widgets.clearElement(div) - } - }, 1000) - - return div - }, - - name: 'clearElement', -} - -export const RefreshTree = { - render: () => { - setInterval(() => { - UI.widgets.refreshTree(document.body) - }, 1000) - - const refreshable = document.createElement('p') - - refreshable.refresh = () => { - refreshable.innerText = new Date().getTime() - } - - return refreshable - }, - - name: 'refreshTree', -} diff --git a/src/stories/DragAndDrop.mdx b/src/stories/DragAndDrop.mdx deleted file mode 100644 index 4c2b245ce..000000000 --- a/src/stories/DragAndDrop.mdx +++ /dev/null @@ -1,39 +0,0 @@ -import * as UI from '../../src/index' -import * as DragAndDropStories from './DragAndDrop.stories'; - -import { Canvas, Meta, Story } from '@storybook/blocks'; -import { action } from '@storybook/addon-actions' - - - -## Drag & Drop - -DragAndDrop has three functions that can be used to handle dragging and dropping of uris, files, and images. - -`makeDraggable` is used to make an element Draggable. Along with the HTML Element, it is also necessary to pass an -object with a uri. This is used to set the data on the EventListener for 'dragstart'. - -`makeDropTarget` is used to make an element enabled as a drop target. Along with the HTML Element, it is also -necessary to pass in two callback functions; one to handle draggable uris and the other to handle draggable files and images. - -`uploadFiles` is used to process the files that a user is uploading. - -Drag the message from the "Draggable" story or drag any file or image and drop it on the element in the "Drop Target" story to see it -work. - - - - - - - - - -## uploadFiles - -The function `uploadFiles` could for example be used in the droppedFileHandler function which is passed to the -makeDropTarget function. - - - - diff --git a/src/stories/DragAndDrop.stories.js b/src/stories/DragAndDrop.stories.js deleted file mode 100644 index 9b7294b9a..000000000 --- a/src/stories/DragAndDrop.stories.js +++ /dev/null @@ -1,68 +0,0 @@ -import * as UI from '../../src/index' - -import { action } from '@storybook/addon-actions' - -export default { - title: 'Drag & Drop', -} - -export const Draggable = { - render: () => { - const dragElement = document.createElement('div') - dragElement.appendChild(document.createTextNode('Drag me to the target!')) - const uri = new $rdf.NamedNode('https://exampleuser.inrupt.net') - UI.widgets.makeDraggable(dragElement, uri) - return dragElement - }, - - name: 'Draggable', -} - -export const DropTarget = { - render: () => { - const target = document.createElement('div') - target.style = - 'padding: 1em; text-align:center; border: 1px solid black; width: 100px; height: 100px' - target.appendChild( - document.createTextNode('Drop things (URIs, files, ...) here') - ) - UI.widgets.makeDropTarget( - target, - action('dropped uri'), - action('dropped file') - ) - return target - }, - - name: 'Drop Target', -} - -export const UploadFiles = { - render: ({ fileBase, pictureBase }) => { - const target = document.createElement('div') - target.style = - 'padding: 1em; text-align:center; border: 1px solid black; width: 100px; height: 100px' - target.appendChild( - document.createTextNode('Drop a file (document, picture, ...) here') - ) - - UI.widgets.makeDropTarget(target, action('dropped uri'), (files) => { - UI.widgets.uploadFiles( - SolidLogic.store.fetcher, - files, - fileBase, - pictureBase, - action('file uploaded successfully') - ) - }) - - return target - }, - - name: 'Upload Files', - - args: { - fileBase: 'https://pod.example/Files', - pictureBase: 'https://pod.example/Pictures', - }, -} diff --git a/src/stories/Events.mdx b/src/stories/Events.mdx deleted file mode 100644 index e453b0dfe..000000000 --- a/src/stories/Events.mdx +++ /dev/null @@ -1,17 +0,0 @@ -import * as UI from '../../src/index' -import * as EventsStories from './Events.stories'; - -import { Canvas, Meta, Story } from "@storybook/blocks"; -import { action } from "@storybook/addon-actions"; - - - -## openHrefInOutlineMode - -Event Handler for links within solid apps. - -[API docs](https://solidos.github.io/solid-ui/docs/api/modules/_widgets_buttons_.html#openhrefinoutlinemode) - - - - diff --git a/src/stories/Events.stories.js b/src/stories/Events.stories.js deleted file mode 100644 index c222f99a0..000000000 --- a/src/stories/Events.stories.js +++ /dev/null @@ -1,28 +0,0 @@ -import * as UI from '../../src/index' - -import { action } from '@storybook/addon-actions' - -export default { - title: 'Events', -} - -export const OpenHrefInOutlineMode = { - render: () => { - document.outlineManager = { - GotoSubject: action('go to subject'), - } - - const anchor = document.createElement('a') - anchor.setAttribute('href', 'http://example.com') - - anchor.onclick = (e) => { - UI.widgets.openHrefInOutlineMode(e) - return false - } - - anchor.appendChild(document.createTextNode('click me')) - return anchor - }, - - name: 'openHrefInOutlineMode', -} diff --git a/src/stories/Header.mdx b/src/stories/Header.mdx deleted file mode 100644 index b54481726..000000000 --- a/src/stories/Header.mdx +++ /dev/null @@ -1,22 +0,0 @@ -import * as UI from '../../src/index' -import * as HeaderStories from './Header.stories'; - -import { Canvas, Meta, Story } from "@storybook/blocks"; - - - -## Header - -