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>
246 lines
8.1 KiB
JavaScript
246 lines
8.1 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.toMatcher = exports.toRegex = void 0;
|
|
const escapeRe = (ch) => (/[.^$+{}()|\\]/.test(ch) ? `\\${ch}` : ch);
|
|
/**
|
|
* Parse an extended glob pattern like ?(a|b|c)
|
|
* Returns the regex string equivalent and the new index position
|
|
*/
|
|
const parseExtGlob = (pattern, startIdx, prefix, options) => {
|
|
let i = startIdx; // startIdx should be pointing at the character after '('
|
|
const parts = [];
|
|
let cur = '';
|
|
let depth = 1; // Track parenthesis depth for nested patterns
|
|
while (i < pattern.length && depth > 0) {
|
|
const ch = pattern[i];
|
|
if (ch === '(') {
|
|
depth++;
|
|
cur += ch;
|
|
i++;
|
|
}
|
|
else if (ch === ')') {
|
|
depth--;
|
|
if (depth === 0) {
|
|
// Found the closing parenthesis
|
|
parts.push(cur);
|
|
i++; // consume ')'
|
|
break;
|
|
}
|
|
else {
|
|
cur += ch;
|
|
i++;
|
|
}
|
|
}
|
|
else if (ch === '|' && depth === 1) {
|
|
// Pipe separator at top level of this extglob
|
|
parts.push(cur);
|
|
cur = '';
|
|
i++;
|
|
}
|
|
else {
|
|
cur += ch;
|
|
i++;
|
|
}
|
|
}
|
|
if (depth !== 0)
|
|
return; // Unclosed parenthesis
|
|
let alternatives = '';
|
|
const length = parts.length;
|
|
for (let j = 0; j < length; j++)
|
|
alternatives += (alternatives ? '|' : '') + (0, exports.toRegex)(parts[j], options).source.replace(/^\^/, '').replace(/\$$/, '');
|
|
switch (prefix) {
|
|
case '?': // zero or one
|
|
return [`(?:${alternatives})?`, i];
|
|
case '*': // zero or more
|
|
return [`(?:${alternatives})*`, i];
|
|
case '+': // one or more
|
|
return [`(?:${alternatives})+`, i];
|
|
case '@': // exactly one
|
|
return [`(?:${alternatives})`, i];
|
|
case '!': // none of (negative match)
|
|
// For negation, we need to match anything that doesn't match the pattern
|
|
// Use negative lookahead without consuming characters after
|
|
return [`(?!${alternatives})[^/]*`, i];
|
|
}
|
|
return;
|
|
};
|
|
/**
|
|
* Convert a glob pattern to a regular expression
|
|
*
|
|
* Supports:
|
|
* - `/` to separate path segments
|
|
* - `*` to match zero or more characters in a path segment
|
|
* - `?` to match one character in a path segment
|
|
* - `**` to match any number of path segments, including none
|
|
* - `{}` to group conditions (e.g. `{html,txt}`)
|
|
* - `[abc]`, `[a-z]`, `[!a-z]`, `[!abc]` character classes
|
|
* - Extended globbing (when `extglob: true` option is set):
|
|
* - `?(pattern-list)` zero or one occurrence
|
|
* - `*(pattern-list)` zero or more occurrences
|
|
* - `+(pattern-list)` one or more occurrences
|
|
* - `@(pattern-list)` exactly one of the patterns
|
|
* - `!(pattern-list)` anything except the patterns
|
|
*/
|
|
const toRegex = (pattern, options) => {
|
|
let regexStr = '';
|
|
let i = 0;
|
|
// Helper to parse a brace group like {a,b,c}. No nesting support.
|
|
const parseBraceGroup = () => {
|
|
// Assume current char is '{'
|
|
i++; // skip '{'
|
|
const parts = [];
|
|
let cur = '';
|
|
let closed = false;
|
|
while (i < pattern.length) {
|
|
const ch = pattern[i];
|
|
if (ch === '}') {
|
|
parts.push(cur);
|
|
i++; // consume '}'
|
|
closed = true;
|
|
break;
|
|
}
|
|
if (ch === ',') {
|
|
parts.push(cur);
|
|
cur = '';
|
|
i++;
|
|
continue;
|
|
}
|
|
cur += ch;
|
|
i++;
|
|
}
|
|
if (!closed) {
|
|
// treat as literal '{...'
|
|
return '\\{' + escapeRe(cur);
|
|
}
|
|
// Convert each part recursively to support globs inside braces
|
|
const alt = parts.map((p) => (0, exports.toRegex)(p, options).source.replace(/^\^/, '').replace(/\$$/, '')).join('|');
|
|
return `(?:${alt})`;
|
|
};
|
|
const extglob = !!options?.extglob;
|
|
while (i < pattern.length) {
|
|
const char = pattern[i];
|
|
// Check for extended glob patterns when extglob is enabled
|
|
if (extglob && pattern[i + 1] === '(') {
|
|
if (char === '?' || char === '*' || char === '+' || char === '@' || char === '!') {
|
|
const result = parseExtGlob(pattern, i + 2, char, options);
|
|
if (result) {
|
|
regexStr += result[0];
|
|
i = result[1];
|
|
continue;
|
|
}
|
|
// If parse failed, fall through to normal handling
|
|
}
|
|
}
|
|
switch (char) {
|
|
case '*': {
|
|
// Check for double star **
|
|
if (pattern[i + 1] === '*') {
|
|
// Collapse consecutive * beyond two (e.g., *** -> **)
|
|
let j = i + 2;
|
|
while (pattern[j] === '*')
|
|
j++;
|
|
// If followed by a slash, make it optional to allow zero segments
|
|
if (pattern[j] === '/') {
|
|
regexStr += '(?:.*/)?';
|
|
i = j + 1; // consume **/
|
|
}
|
|
else {
|
|
regexStr += '.*';
|
|
i = j; // consume **
|
|
}
|
|
}
|
|
else {
|
|
regexStr += '[^/]*';
|
|
i++;
|
|
}
|
|
break;
|
|
}
|
|
case '?':
|
|
regexStr += '[^/]';
|
|
i++;
|
|
break;
|
|
case '[': {
|
|
// Copy character class as-is with support for leading '!'
|
|
let cls = '[';
|
|
i++;
|
|
if (i < pattern.length && pattern[i] === '!') {
|
|
cls += '^';
|
|
i++;
|
|
}
|
|
// if first after [ or [^ is ']' include it literally
|
|
if (i < pattern.length && pattern[i] === ']') {
|
|
cls += ']';
|
|
i++;
|
|
}
|
|
while (i < pattern.length && pattern[i] !== ']') {
|
|
const ch = pattern[i];
|
|
// Escape backslash inside class
|
|
cls += ch === '\\' ? '\\\\' : ch;
|
|
i++;
|
|
}
|
|
if (i < pattern.length && pattern[i] === ']') {
|
|
cls += ']';
|
|
i++;
|
|
}
|
|
else {
|
|
// Unclosed class -> treat '[' literally
|
|
regexStr += '\\[';
|
|
continue;
|
|
}
|
|
regexStr += cls;
|
|
break;
|
|
}
|
|
case '{': {
|
|
regexStr += parseBraceGroup();
|
|
break;
|
|
}
|
|
case '/':
|
|
regexStr += '/';
|
|
i++;
|
|
break;
|
|
case '.':
|
|
case '^':
|
|
case '$':
|
|
case '+':
|
|
case '(':
|
|
case ')':
|
|
case '|':
|
|
case '\\':
|
|
regexStr += `\\${char}`;
|
|
i++;
|
|
break;
|
|
default:
|
|
regexStr += char;
|
|
i++;
|
|
break;
|
|
}
|
|
}
|
|
const flags = options?.nocase ? 'i' : '';
|
|
return new RegExp('^' + regexStr + '$', flags);
|
|
};
|
|
exports.toRegex = toRegex;
|
|
const isRegExp = /^\/(.{1,4096})\/([gimsuy]{0,6})$/;
|
|
const toMatcher = (pattern, options) => {
|
|
const regexes = [];
|
|
const patterns = Array.isArray(pattern) ? pattern : [pattern];
|
|
for (const pat of patterns) {
|
|
if (typeof pat === 'string') {
|
|
const match = isRegExp.exec(pat);
|
|
if (match) {
|
|
const [, expr, flags] = match;
|
|
regexes.push(new RegExp(expr, flags));
|
|
}
|
|
else {
|
|
regexes.push((0, exports.toRegex)(pat, options));
|
|
}
|
|
}
|
|
else {
|
|
regexes.push(pat);
|
|
}
|
|
}
|
|
return regexes.length
|
|
? new Function('p', 'return ' + regexes.map((r) => r + '.test(p)').join('||'))
|
|
: () => false;
|
|
};
|
|
exports.toMatcher = toMatcher;
|