Initial commit: notification-elements-demo app

Interactive Angular 19 demo for @sda/notification-elements-ui with
6 sections: Bell & Feed, Notification Center, Inbox, Comments &
Threads, Mention Input, and Full-Featured layout. Includes mock
data, dark mode toggle, and real-time event log.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Giuliano Silvestro
2026-02-13 21:49:19 +10:00
commit 5d0c9ec7eb
36473 changed files with 3778146 additions and 0 deletions

20
node_modules/postcss-modules-local-by-default/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright 2015 Mark Dalgleish <mark.john.dalgleish@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

123
node_modules/postcss-modules-local-by-default/README.md generated vendored Normal file
View File

@@ -0,0 +1,123 @@
[![Build Status][ci-img]][ci] [![codecov][codecov-img]][codecov] [![npm][npm-img]][npm]
# CSS Modules: Local by Default
Transformation examples:
Selectors (mode `local`, by default)::
<!-- prettier-ignore-start -->
```css
.foo { ... } /* => */ :local(.foo) { ... }
.foo .bar { ... } /* => */ :local(.foo) :local(.bar) { ... }
/* Shorthand global selector */
:global .foo .bar { ... } /* => */ .foo .bar { ... }
.foo :global .bar { ... } /* => */ :local(.foo) .bar { ... }
/* Targeted global selector */
:global(.foo) .bar { ... } /* => */ .foo :local(.bar) { ... }
.foo:global(.bar) { ... } /* => */ :local(.foo).bar { ... }
.foo :global(.bar) .baz { ... } /* => */ :local(.foo) .bar :local(.baz) { ... }
.foo:global(.bar) .baz { ... } /* => */ :local(.foo).bar :local(.baz) { ... }
```
<!-- prettier-ignore-end -->
Declarations (mode `local`, by default):
<!-- prettier-ignore-start -->
```css
.foo {
animation-name: fadeInOut, global(moveLeft300px), local(bounce);
}
.bar {
animation: rotate 1s, global(spin) 3s, local(fly) 6s;
}
/* => */
:local(.foo) {
animation-name: :local(fadeInOut), moveLeft300px, :local(bounce);
}
:local(.bar) {
animation: :local(rotate) 1s, spin 3s, :local(fly) 6s;
}
```
<!-- prettier-ignore-end -->
## Pure Mode
In pure mode, all selectors must contain at least one local class or id
selector
To ignore this rule for a specific selector, add the a `/* cssmodules-pure-ignore */` comment in front
of the selector:
```css
/* cssmodules-pure-ignore */
:global(#modal-backdrop) {
...;
}
```
or by adding a `/* cssmodules-pure-no-check */` comment at the top of a file to disable this check for the whole file:
```css
/* cssmodules-pure-no-check */
:global(#modal-backdrop) {
...;
}
:global(#my-id) {
...;
}
```
## Building
```bash
$ npm install
$ npm test
```
- Build: [![Build Status][ci-img]][ci]
- Lines: [![coveralls][coveralls-img]][coveralls]
- Statements: [![codecov][codecov-img]][codecov]
## Development
```bash
$ yarn test:watch
```
## License
MIT
## With thanks
- [Tobias Koppers](https://github.com/sokra)
- [Glen Maddern](https://github.com/geelen)
---
Mark Dalgleish, 2015.
[ci-img]: https://github.com/css-modules/postcss-modules-local-by-default/actions/workflows/nodejs.yml/badge.svg
[ci]: https://github.com/css-modules/postcss-modules-local-by-default/actions/workflows/nodejs.yml
[npm-img]: https://img.shields.io/npm/v/postcss-modules-local-by-default.svg?style=flat-square
[npm]: https://www.npmjs.com/package/postcss-modules-local-by-default
[coveralls-img]: https://img.shields.io/coveralls/css-modules/postcss-modules-local-by-default/master.svg?style=flat-square
[coveralls]: https://coveralls.io/r/css-modules/postcss-modules-local-by-default?branch=master
[codecov-img]: https://img.shields.io/codecov/c/github/css-modules/postcss-modules-local-by-default/master.svg?style=flat-square
[codecov]: https://codecov.io/github/css-modules/postcss-modules-local-by-default?branch=master

View File

@@ -0,0 +1,52 @@
{
"name": "postcss-modules-local-by-default",
"version": "4.2.0",
"description": "A CSS Modules transform to make local scope the default",
"main": "src/index.js",
"author": "Mark Dalgleish",
"license": "MIT",
"files": [
"src"
],
"repository": {
"type": "git",
"url": "https://github.com/css-modules/postcss-modules-local-by-default.git"
},
"engines": {
"node": "^10 || ^12 || >= 14"
},
"keywords": [
"css-modules",
"postcss",
"css",
"postcss-plugin"
],
"scripts": {
"prettier": "prettier -l --ignore-path .gitignore .",
"eslint": "eslint --ignore-path .gitignore .",
"lint": "yarn eslint && yarn prettier",
"test:only": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage --collectCoverageFrom=\"src/**/*\"",
"test": "yarn test:coverage",
"prepublishOnly": "yarn lint && yarn test"
},
"dependencies": {
"icss-utils": "^5.0.0",
"postcss-selector-parser": "^7.0.0",
"postcss-value-parser": "^4.1.0"
},
"devDependencies": {
"coveralls": "^3.1.0",
"eslint": "^7.10.0",
"eslint-config-prettier": "^6.12.0",
"husky": "^4.3.0",
"jest": "^26.5.2",
"lint-staged": "^10.4.0",
"postcss": "^8.1.0",
"prettier": "^2.1.2"
},
"peerDependencies": {
"postcss": "^8.1.0"
}
}

View File

@@ -0,0 +1,732 @@
"use strict";
const selectorParser = require("postcss-selector-parser");
const valueParser = require("postcss-value-parser");
const { extractICSS } = require("icss-utils");
const IGNORE_FILE_MARKER = "cssmodules-pure-no-check";
const IGNORE_NEXT_LINE_MARKER = "cssmodules-pure-ignore";
const isSpacing = (node) => node.type === "combinator" && node.value === " ";
const isPureCheckDisabled = (root) => {
for (const node of root.nodes) {
if (node.type !== "comment") {
return false;
}
if (node.text.trim().startsWith(IGNORE_FILE_MARKER)) {
return true;
}
}
return false;
};
function getIgnoreComment(node) {
if (!node.parent) {
return;
}
const indexInParent = node.parent.index(node);
for (let i = indexInParent - 1; i >= 0; i--) {
const prevNode = node.parent.nodes[i];
if (prevNode.type === "comment") {
if (prevNode.text.trimStart().startsWith(IGNORE_NEXT_LINE_MARKER)) {
return prevNode;
}
} else {
break;
}
}
}
function normalizeNodeArray(nodes) {
const array = [];
nodes.forEach((x) => {
if (Array.isArray(x)) {
normalizeNodeArray(x).forEach((item) => {
array.push(item);
});
} else if (x) {
array.push(x);
}
});
if (array.length > 0 && isSpacing(array[array.length - 1])) {
array.pop();
}
return array;
}
const isPureSelectorSymbol = Symbol("is-pure-selector");
function localizeNode(rule, mode, localAliasMap) {
const transform = (node, context) => {
if (context.ignoreNextSpacing && !isSpacing(node)) {
throw new Error("Missing whitespace after " + context.ignoreNextSpacing);
}
if (context.enforceNoSpacing && isSpacing(node)) {
throw new Error("Missing whitespace before " + context.enforceNoSpacing);
}
let newNodes;
switch (node.type) {
case "root": {
let resultingGlobal;
context.hasPureGlobals = false;
newNodes = node.nodes.map((n) => {
const nContext = {
global: context.global,
lastWasSpacing: true,
hasLocals: false,
explicit: false,
};
n = transform(n, nContext);
if (typeof resultingGlobal === "undefined") {
resultingGlobal = nContext.global;
} else if (resultingGlobal !== nContext.global) {
throw new Error(
'Inconsistent rule global/local result in rule "' +
node +
'" (multiple selectors must result in the same mode for the rule)'
);
}
if (!nContext.hasLocals) {
context.hasPureGlobals = true;
}
return n;
});
context.global = resultingGlobal;
node.nodes = normalizeNodeArray(newNodes);
break;
}
case "selector": {
newNodes = node.map((childNode) => transform(childNode, context));
node = node.clone();
node.nodes = normalizeNodeArray(newNodes);
break;
}
case "combinator": {
if (isSpacing(node)) {
if (context.ignoreNextSpacing) {
context.ignoreNextSpacing = false;
context.lastWasSpacing = false;
context.enforceNoSpacing = false;
return null;
}
context.lastWasSpacing = true;
return node;
}
break;
}
case "pseudo": {
let childContext;
const isNested = !!node.length;
const isScoped = node.value === ":local" || node.value === ":global";
const isImportExport =
node.value === ":import" || node.value === ":export";
if (isImportExport) {
context.hasLocals = true;
// :local(.foo)
} else if (isNested) {
if (isScoped) {
if (node.nodes.length === 0) {
throw new Error(`${node.value}() can't be empty`);
}
if (context.inside) {
throw new Error(
`A ${node.value} is not allowed inside of a ${context.inside}(...)`
);
}
childContext = {
global: node.value === ":global",
inside: node.value,
hasLocals: false,
explicit: true,
};
newNodes = node
.map((childNode) => transform(childNode, childContext))
.reduce((acc, next) => acc.concat(next.nodes), []);
if (newNodes.length) {
const { before, after } = node.spaces;
const first = newNodes[0];
const last = newNodes[newNodes.length - 1];
first.spaces = { before, after: first.spaces.after };
last.spaces = { before: last.spaces.before, after };
}
node = newNodes;
break;
} else {
childContext = {
global: context.global,
inside: context.inside,
lastWasSpacing: true,
hasLocals: false,
explicit: context.explicit,
};
newNodes = node.map((childNode) => {
const newContext = {
...childContext,
enforceNoSpacing: false,
};
const result = transform(childNode, newContext);
childContext.global = newContext.global;
childContext.hasLocals = newContext.hasLocals;
return result;
});
node = node.clone();
node.nodes = normalizeNodeArray(newNodes);
if (childContext.hasLocals) {
context.hasLocals = true;
}
}
break;
//:local .foo .bar
} else if (isScoped) {
if (context.inside) {
throw new Error(
`A ${node.value} is not allowed inside of a ${context.inside}(...)`
);
}
const addBackSpacing = !!node.spaces.before;
context.ignoreNextSpacing = context.lastWasSpacing
? node.value
: false;
context.enforceNoSpacing = context.lastWasSpacing
? false
: node.value;
context.global = node.value === ":global";
context.explicit = true;
// because this node has spacing that is lost when we remove it
// we make up for it by adding an extra combinator in since adding
// spacing on the parent selector doesn't work
return addBackSpacing
? selectorParser.combinator({ value: " " })
: null;
}
break;
}
case "id":
case "class": {
if (!node.value) {
throw new Error("Invalid class or id selector syntax");
}
if (context.global) {
break;
}
const isImportedValue = localAliasMap.has(node.value);
const isImportedWithExplicitScope = isImportedValue && context.explicit;
if (!isImportedValue || isImportedWithExplicitScope) {
const innerNode = node.clone();
innerNode.spaces = { before: "", after: "" };
node = selectorParser.pseudo({
value: ":local",
nodes: [innerNode],
spaces: node.spaces,
});
context.hasLocals = true;
}
break;
}
case "nesting": {
if (node.value === "&") {
context.hasLocals = rule.parent[isPureSelectorSymbol];
}
}
}
context.lastWasSpacing = false;
context.ignoreNextSpacing = false;
context.enforceNoSpacing = false;
return node;
};
const rootContext = {
global: mode === "global",
hasPureGlobals: false,
};
rootContext.selector = selectorParser((root) => {
transform(root, rootContext);
}).processSync(rule, { updateSelector: false, lossless: true });
return rootContext;
}
function localizeDeclNode(node, context) {
switch (node.type) {
case "word":
if (context.localizeNextItem) {
if (!context.localAliasMap.has(node.value)) {
node.value = ":local(" + node.value + ")";
context.localizeNextItem = false;
}
}
break;
case "function":
if (
context.options &&
context.options.rewriteUrl &&
node.value.toLowerCase() === "url"
) {
node.nodes.map((nestedNode) => {
if (nestedNode.type !== "string" && nestedNode.type !== "word") {
return;
}
let newUrl = context.options.rewriteUrl(
context.global,
nestedNode.value
);
switch (nestedNode.type) {
case "string":
if (nestedNode.quote === "'") {
newUrl = newUrl.replace(/(\\)/g, "\\$1").replace(/'/g, "\\'");
}
if (nestedNode.quote === '"') {
newUrl = newUrl.replace(/(\\)/g, "\\$1").replace(/"/g, '\\"');
}
break;
case "word":
newUrl = newUrl.replace(/("|'|\)|\\)/g, "\\$1");
break;
}
nestedNode.value = newUrl;
});
}
break;
}
return node;
}
// `none` is special value, other is global values
const specialKeywords = [
"none",
"inherit",
"initial",
"revert",
"revert-layer",
"unset",
];
function localizeDeclarationValues(localize, declaration, context) {
const valueNodes = valueParser(declaration.value);
valueNodes.walk((node, index, nodes) => {
if (
node.type === "function" &&
(node.value.toLowerCase() === "var" || node.value.toLowerCase() === "env")
) {
return false;
}
if (
node.type === "word" &&
specialKeywords.includes(node.value.toLowerCase())
) {
return;
}
const subContext = {
options: context.options,
global: context.global,
localizeNextItem: localize && !context.global,
localAliasMap: context.localAliasMap,
};
nodes[index] = localizeDeclNode(node, subContext);
});
declaration.value = valueNodes.toString();
}
// letter
// An uppercase letter or a lowercase letter.
//
// ident-start code point
// A letter, a non-ASCII code point, or U+005F LOW LINE (_).
//
// ident code point
// An ident-start code point, a digit, or U+002D HYPHEN-MINUS (-).
// We don't validate `hex digits`, because we don't need it, it is work of linters.
const validIdent =
/^-?([a-z\u0080-\uFFFF_]|(\\[^\r\n\f])|-(?![0-9]))((\\[^\r\n\f])|[a-z\u0080-\uFFFF_0-9-])*$/i;
/*
The spec defines some keywords that you can use to describe properties such as the timing
function. These are still valid animation names, so as long as there is a property that accepts
a keyword, it is given priority. Only when all the properties that can take a keyword are
exhausted can the animation name be set to the keyword. I.e.
animation: infinite infinite;
The animation will repeat an infinite number of times from the first argument, and will have an
animation name of infinite from the second.
*/
const animationKeywords = {
// animation-direction
$normal: 1,
$reverse: 1,
$alternate: 1,
"$alternate-reverse": 1,
// animation-fill-mode
$forwards: 1,
$backwards: 1,
$both: 1,
// animation-iteration-count
$infinite: 1,
// animation-play-state
$paused: 1,
$running: 1,
// animation-timing-function
$ease: 1,
"$ease-in": 1,
"$ease-out": 1,
"$ease-in-out": 1,
$linear: 1,
"$step-end": 1,
"$step-start": 1,
// Special
$none: Infinity, // No matter how many times you write none, it will never be an animation name
// Global values
$initial: Infinity,
$inherit: Infinity,
$unset: Infinity,
$revert: Infinity,
"$revert-layer": Infinity,
};
function localizeDeclaration(declaration, context) {
const isAnimation = /animation(-name)?$/i.test(declaration.prop);
if (isAnimation) {
let parsedAnimationKeywords = {};
const valueNodes = valueParser(declaration.value).walk((node) => {
// If div-token appeared (represents as comma ','), a possibility of an animation-keywords should be reflesh.
if (node.type === "div") {
parsedAnimationKeywords = {};
return;
} else if (
node.type === "function" &&
node.value.toLowerCase() === "local" &&
node.nodes.length === 1
) {
node.type = "word";
node.value = node.nodes[0].value;
return localizeDeclNode(node, {
options: context.options,
global: context.global,
localizeNextItem: true,
localAliasMap: context.localAliasMap,
});
} else if (node.type === "function") {
// replace `animation: global(example)` with `animation-name: example`
if (node.value.toLowerCase() === "global" && node.nodes.length === 1) {
node.type = "word";
node.value = node.nodes[0].value;
}
// Do not handle nested functions
return false;
}
// Ignore all except word
else if (node.type !== "word") {
return;
}
const value = node.type === "word" ? node.value.toLowerCase() : null;
let shouldParseAnimationName = false;
if (value && validIdent.test(value)) {
if ("$" + value in animationKeywords) {
parsedAnimationKeywords["$" + value] =
"$" + value in parsedAnimationKeywords
? parsedAnimationKeywords["$" + value] + 1
: 0;
shouldParseAnimationName =
parsedAnimationKeywords["$" + value] >=
animationKeywords["$" + value];
} else {
shouldParseAnimationName = true;
}
}
return localizeDeclNode(node, {
options: context.options,
global: context.global,
localizeNextItem: shouldParseAnimationName && !context.global,
localAliasMap: context.localAliasMap,
});
});
declaration.value = valueNodes.toString();
return;
}
if (/url\(/i.test(declaration.value)) {
return localizeDeclarationValues(false, declaration, context);
}
}
const isPureSelector = (context, rule) => {
if (!rule.parent || rule.type === "root") {
return !context.hasPureGlobals;
}
if (rule.type === "rule" && rule[isPureSelectorSymbol]) {
return rule[isPureSelectorSymbol] || isPureSelector(context, rule.parent);
}
return !context.hasPureGlobals || isPureSelector(context, rule.parent);
};
const isNodeWithoutDeclarations = (rule) => {
if (rule.nodes.length > 0) {
return !rule.nodes.every(
(item) =>
item.type === "rule" ||
(item.type === "atrule" && !isNodeWithoutDeclarations(item))
);
}
return true;
};
module.exports = (options = {}) => {
if (
options &&
options.mode &&
options.mode !== "global" &&
options.mode !== "local" &&
options.mode !== "pure"
) {
throw new Error(
'options.mode must be either "global", "local" or "pure" (default "local")'
);
}
const pureMode = options && options.mode === "pure";
const globalMode = options && options.mode === "global";
return {
postcssPlugin: "postcss-modules-local-by-default",
prepare() {
const localAliasMap = new Map();
return {
Once(root) {
const { icssImports } = extractICSS(root, false);
const enforcePureMode = pureMode && !isPureCheckDisabled(root);
Object.keys(icssImports).forEach((key) => {
Object.keys(icssImports[key]).forEach((prop) => {
localAliasMap.set(prop, icssImports[key][prop]);
});
});
root.walkAtRules((atRule) => {
if (/keyframes$/i.test(atRule.name)) {
const globalMatch = /^\s*:global\s*\((.+)\)\s*$/.exec(
atRule.params
);
const localMatch = /^\s*:local\s*\((.+)\)\s*$/.exec(
atRule.params
);
let globalKeyframes = globalMode;
if (globalMatch) {
if (enforcePureMode) {
const ignoreComment = getIgnoreComment(atRule);
if (!ignoreComment) {
throw atRule.error(
"@keyframes :global(...) is not allowed in pure mode"
);
} else {
ignoreComment.remove();
}
}
atRule.params = globalMatch[1];
globalKeyframes = true;
} else if (localMatch) {
atRule.params = localMatch[0];
globalKeyframes = false;
} else if (
atRule.params &&
!globalMode &&
!localAliasMap.has(atRule.params)
) {
atRule.params = ":local(" + atRule.params + ")";
}
atRule.walkDecls((declaration) => {
localizeDeclaration(declaration, {
localAliasMap,
options: options,
global: globalKeyframes,
});
});
} else if (/scope$/i.test(atRule.name)) {
if (atRule.params) {
const ignoreComment = pureMode
? getIgnoreComment(atRule)
: undefined;
if (ignoreComment) {
ignoreComment.remove();
}
atRule.params = atRule.params
.split("to")
.map((item) => {
const selector = item.trim().slice(1, -1).trim();
const context = localizeNode(
selector,
options.mode,
localAliasMap
);
context.options = options;
context.localAliasMap = localAliasMap;
if (
enforcePureMode &&
context.hasPureGlobals &&
!ignoreComment
) {
throw atRule.error(
'Selector in at-rule"' +
selector +
'" is not pure ' +
"(pure selectors must contain at least one local class or id)"
);
}
return `(${context.selector})`;
})
.join(" to ");
}
atRule.nodes.forEach((declaration) => {
if (declaration.type === "decl") {
localizeDeclaration(declaration, {
localAliasMap,
options: options,
global: globalMode,
});
}
});
} else if (atRule.nodes) {
atRule.nodes.forEach((declaration) => {
if (declaration.type === "decl") {
localizeDeclaration(declaration, {
localAliasMap,
options: options,
global: globalMode,
});
}
});
}
});
root.walkRules((rule) => {
if (
rule.parent &&
rule.parent.type === "atrule" &&
/keyframes$/i.test(rule.parent.name)
) {
// ignore keyframe rules
return;
}
const context = localizeNode(rule, options.mode, localAliasMap);
context.options = options;
context.localAliasMap = localAliasMap;
const ignoreComment = enforcePureMode
? getIgnoreComment(rule)
: undefined;
const isNotPure = enforcePureMode && !isPureSelector(context, rule);
if (
isNotPure &&
isNodeWithoutDeclarations(rule) &&
!ignoreComment
) {
throw rule.error(
'Selector "' +
rule.selector +
'" is not pure ' +
"(pure selectors must contain at least one local class or id)"
);
} else if (ignoreComment) {
ignoreComment.remove();
}
if (pureMode) {
rule[isPureSelectorSymbol] = !isNotPure;
}
rule.selector = context.selector;
// Less-syntax mixins parse as rules with no nodes
if (rule.nodes) {
rule.nodes.forEach((declaration) =>
localizeDeclaration(declaration, context)
);
}
});
},
};
},
};
};
module.exports.postcss = true;