@diskette/palette 0.23.2 → 0.25.0
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/dist/cli/cli.js +23 -28
- package/dist/cli/output.d.ts +3 -4
- package/dist/cli/output.js +8 -3
- package/dist/cli/utils.d.ts +0 -9
- package/dist/cli/utils.js +0 -44
- package/dist/css.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/types.d.ts +6 -2
- package/dist/types.js +6 -6
- package/dist/utils.d.ts +52 -1
- package/dist/utils.js +54 -0
- package/package.json +1 -1
package/dist/cli/cli.js
CHANGED
|
@@ -2,47 +2,50 @@ import * as p from '@clack/prompts';
|
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import { mkdirSync } from 'node:fs';
|
|
4
4
|
import { relative, resolve } from 'node:path';
|
|
5
|
-
import { accentColors, grayColors
|
|
5
|
+
import { accentColors, grayColors } from "../types.js";
|
|
6
6
|
import { getMatchingGrayColor } from "../utils.js";
|
|
7
7
|
import { buildOutputFiles, confirmOverwrites, writeOutputFiles, } from "./output.js";
|
|
8
|
-
import { alphaColors, capitalize, getColorHex } from "./utils.js";
|
|
8
|
+
import { alphaColors, capitalize, getColorHex, invariant } from "./utils.js";
|
|
9
|
+
const accentOptionsList = [...accentColors];
|
|
10
|
+
const alphaOptionsList = [...alphaColors];
|
|
9
11
|
const accentOptions = {
|
|
10
|
-
Accents:
|
|
11
|
-
.filter((c) => c !== 'gray')
|
|
12
|
-
.map((c) => {
|
|
12
|
+
Accents: accentOptionsList.map((c) => {
|
|
13
13
|
const hex = getColorHex(c);
|
|
14
14
|
const swatch = hex ? chalk.bgHex(hex)(' ') + ' ' : '';
|
|
15
15
|
return { value: c, label: `${swatch}${capitalize(c)}` };
|
|
16
16
|
}),
|
|
17
|
-
Alpha:
|
|
17
|
+
Alpha: alphaOptionsList.map((c) => {
|
|
18
18
|
const [label] = c.split('Alpha');
|
|
19
19
|
return { value: c, label: capitalize(label) };
|
|
20
20
|
}),
|
|
21
21
|
};
|
|
22
|
-
const
|
|
22
|
+
const grayOptionsList = [...grayColors];
|
|
23
|
+
const grayOptions = grayOptionsList.map((c) => {
|
|
23
24
|
const hex = getColorHex(c);
|
|
24
25
|
const swatch = hex ? chalk.bgHex(hex)(' ') + ' ' : '';
|
|
25
26
|
return { value: c, label: `${swatch}${capitalize(c)}` };
|
|
26
27
|
});
|
|
27
28
|
async function main() {
|
|
28
|
-
p.intro(`palette v0.
|
|
29
|
+
p.intro(`palette v0.25.0`);
|
|
29
30
|
const results = await p.group({
|
|
30
31
|
colors: () => p.groupMultiselect({
|
|
31
32
|
message: 'Select accent colors',
|
|
32
33
|
options: accentOptions,
|
|
33
34
|
required: true,
|
|
34
|
-
initialValues:
|
|
35
|
+
initialValues: alphaOptionsList,
|
|
35
36
|
}),
|
|
36
37
|
grays: ({ results }) => {
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
const colors = results.colors ?? [];
|
|
39
|
+
const grays = [];
|
|
40
|
+
for (const color of colors) {
|
|
41
|
+
if (accentOptionsList.includes(color)) {
|
|
42
|
+
grays.push(getMatchingGrayColor(color));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
42
45
|
return p.multiselect({
|
|
43
46
|
message: 'Select gray colors (natural pairings pre-selected)',
|
|
44
47
|
options: grayOptions,
|
|
45
|
-
initialValues:
|
|
48
|
+
initialValues: grays,
|
|
46
49
|
required: false,
|
|
47
50
|
});
|
|
48
51
|
},
|
|
@@ -65,23 +68,15 @@ async function main() {
|
|
|
65
68
|
process.exit(0);
|
|
66
69
|
},
|
|
67
70
|
});
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
process.exit(0);
|
|
73
|
-
}
|
|
71
|
+
// TODO: figure out why results.grays is typed as unknown
|
|
72
|
+
invariant(Array.isArray(results.grays) &&
|
|
73
|
+
results.grays.every((g) => typeof g === 'string'));
|
|
74
|
+
const grays = results.grays;
|
|
74
75
|
const cwd = process.cwd();
|
|
75
76
|
const outputPath = resolve(cwd, results.output);
|
|
76
77
|
mkdirSync(outputPath, { recursive: true });
|
|
77
78
|
// Build file list, confirm overwrites, and write
|
|
78
|
-
const allFiles = buildOutputFiles({
|
|
79
|
-
colors: allColors,
|
|
80
|
-
grays: selectedGrays,
|
|
81
|
-
outputPath,
|
|
82
|
-
accents: results.accents,
|
|
83
|
-
tailwind: results.tailwind,
|
|
84
|
-
});
|
|
79
|
+
const allFiles = buildOutputFiles(outputPath, { ...results, grays });
|
|
85
80
|
const filesToWrite = await confirmOverwrites(allFiles);
|
|
86
81
|
const s = p.spinner({ withGuide: false });
|
|
87
82
|
const writeResults = writeOutputFiles(filesToWrite);
|
package/dist/cli/output.d.ts
CHANGED
|
@@ -8,12 +8,11 @@ export type WriteResult = {
|
|
|
8
8
|
status: 'written' | 'skipped' | 'error';
|
|
9
9
|
error?: Error;
|
|
10
10
|
};
|
|
11
|
-
export declare function buildOutputFiles(
|
|
11
|
+
export declare function buildOutputFiles(outputPath: string, results: {
|
|
12
12
|
colors: string[];
|
|
13
13
|
grays: string[];
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
tailwind?: boolean;
|
|
14
|
+
accents: boolean;
|
|
15
|
+
tailwind: boolean;
|
|
17
16
|
}): OutputFile[];
|
|
18
17
|
export declare function confirmOverwrites(files: OutputFile[]): Promise<OutputFile[]>;
|
|
19
18
|
export declare function writeOutputFiles(files: OutputFile[]): WriteResult[];
|
package/dist/cli/output.js
CHANGED
|
@@ -56,8 +56,9 @@ function tailwindFile(names, outputPath, accents) {
|
|
|
56
56
|
content: () => css.tailwind(names, { accents }),
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
|
-
export function buildOutputFiles(
|
|
60
|
-
const {
|
|
59
|
+
export function buildOutputFiles(outputPath, results) {
|
|
60
|
+
const { grays, accents, tailwind } = results;
|
|
61
|
+
const colors = [...results.colors, ...results.grays];
|
|
61
62
|
const files = [];
|
|
62
63
|
for (const name of colors) {
|
|
63
64
|
if (isAlphaColor(name)) {
|
|
@@ -110,7 +111,11 @@ export function writeOutputFiles(files) {
|
|
|
110
111
|
return { path: file.path, status: 'written' };
|
|
111
112
|
}
|
|
112
113
|
catch (error) {
|
|
113
|
-
return {
|
|
114
|
+
return {
|
|
115
|
+
path: file.path,
|
|
116
|
+
status: 'error',
|
|
117
|
+
error: error,
|
|
118
|
+
};
|
|
114
119
|
}
|
|
115
120
|
});
|
|
116
121
|
}
|
package/dist/cli/utils.d.ts
CHANGED
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
export declare const alphaColors: readonly ["blackAlpha", "whiteAlpha"];
|
|
2
2
|
export type AlphaColor = (typeof alphaColors)[number];
|
|
3
|
-
/**
|
|
4
|
-
* get package.json content from startDir
|
|
5
|
-
* @param cwd
|
|
6
|
-
* @returns package.json content
|
|
7
|
-
*/
|
|
8
|
-
export declare const getPackageJson: (cwd: string) => {
|
|
9
|
-
pkg: any;
|
|
10
|
-
packageJsonPath: string;
|
|
11
|
-
} | undefined;
|
|
12
3
|
export declare const capitalize: (s: string) => string;
|
|
13
4
|
/** Get hex color (step 9) for a palette color */
|
|
14
5
|
export declare const getColorHex: (name: string) => string | null;
|
package/dist/cli/utils.js
CHANGED
|
@@ -1,50 +1,6 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import { createRequire } from 'node:module';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import * as url from 'node:url';
|
|
5
1
|
import * as colors from "../colors/index.js";
|
|
6
2
|
import {} from "../types.js";
|
|
7
3
|
export const alphaColors = ['blackAlpha', 'whiteAlpha'];
|
|
8
|
-
const require = createRequire(import.meta.url);
|
|
9
|
-
/**
|
|
10
|
-
* get package.json content from startDir
|
|
11
|
-
* @param cwd
|
|
12
|
-
* @returns package.json content
|
|
13
|
-
*/
|
|
14
|
-
export const getPackageJson = (cwd) => {
|
|
15
|
-
try {
|
|
16
|
-
const startDir = path.dirname(url.fileURLToPath(cwd));
|
|
17
|
-
const packageJsonPath = findPackageJson(startDir);
|
|
18
|
-
if (packageJsonPath) {
|
|
19
|
-
const pkg = require(packageJsonPath);
|
|
20
|
-
return { pkg, packageJsonPath };
|
|
21
|
-
}
|
|
22
|
-
return undefined;
|
|
23
|
-
}
|
|
24
|
-
catch (error) {
|
|
25
|
-
// ignore error
|
|
26
|
-
return undefined;
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
/**
|
|
30
|
-
* search package.json from startDir
|
|
31
|
-
* @param startDir
|
|
32
|
-
* @returns
|
|
33
|
-
*/
|
|
34
|
-
const findPackageJson = (startDir) => {
|
|
35
|
-
let currentDir = startDir;
|
|
36
|
-
while (true) {
|
|
37
|
-
const packageJsonPath = path.join(currentDir, 'package.json');
|
|
38
|
-
if (fs.existsSync(packageJsonPath)) {
|
|
39
|
-
return packageJsonPath;
|
|
40
|
-
}
|
|
41
|
-
const parentDir = path.resolve(currentDir, '..');
|
|
42
|
-
if (parentDir === currentDir) {
|
|
43
|
-
return '';
|
|
44
|
-
}
|
|
45
|
-
currentDir = parentDir;
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
4
|
export const capitalize = (s) => s.charAt(0).toUpperCase() + s.slice(1);
|
|
49
5
|
/** Get hex color (step 9) for a palette color */
|
|
50
6
|
export const getColorHex = (name) => {
|
package/dist/css.js
CHANGED
|
@@ -80,7 +80,7 @@ function accents(colorNames) {
|
|
|
80
80
|
output.push(formatRule(`[data-accent-color]:where(:not([data-accent-color=''], [data-accent-color='gray']))`, [...focus, ...focusAlpha]));
|
|
81
81
|
return output.join('\n\n') + '\n';
|
|
82
82
|
}
|
|
83
|
-
function grays(names, className = 'diskette-
|
|
83
|
+
function grays(names, className = 'diskette-palette') {
|
|
84
84
|
const grays = names.filter((n) => n !== 'gray');
|
|
85
85
|
const blocks = grays.map((colorName) => {
|
|
86
86
|
const srgb = [];
|
package/dist/index.d.ts
CHANGED
|
@@ -1118,6 +1118,6 @@ export declare const colors: {
|
|
|
1118
1118
|
};
|
|
1119
1119
|
export default colors;
|
|
1120
1120
|
export { accentColors, grayColors } from './types.ts';
|
|
1121
|
-
export type { AccentColor, AlphaConfig, AlphaScale, Color, ColorScale, GrayColor, PaletteColor, ScaleSteps, } from './types.ts';
|
|
1121
|
+
export type { AccentColor, AlphaConfig, AlphaScale, Color, ColorScale, ColorSteps, GrayColor, PaletteColor, ScaleSteps, } from './types.ts';
|
|
1122
1122
|
export { applyColorVars, css } from './css.ts';
|
|
1123
|
-
export { getMatchingGrayColor, toVarName } from './utils.ts';
|
|
1123
|
+
export { getColor, getColorModes, getMatchingGrayColor, toVarName, } from './utils.ts';
|
package/dist/index.js
CHANGED
|
@@ -65,4 +65,4 @@ export const colors = {
|
|
|
65
65
|
export default colors;
|
|
66
66
|
export { accentColors, grayColors } from "./types.js";
|
|
67
67
|
export { applyColorVars, css } from "./css.js";
|
|
68
|
-
export { getMatchingGrayColor, toVarName } from "./utils.js";
|
|
68
|
+
export { getColor, getColorModes, getMatchingGrayColor, toVarName, } from "./utils.js";
|
package/dist/types.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export declare const grayColors: readonly ["gray", "mauve", "slate", "sage", "olive", "sand"];
|
|
2
2
|
export type GrayColor = (typeof grayColors)[number];
|
|
3
|
-
export declare const accentColors: readonly ["gray", "
|
|
3
|
+
export declare const accentColors: readonly ["gray", "tomato", "red", "ruby", "crimson", "pink", "plum", "purple", "violet", "iris", "indigo", "blue", "cyan", "teal", "jade", "green", "grass", "bronze", "gold", "brown", "orange", "amber", "yellow", "lime", "mint", "sky"];
|
|
4
4
|
export type AccentColor = (typeof accentColors)[number];
|
|
5
5
|
export type Color = AccentColor | GrayColor;
|
|
6
6
|
type Tuple<T, N extends number, R extends T[] = []> = R['length'] extends N ? R : Tuple<T, N, [...R, T]>;
|
|
@@ -33,8 +33,12 @@ export interface PaletteColor<C extends Color> {
|
|
|
33
33
|
};
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
|
+
export type Steps = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
|
|
37
|
+
export type ColorSteps = {
|
|
38
|
+
[K in `step${Steps}`]: string;
|
|
39
|
+
};
|
|
36
40
|
export type AlphaScale<C extends 'white' | 'back'> = {
|
|
37
|
-
[K in `${C}A${
|
|
41
|
+
[K in `${C}A${Steps}`]: string;
|
|
38
42
|
};
|
|
39
43
|
export type AlphaConfig<C extends 'white' | 'back'> = {
|
|
40
44
|
name: C;
|
package/dist/types.js
CHANGED
|
@@ -8,12 +8,6 @@ export const grayColors = [
|
|
|
8
8
|
];
|
|
9
9
|
export const accentColors = [
|
|
10
10
|
'gray',
|
|
11
|
-
'gold',
|
|
12
|
-
'bronze',
|
|
13
|
-
'brown',
|
|
14
|
-
'yellow',
|
|
15
|
-
'amber',
|
|
16
|
-
'orange',
|
|
17
11
|
'tomato',
|
|
18
12
|
'red',
|
|
19
13
|
'ruby',
|
|
@@ -30,6 +24,12 @@ export const accentColors = [
|
|
|
30
24
|
'jade',
|
|
31
25
|
'green',
|
|
32
26
|
'grass',
|
|
27
|
+
'bronze',
|
|
28
|
+
'gold',
|
|
29
|
+
'brown',
|
|
30
|
+
'orange',
|
|
31
|
+
'amber',
|
|
32
|
+
'yellow',
|
|
33
33
|
'lime',
|
|
34
34
|
'mint',
|
|
35
35
|
'sky',
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AccentColor, GrayColor } from './types.ts';
|
|
1
|
+
import type { AccentColor, Color, ColorSteps, GrayColor } from './types.ts';
|
|
2
2
|
export declare const LIGHT_SELECTOR = ":root, .light, .light-theme";
|
|
3
3
|
export declare const DARK_SELECTOR = ".dark, .dark-theme";
|
|
4
4
|
export declare const schemeSelector: (scheme: "light" | "dark") => string;
|
|
@@ -8,3 +8,54 @@ export declare const formatRule: (selector: string, vars: string[]) => string;
|
|
|
8
8
|
export declare const formatP3: (selector: string, vars: string[]) => string;
|
|
9
9
|
export declare const formatNestedBlock: (selector: string, vars: string[]) => string;
|
|
10
10
|
export declare function getMatchingGrayColor(accentColor: AccentColor): GrayColor;
|
|
11
|
+
/**
|
|
12
|
+
* Steps `1` and `2` are designed for app backgrounds and subtle component backgrounds.
|
|
13
|
+
* Appropriate applications include:
|
|
14
|
+
* - Main app background
|
|
15
|
+
* - Striped table background
|
|
16
|
+
* - Code block background
|
|
17
|
+
* - Card background
|
|
18
|
+
* - Sidebar background
|
|
19
|
+
* - Canvas area background
|
|
20
|
+
*
|
|
21
|
+
* Steps `3`, `4`, and `5` are designed for UI component backgrounds.
|
|
22
|
+
* - Step `3` is for normal states.
|
|
23
|
+
* - Step `4` is for hover states.
|
|
24
|
+
* - Step `5` is for pressed or selected states.
|
|
25
|
+
* Steps `11` and `12`—which are designed for text
|
|
26
|
+
*
|
|
27
|
+
* Steps `6`, `7`, and `8` are designed for borders.
|
|
28
|
+
* - Step `6` is designed for subtle borders on components which are not interactive. For example sidebars, headers, cards, alerts, and separators.
|
|
29
|
+
* - Step `7` is designed for subtle borders on interactive components.
|
|
30
|
+
* - Step `8` is designed for stronger borders on interactive components and focus rings. Most of the components use --focus-8 for the focus outline color.
|
|
31
|
+
*
|
|
32
|
+
* Step `9` has the highest chroma of all steps in the scale. In other words, it's the purest step, the step mixed with the least amount of white or black. Because `9` is the purest step, it has a wide range of applications:
|
|
33
|
+
* - Website/App backgrounds
|
|
34
|
+
* - Website section backgrounds
|
|
35
|
+
* - Header backgrounds
|
|
36
|
+
* - Component backgrounds
|
|
37
|
+
* - Graphics/Logos
|
|
38
|
+
* - Overlays
|
|
39
|
+
* - Coloured shadows
|
|
40
|
+
* - Accent borders
|
|
41
|
+
*
|
|
42
|
+
* Step `10` is designed for component hover states, where step `9` is the component's normal state background.
|
|
43
|
+
*
|
|
44
|
+
* Most step 9 colors are designed for white foreground text. `Sky`, `Mint`, `Lime`, `Yellow`, and `Amber` are designed for dark foreground text and steps `9` and `10`.
|
|
45
|
+
*
|
|
46
|
+
* Steps `11` and `12` are designed for text.
|
|
47
|
+
* - Step `11` is designed for low-contrast text.
|
|
48
|
+
* - Step `12` is designed for high-contrast text.
|
|
49
|
+
*/
|
|
50
|
+
export declare function getColor(color: Color, opts?: {
|
|
51
|
+
mode?: 'light' | 'dark';
|
|
52
|
+
variant?: 'alpha' | 'solid';
|
|
53
|
+
type?: 'p3' | 'srgb';
|
|
54
|
+
}): ColorSteps;
|
|
55
|
+
export declare function getColorModes(color: Color, opts?: {
|
|
56
|
+
variant?: 'alpha' | 'solid';
|
|
57
|
+
type?: 'p3' | 'srgb';
|
|
58
|
+
}): {
|
|
59
|
+
light: ColorSteps;
|
|
60
|
+
dark: ColorSteps;
|
|
61
|
+
};
|
package/dist/utils.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import colors from "./index.js";
|
|
1
2
|
export const LIGHT_SELECTOR = ':root, .light, .light-theme';
|
|
2
3
|
export const DARK_SELECTOR = '.dark, .dark-theme';
|
|
3
4
|
export const schemeSelector = (scheme) => scheme === 'light' ? LIGHT_SELECTOR : DARK_SELECTOR;
|
|
@@ -43,3 +44,56 @@ export function getMatchingGrayColor(accentColor) {
|
|
|
43
44
|
return 'gray';
|
|
44
45
|
}
|
|
45
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Steps `1` and `2` are designed for app backgrounds and subtle component backgrounds.
|
|
49
|
+
* Appropriate applications include:
|
|
50
|
+
* - Main app background
|
|
51
|
+
* - Striped table background
|
|
52
|
+
* - Code block background
|
|
53
|
+
* - Card background
|
|
54
|
+
* - Sidebar background
|
|
55
|
+
* - Canvas area background
|
|
56
|
+
*
|
|
57
|
+
* Steps `3`, `4`, and `5` are designed for UI component backgrounds.
|
|
58
|
+
* - Step `3` is for normal states.
|
|
59
|
+
* - Step `4` is for hover states.
|
|
60
|
+
* - Step `5` is for pressed or selected states.
|
|
61
|
+
* Steps `11` and `12`—which are designed for text
|
|
62
|
+
*
|
|
63
|
+
* Steps `6`, `7`, and `8` are designed for borders.
|
|
64
|
+
* - Step `6` is designed for subtle borders on components which are not interactive. For example sidebars, headers, cards, alerts, and separators.
|
|
65
|
+
* - Step `7` is designed for subtle borders on interactive components.
|
|
66
|
+
* - Step `8` is designed for stronger borders on interactive components and focus rings. Most of the components use --focus-8 for the focus outline color.
|
|
67
|
+
*
|
|
68
|
+
* Step `9` has the highest chroma of all steps in the scale. In other words, it's the purest step, the step mixed with the least amount of white or black. Because `9` is the purest step, it has a wide range of applications:
|
|
69
|
+
* - Website/App backgrounds
|
|
70
|
+
* - Website section backgrounds
|
|
71
|
+
* - Header backgrounds
|
|
72
|
+
* - Component backgrounds
|
|
73
|
+
* - Graphics/Logos
|
|
74
|
+
* - Overlays
|
|
75
|
+
* - Coloured shadows
|
|
76
|
+
* - Accent borders
|
|
77
|
+
*
|
|
78
|
+
* Step `10` is designed for component hover states, where step `9` is the component's normal state background.
|
|
79
|
+
*
|
|
80
|
+
* Most step 9 colors are designed for white foreground text. `Sky`, `Mint`, `Lime`, `Yellow`, and `Amber` are designed for dark foreground text and steps `9` and `10`.
|
|
81
|
+
*
|
|
82
|
+
* Steps `11` and `12` are designed for text.
|
|
83
|
+
* - Step `11` is designed for low-contrast text.
|
|
84
|
+
* - Step `12` is designed for high-contrast text.
|
|
85
|
+
*/
|
|
86
|
+
export function getColor(color, opts) {
|
|
87
|
+
const { variant = 'solid', type = 'p3', mode = 'dark' } = opts ?? {};
|
|
88
|
+
const scales = colors[color][mode][type][variant];
|
|
89
|
+
return scales.reduce((acc, current, index) => {
|
|
90
|
+
const key = `step${index + 1}`;
|
|
91
|
+
acc[key] = current;
|
|
92
|
+
return acc;
|
|
93
|
+
}, {});
|
|
94
|
+
}
|
|
95
|
+
export function getColorModes(color, opts) {
|
|
96
|
+
const light = getColor(color, opts);
|
|
97
|
+
const dark = getColor(color, opts);
|
|
98
|
+
return { light, dark };
|
|
99
|
+
}
|