@truenas/ui-components 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +335 -0
- package/assets/tn-icons/custom/cloud-off.svg +1 -0
- package/assets/tn-icons/custom/dataset-root.svg +3 -0
- package/assets/tn-icons/custom/dataset.svg +3 -0
- package/assets/tn-icons/custom/enclosure.svg +21 -0
- package/assets/tn-icons/custom/ha-disabled.svg +10 -0
- package/assets/tn-icons/custom/ha-enabled.svg +1 -0
- package/assets/tn-icons/custom/ha-reconnecting.svg +10 -0
- package/assets/tn-icons/custom/hdd-mirror.svg +3 -0
- package/assets/tn-icons/custom/hdd.svg +3 -0
- package/assets/tn-icons/custom/iscsi-share.svg +3 -0
- package/assets/tn-icons/custom/layout-full.svg +8 -0
- package/assets/tn-icons/custom/layout-half-and-quarters.svg +7 -0
- package/assets/tn-icons/custom/layout-halves.svg +6 -0
- package/assets/tn-icons/custom/layout-quarters-and-half.svg +7 -0
- package/assets/tn-icons/custom/layout-quarters.svg +8 -0
- package/assets/tn-icons/custom/network-upload-download-both.svg +4 -0
- package/assets/tn-icons/custom/network-upload-download-disabled.svg +7 -0
- package/assets/tn-icons/custom/network-upload-download-down.svg +4 -0
- package/assets/tn-icons/custom/network-upload-download-up.svg +4 -0
- package/assets/tn-icons/custom/network-upload-download.svg +4 -0
- package/assets/tn-icons/custom/nfs-share.svg +3 -0
- package/assets/tn-icons/custom/nvme-share.svg +7 -0
- package/assets/tn-icons/custom/replication.svg +14 -0
- package/assets/tn-icons/custom/smb-share.svg +3 -0
- package/assets/tn-icons/custom/ssd-mirror.svg +3 -0
- package/assets/tn-icons/custom/ssd.svg +3 -0
- package/assets/tn-icons/custom/true-cloud.svg +10 -0
- package/assets/tn-icons/custom/truecommand-logo-mark-color.svg +14 -0
- package/assets/tn-icons/custom/truecommand-logo-mark.svg +10 -0
- package/assets/tn-icons/custom/truenas-connect-logo.svg +1 -0
- package/assets/tn-icons/custom/truenas-logo-ce-color.svg +1 -0
- package/assets/tn-icons/custom/truenas-logo-ce.svg +4 -0
- package/assets/tn-icons/custom/truenas-logo-enterprise-color.svg +19 -0
- package/assets/tn-icons/custom/truenas-logo-enterprise.svg +13 -0
- package/assets/tn-icons/custom/truenas-logo-mark-color.svg +7 -0
- package/assets/tn-icons/custom/truenas-logo-mark.svg +4 -0
- package/assets/tn-icons/custom/truenas-logo-type-color.svg +4 -0
- package/assets/tn-icons/custom/truenas-logo-type.svg +4 -0
- package/assets/tn-icons/custom/truenas-logo.svg +4 -0
- package/assets/tn-icons/custom/two-factor-auth.svg +1 -0
- package/assets/tn-icons/sprite-config.json +78 -0
- package/assets/tn-icons/sprite.svg +1 -0
- package/fesm2022/truenas-ui-components.mjs +8063 -0
- package/fesm2022/truenas-ui-components.mjs.map +1 -0
- package/package.json +45 -0
- package/scripts/icon-sprite/cli-main.ts +174 -0
- package/scripts/icon-sprite/cli-wrapper.js +32 -0
- package/scripts/icon-sprite/cli.js +30 -0
- package/scripts/icon-sprite/generate-sprite.ts +171 -0
- package/scripts/icon-sprite/lib/add-custom-icons.ts +33 -0
- package/scripts/icon-sprite/lib/build-sprite.ts +25 -0
- package/scripts/icon-sprite/lib/find-icons-in-templates.ts +108 -0
- package/scripts/icon-sprite/lib/find-icons-with-marker.ts +63 -0
- package/scripts/icon-sprite/lib/get-icon-paths.ts +95 -0
- package/scripts/icon-sprite/lib/warn-about-duplicates.ts +13 -0
- package/scripts/icon-sprite/make-sprite.ts +26 -0
- package/scripts/icon-sprite/sprite-config-interface.ts +72 -0
- package/src/assets/icons/dataset.svg +3 -0
- package/src/styles/dialog.css +150 -0
- package/src/styles/themes.css +665 -0
- package/types/truenas-ui-components.d.ts +2687 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import { resolve } from 'path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Find the node_modules directory by checking project root and parent directories
|
|
6
|
+
*/
|
|
7
|
+
function findNodeModules(projectRoot: string): string {
|
|
8
|
+
// Try project root first
|
|
9
|
+
const projectNodeModules = resolve(projectRoot, 'node_modules');
|
|
10
|
+
if (fs.existsSync(projectNodeModules)) {
|
|
11
|
+
return projectNodeModules;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Try parent directory (for monorepo/library structure)
|
|
15
|
+
const parentNodeModules = resolve(projectRoot, '../../node_modules');
|
|
16
|
+
if (fs.existsSync(parentNodeModules)) {
|
|
17
|
+
return parentNodeModules;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Fallback to project root (will error later if doesn't exist)
|
|
21
|
+
return projectNodeModules;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function getIconPaths(names: Set<string>, projectRoot: string = process.cwd()): Map<string, string> {
|
|
25
|
+
const iconPaths = new Map<string, string>();
|
|
26
|
+
const nodeModulesPath = findNodeModules(projectRoot);
|
|
27
|
+
|
|
28
|
+
names.forEach((name) => {
|
|
29
|
+
// Library custom icons (tn- prefix) - ONLY from library package
|
|
30
|
+
if (name.startsWith('tn-')) {
|
|
31
|
+
// When building the library itself, icons are in the source directory
|
|
32
|
+
// When consuming the library, icons are in node_modules
|
|
33
|
+
const sourceLibraryPath = resolve(projectRoot, `assets/tn-icons/custom/${name.slice(3)}.svg`);
|
|
34
|
+
const installedLibraryPath = resolve(nodeModulesPath, `@truenas/ui-components/assets/tn-icons/custom/${name.slice(3)}.svg`);
|
|
35
|
+
|
|
36
|
+
// Check source first (for library development), then installed package (for consumers)
|
|
37
|
+
if (fs.existsSync(sourceLibraryPath)) {
|
|
38
|
+
iconPaths.set(name, sourceLibraryPath);
|
|
39
|
+
} else if (fs.existsSync(installedLibraryPath)) {
|
|
40
|
+
iconPaths.set(name, installedLibraryPath);
|
|
41
|
+
} else {
|
|
42
|
+
console.warn(`⚠ Library custom icon not found: ${name} (looking for ${name.slice(3)}.svg in library)`);
|
|
43
|
+
// Fallback to source path (will error if doesn't exist, which is expected)
|
|
44
|
+
iconPaths.set(name, sourceLibraryPath);
|
|
45
|
+
}
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Consumer custom icons (app- prefix) - ONLY from consumer's assets
|
|
50
|
+
if (name.startsWith('app-')) {
|
|
51
|
+
// Try src/assets first (Angular convention), then assets (fallback)
|
|
52
|
+
const srcAssetsPath = resolve(projectRoot, `src/assets/icons/custom/${name.slice(4)}.svg`);
|
|
53
|
+
const assetsPath = resolve(projectRoot, `assets/icons/custom/${name.slice(4)}.svg`);
|
|
54
|
+
|
|
55
|
+
let localPath = srcAssetsPath;
|
|
56
|
+
if (!fs.existsSync(srcAssetsPath) && fs.existsSync(assetsPath)) {
|
|
57
|
+
localPath = assetsPath;
|
|
58
|
+
} else if (!fs.existsSync(srcAssetsPath)) {
|
|
59
|
+
console.warn(`⚠ Consumer custom icon not found: ${name} (looking for ${name.slice(4)}.svg in src/assets/icons/custom/ or assets/icons/custom/)`);
|
|
60
|
+
}
|
|
61
|
+
iconPaths.set(name, localPath);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// MDI icons
|
|
66
|
+
if (name.startsWith('mdi-')) {
|
|
67
|
+
const mdiPath = resolve(nodeModulesPath, `@mdi/svg/svg/${name.slice(4)}.svg`);
|
|
68
|
+
if (!fs.existsSync(mdiPath)) {
|
|
69
|
+
console.warn(`⚠ MDI icon not found: ${name} (looking for ${name.slice(4)}.svg in @mdi/svg)`);
|
|
70
|
+
console.warn(` This icon may not exist in the current MDI version. Check https://pictogrammers.com/library/mdi/`);
|
|
71
|
+
}
|
|
72
|
+
iconPaths.set(name, mdiPath);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Material Design Icons (mat- prefix)
|
|
77
|
+
if (name.startsWith('mat-')) {
|
|
78
|
+
const materialPath = resolve(nodeModulesPath, `@material-design-icons/svg/filled/${name.slice(4)}.svg`);
|
|
79
|
+
if (!fs.existsSync(materialPath)) {
|
|
80
|
+
console.warn(`⚠ Material icon not found: ${name} (looking for ${name.slice(4)}.svg in @material-design-icons)`);
|
|
81
|
+
}
|
|
82
|
+
iconPaths.set(name, materialPath);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Fallback for icons without recognized prefix (should be rare)
|
|
87
|
+
const materialPath = resolve(nodeModulesPath, `@material-design-icons/svg/filled/${name}.svg`);
|
|
88
|
+
if (!fs.existsSync(materialPath)) {
|
|
89
|
+
console.warn(`⚠ Material icon not found: ${name} (looking for ${name}.svg in @material-design-icons)`);
|
|
90
|
+
}
|
|
91
|
+
iconPaths.set(name, materialPath);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
return iconPaths;
|
|
95
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function warnAboutDuplicates(icons: Set<string>): void {
|
|
2
|
+
icons.forEach((icon) => {
|
|
3
|
+
if (icon.startsWith('mdi-')) {
|
|
4
|
+
return;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
if (!icons.has(`mdi-${icon}`)) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
console.warn(`Both "${icon}" and "mdi-${icon}" are used in the application. Consider only using the 'mdi' version.`);
|
|
12
|
+
});
|
|
13
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { resolve, dirname } from 'path';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
import { generateSprite } from './generate-sprite.js';
|
|
4
|
+
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = dirname(__filename);
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Generate sprite for the truenas-ui library itself
|
|
10
|
+
* This uses the library-specific paths for development
|
|
11
|
+
*/
|
|
12
|
+
async function makeSprite(): Promise<void> {
|
|
13
|
+
// Library structure: scripts are in projects/truenas-ui/scripts/icon-sprite/
|
|
14
|
+
// Source files are in projects/truenas-ui/src/
|
|
15
|
+
// Assets are in projects/truenas-ui/assets/
|
|
16
|
+
const projectRoot = resolve(__dirname, '../..');
|
|
17
|
+
|
|
18
|
+
await generateSprite({
|
|
19
|
+
projectRoot,
|
|
20
|
+
srcDirs: ['./src/lib', './src/stories'],
|
|
21
|
+
// outputDir defaults to './assets/tn-icons' (namespaced to avoid collisions)
|
|
22
|
+
customIconsDir: './assets/tn-icons/custom',
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
makeSprite();
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default base path for sprite assets (namespaced to avoid collisions with consumer apps)
|
|
3
|
+
*/
|
|
4
|
+
export const defaultSpriteBasePath = 'assets/tn-icons';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Configuration options for icon sprite generation
|
|
8
|
+
*/
|
|
9
|
+
export interface SpriteGeneratorConfig {
|
|
10
|
+
/**
|
|
11
|
+
* Source directories to scan for icon usage
|
|
12
|
+
* Defaults to ['./src/lib', './src/app']
|
|
13
|
+
*/
|
|
14
|
+
srcDirs?: string[];
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Output directory for generated sprite files
|
|
18
|
+
* Defaults to `./${defaultSpriteBasePath}`
|
|
19
|
+
*/
|
|
20
|
+
outputDir?: string;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Runtime URL path for the sprite (used in sprite-config.json)
|
|
24
|
+
* If not specified, defaults to outputDir with './' stripped
|
|
25
|
+
*
|
|
26
|
+
* Use this when your build process transforms paths (e.g., Angular strips 'src/')
|
|
27
|
+
* Example: outputDir='./src/assets/tn-icons', spriteUrlPath='assets/tn-icons'
|
|
28
|
+
*/
|
|
29
|
+
spriteUrlPath?: string;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Optional directory containing custom SVG icons
|
|
33
|
+
* Custom icons will be automatically prefixed with 'tn-'
|
|
34
|
+
*/
|
|
35
|
+
customIconsDir?: string;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Project root directory (for resolving relative paths)
|
|
39
|
+
* Defaults to process.cwd()
|
|
40
|
+
*/
|
|
41
|
+
projectRoot?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Resolved configuration with all defaults applied
|
|
46
|
+
*/
|
|
47
|
+
export interface ResolvedSpriteConfig {
|
|
48
|
+
srcDirs: string[];
|
|
49
|
+
outputDir: string;
|
|
50
|
+
spriteUrlPath: string;
|
|
51
|
+
customIconsDir: string | null;
|
|
52
|
+
projectRoot: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Applies defaults to partial configuration
|
|
57
|
+
*/
|
|
58
|
+
export function resolveConfig(config: SpriteGeneratorConfig = {}): ResolvedSpriteConfig {
|
|
59
|
+
const projectRoot = config.projectRoot || process.cwd();
|
|
60
|
+
const outputDir = config.outputDir || `./${defaultSpriteBasePath}`;
|
|
61
|
+
|
|
62
|
+
// If spriteUrlPath is not provided, derive it from outputDir by stripping './'
|
|
63
|
+
const spriteUrlPath = config.spriteUrlPath || outputDir.replace(/^\.\//, '');
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
projectRoot,
|
|
67
|
+
srcDirs: config.srcDirs || ['./src/lib', './src/app'],
|
|
68
|
+
outputDir,
|
|
69
|
+
spriteUrlPath,
|
|
70
|
+
customIconsDir: config.customIconsDir || null,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M6 8C5.72 8 5.48 7.9 5.29 7.71C5.1 7.52 5 7.28 5 7C5 6.72 5.1 6.48 5.29 6.29C5.48 6.1 5.72 6 6 6H18C18.28 6 18.52 6.1 18.71 6.29C18.9 6.48 19 6.72 19 7C19 7.28 18.9 7.52 18.71 7.71C18.52 7.9 18.28 8 18 8H6ZM8 5C7.72 5 7.48 4.9 7.29 4.71C7.1 4.52 7 4.28 7 4C7 3.72 7.1 3.48 7.29 3.29C7.48 3.1 7.72 3 8 3H16C16.28 3 16.52 3.1 16.71 3.29C16.9 3.48 17 3.72 17 4C17 4.28 16.9 4.52 16.71 4.71C16.52 4.9 16.28 5 16 5H8ZM3 9L5 21H19L21 9H3ZM14.71 15.1C14.52 15.29 14.28 15.39 14 15.39H10C9.72 15.39 9.48 15.29 9.29 15.1C9.1 14.91 9 14.67 9 14.39C9 14.11 9.1 13.87 9.29 13.68C9.48 13.49 9.72 13.39 10 13.39H14C14.28 13.39 14.52 13.49 14.71 13.68C14.9 13.87 15 14.11 15 14.39C15 14.67 14.9 14.91 14.71 15.1Z" fill="currentColor"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/* TN Dialog Global Styles for CDK Dialog */
|
|
2
|
+
|
|
3
|
+
/* CDK Dialog Container Styles */
|
|
4
|
+
.cdk-dialog-container {
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: column;
|
|
7
|
+
align-items: center;
|
|
8
|
+
justify-content: center;
|
|
9
|
+
min-height: 100vh;
|
|
10
|
+
padding: 20px;
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/* Dialog Panel - this is the actual dialog box */
|
|
15
|
+
.cdk-dialog-panel,
|
|
16
|
+
.tn-dialog-panel {
|
|
17
|
+
position: relative;
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
border-radius: 8px;
|
|
21
|
+
box-sizing: border-box;
|
|
22
|
+
overflow: hidden;
|
|
23
|
+
outline: 0;
|
|
24
|
+
width: 100%;
|
|
25
|
+
max-width: 80vw;
|
|
26
|
+
max-height: 80vh;
|
|
27
|
+
background: var(--tn-bg1, #ffffff);
|
|
28
|
+
color: var(--tn-fg1, #000000);
|
|
29
|
+
box-shadow: 0 11px 15px -7px rgba(0, 0, 0, 0.2),
|
|
30
|
+
0 24px 38px 3px rgba(0, 0, 0, 0.14),
|
|
31
|
+
0 9px 46px 8px rgba(0, 0, 0, 0.12);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/* Dialog backdrop */
|
|
35
|
+
.cdk-dialog-backdrop {
|
|
36
|
+
background: rgba(0, 0, 0, 0.5);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/* Dialog content structure styling */
|
|
40
|
+
.cdk-dialog-panel > * {
|
|
41
|
+
display: flex;
|
|
42
|
+
flex-direction: column;
|
|
43
|
+
height: 100%;
|
|
44
|
+
width: 100%;
|
|
45
|
+
box-sizing: border-box;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* Dialog title should be at the very top */
|
|
49
|
+
.tn-dialog-title {
|
|
50
|
+
box-sizing: border-box;
|
|
51
|
+
display: block;
|
|
52
|
+
flex-shrink: 0; /* Don't allow shrinking */
|
|
53
|
+
width: 100%; /* Full width */
|
|
54
|
+
padding: 24px 48px 20px 24px; /* Top, right (for close button), bottom, left */
|
|
55
|
+
margin: 0;
|
|
56
|
+
font-size: 1.25rem;
|
|
57
|
+
font-weight: 600;
|
|
58
|
+
line-height: 1.5;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* Reset margins for any heading elements inside the title */
|
|
62
|
+
.tn-dialog-title h1,
|
|
63
|
+
.tn-dialog-title h2,
|
|
64
|
+
.tn-dialog-title h3,
|
|
65
|
+
.tn-dialog-title h4,
|
|
66
|
+
.tn-dialog-title h5,
|
|
67
|
+
.tn-dialog-title h6 {
|
|
68
|
+
margin: 0;
|
|
69
|
+
font-size: inherit;
|
|
70
|
+
font-weight: inherit;
|
|
71
|
+
line-height: inherit;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* Dialog content should expand to fill available space between title and actions */
|
|
75
|
+
.tn-dialog-content {
|
|
76
|
+
flex-grow: 1;
|
|
77
|
+
overflow: auto;
|
|
78
|
+
box-sizing: border-box;
|
|
79
|
+
min-height: 0; /* Allow flexbox to shrink */
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/* Dialog actions styling */
|
|
83
|
+
.tn-dialog-actions {
|
|
84
|
+
box-sizing: border-box;
|
|
85
|
+
display: flex !important;
|
|
86
|
+
flex-shrink: 0; /* Don't allow shrinking - always visible */
|
|
87
|
+
align-items: center !important;
|
|
88
|
+
justify-content: flex-end !important;
|
|
89
|
+
width: 100%; /* Full width */
|
|
90
|
+
padding: 8px 24px 24px 24px !important; /* Top, right, bottom, left */
|
|
91
|
+
margin: 0 !important;
|
|
92
|
+
gap: 12px !important;
|
|
93
|
+
border-top: 1px solid var(--tn-lines, #e5e7eb) !important;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* Close button styling */
|
|
97
|
+
.tn-dialog-close-btn {
|
|
98
|
+
position: absolute;
|
|
99
|
+
top: 16px;
|
|
100
|
+
right: 16px;
|
|
101
|
+
display: flex;
|
|
102
|
+
align-items: center;
|
|
103
|
+
justify-content: center;
|
|
104
|
+
width: 32px;
|
|
105
|
+
height: 32px;
|
|
106
|
+
padding: 0;
|
|
107
|
+
background: none;
|
|
108
|
+
border: none;
|
|
109
|
+
border-radius: 4px;
|
|
110
|
+
color: var(--tn-fg2, #6c757d);
|
|
111
|
+
cursor: pointer;
|
|
112
|
+
font-size: 24px;
|
|
113
|
+
line-height: 1;
|
|
114
|
+
transition: all 0.2s ease-in-out;
|
|
115
|
+
z-index: 10;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.tn-dialog-close-btn:hover {
|
|
119
|
+
background: var(--tn-alt-bg1, #f8f9fa);
|
|
120
|
+
color: var(--tn-fg1, #000000);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.tn-dialog-close-btn:focus {
|
|
124
|
+
outline: 2px solid var(--tn-primary, #007bff);
|
|
125
|
+
outline-offset: 2px;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.tn-dialog-close-btn:active {
|
|
129
|
+
transform: scale(0.95);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.tn-dialog-close-btn span {
|
|
133
|
+
font-weight: normal;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/* High contrast mode support */
|
|
137
|
+
@media (prefers-contrast: high) {
|
|
138
|
+
.cdk-dialog-panel,
|
|
139
|
+
.tn-dialog-panel {
|
|
140
|
+
border: 1px solid var(--tn-fg1, #000);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* Reduced motion support */
|
|
145
|
+
@media (prefers-reduced-motion: reduce) {
|
|
146
|
+
.cdk-dialog-panel,
|
|
147
|
+
.tn-dialog-panel {
|
|
148
|
+
/* Animations will be disabled by the animation trigger */
|
|
149
|
+
}
|
|
150
|
+
}
|