0
Fork 0
mirror of https://github.com/penpot/penpot-plugins.git synced 2025-01-02 04:40:11 -05:00

feat: refactor contrast plugin

This commit is contained in:
Juanfran 2024-05-24 12:51:08 +02:00
parent 14c4983bf2
commit 1d76bfe985
26 changed files with 549 additions and 462 deletions

View file

@ -1,3 +0,0 @@
{
"presets": ["@nx/js/babel"]
}

View file

@ -1,8 +0,0 @@
{
"jsc": {
"parser": {
"syntax": "typescript"
},
"target": "es2016"
}
}

View file

@ -1,25 +1,43 @@
import baseConfig from '../../eslint.config.js';
import typescriptEslintParser from '@typescript-eslint/parser';
import { compat } from '../../eslint.base.config.js';
export default [
...baseConfig,
{
languageOptions: {
parser: typescriptEslintParser,
parserOptions: { project: './apps/contrast-plugin/tsconfig.app.json' },
},
},
{
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
rules: {},
},
{
files: ['**/*.ts', '**/*.tsx'],
rules: {},
},
{
files: ['**/*.js', '**/*.jsx'],
rules: {},
},
{ ignores: ['vite.config.ts'] },
...compat
.config({
extends: [
'plugin:@nx/angular',
'plugin:@angular-eslint/template/process-inline-templates',
],
})
.map((config) => ({
...config,
files: ['**/*.ts'],
rules: {
'@angular-eslint/directive-selector': [
'error',
{
type: 'attribute',
prefix: 'app',
style: 'camelCase',
},
],
'@angular-eslint/component-selector': [
'error',
{
type: 'element',
prefix: 'app',
style: 'kebab-case',
},
],
},
})),
...compat
.config({ extends: ['plugin:@nx/angular-template'] })
.map((config) => ({
...config,
files: ['**/*.html'],
rules: {},
})),
{ ignores: ['**/assets/*.js'] },
];

View file

@ -2,7 +2,92 @@
"name": "contrast-plugin",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"prefix": "app",
"sourceRoot": "apps/contrast-plugin/src",
"tags": ["type:plugin"],
"targets": {}
"targets": {
"build": {
"executor": "@angular-devkit/build-angular:application",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/apps/contrast-plugin",
"index": "apps/contrast-plugin/src/index.html",
"browser": "apps/contrast-plugin/src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "apps/contrast-plugin/tsconfig.app.json",
"assets": [
"apps/contrast-plugin/src/favicon.ico",
"apps/contrast-plugin/src/assets"
],
"styles": [
"libs/plugins-styles/src/lib/styles.css",
"apps/contrast-plugin/src/styles.css"
],
"scripts": [],
"optimization": {
"scripts": true,
"styles": true,
"fonts": false
}
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"executor": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "contrast-plugin:build:production"
},
"development": {
"buildTarget": "contrast-plugin:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"executor": "@angular-devkit/build-angular:extract-i18n",
"options": {
"buildTarget": "contrast-plugin:build"
}
},
"buildPlugin": {
"executor": "@nx/esbuild:esbuild",
"outputs": [
"{options.outputPath}"
],
"options": {
"minify": true,
"outputPath": "apps/contrast-plugin/src/assets/",
"main": "apps/contrast-plugin/src/plugin.ts",
"tsConfig": "apps/contrast-plugin/tsconfig.plugin.json",
"generatePackageJson": false,
"format": [
"esm"
],
"deleteOutputPath": false
}
},
}
}

View file

@ -1,11 +0,0 @@
{
"name": "Contrast plugin",
"host": "http://localhost:4302",
"code": "/plugin.js",
"icon": "/icon.png",
"permissions": [
"page:read",
"file:read",
"selection:read"
]
}

View file

@ -0,0 +1,77 @@
.wrapper {
padding-block-start: var(--spacing-24);
}
.bold {
font-weight: 600;
}
.contrast-preview {
display: flex;
flex-direction: column;
gap: var(--spacing-8);
padding-block-end: var(--spacing-20);
border-block-end: 2px solid var(--background-quaternary);
}
.color-box {
block-size: 66px;
border-radius: var(--spacing-8);
background: linear-gradient(
to right,
var(--color1) 0%,
var(--color1) 50%,
var(--color2) 50%,
var(--color2) 100%
);
}
.select-colors {
display: flex;
justify-content: space-between;
}
.contrast-ratio {
padding-block: var(--spacing-24);
span {
color: var(--foreground-primary);
}
}
.contrast-results {
display: flex;
flex-direction: column;
gap: var(--spacing-16);
}
.contrast-result {
.title {
margin-block-end: var(--spacing-4);
}
.list {
display: flex;
gap: var(--spacing-8);
}
}
.tag {
display: flex;
align-items: center;
justify-content: center;
inline-size: 42px;
block-size: 32px;
color: var(--app-white);
border: 1px solid transparent;
border-radius: var(--spacing-8);
&.good {
background-color: var(--success-950);
border-color: var(--success-500);
}
&.fail {
background-color: var(--error-950);
border-color: var(--error-700);
}
}

View file

@ -0,0 +1,231 @@
import {
ChangeDetectionStrategy,
Component,
computed,
inject,
} from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { ActivatedRoute } from '@angular/router';
import type {
PluginMessageEvent,
PluginUIEvent,
ThemePluginEvent,
} from '../model';
import { filter, fromEvent, map, merge, take } from 'rxjs';
import { CommonModule } from '@angular/common';
import { PenpotShape } from '@penpot/plugin-types';
@Component({
standalone: true,
imports: [CommonModule],
selector: 'app-root',
template: `
<div class="wrapper body-s">
@if (selection().length === 0) {
<p class="empty-preview">
Select two filled shapes to calculate the color contrast between them.
</p>
} @else if (selection().length === 1) {
<p class="empty-preview">
Select <span class="bold">one more</span> filled shape to calculate the
color contrast between the selected colors.
</p>
} @else if (selection().length >= 2) {
<div class="contrast-preview">
<p>Selected colors:</p>
<div class="color-box"></div>
<ul class="select-colors">
<li>
{{ color1() }}
</li>
<li>{{ color2() }}</li>
</ul>
</div>
<p class="contrast-ratio">
Contrast ratio: <span>{{ result() }} : 1</span>
</p>
<div class="contrast-results">
<div class="contrast-result">
<p class="title">Normal text:</p>
<ul class="list">
<li
class="tag"
[ngClass]="
result() >= contrastStandards.AA.normal ? 'good' : 'fail'
"
>
AA
</li>
<li
class="tag"
[ngClass]="
result() >= contrastStandards.AAA.normal ? 'good' : 'fail'
"
>
AAA
</li>
</ul>
</div>
<div class="contrast-result">
<p class="title">
Large text
<span class="body-xs">(starting from 19px bold or 24px):</span>
</p>
<ul class="list">
<li
class="tag"
[ngClass]="
result() >= contrastStandards.AA.large ? 'good' : 'fail'
"
>
AA
</li>
<li
class="tag"
[ngClass]="
result() >= contrastStandards.AAA.large ? 'good' : 'fail'
"
>
AAA
</li>
</ul>
</div>
<div class="contrast-result">
<p class="title">
Graphics <span class="body-xs">(such as form input borders):</span>
</p>
<ul class="list">
<li
class="tag"
[ngClass]="
result() >= contrastStandards.graphics ? 'good' : 'fail'
"
>
AA
</li>
</ul>
</div>
</div>
}
</div>
`,
styleUrl: './app.component.css',
host: {
'[attr.data-theme]': 'theme()',
'[style.--color1]': 'color1()',
'[style.--color2]': 'color2()',
},
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AppComponent {
#route = inject(ActivatedRoute);
#messages$ = fromEvent<MessageEvent<PluginMessageEvent>>(window, 'message');
#initialTheme$ = this.#route.queryParamMap.pipe(
map((params) => params.get('theme')),
filter((theme) => !!theme),
take(1)
);
selection = toSignal(
this.#messages$.pipe(
filter(
(event) => event.data.type === 'init' || event.data.type === 'selection'
),
map((event) => {
if (event.data.type === 'init') {
return event.data.content.selection;
} else if (event.data.type === 'selection') {
return event.data.content;
}
return [];
}),
map((shapes) => {
return shapes
.map((shape) => this.#getShapeColor(shape))
.filter((color): color is string => !!color);
})
),
{
initialValue: [],
}
);
theme = toSignal(
merge(
this.#initialTheme$,
this.#messages$.pipe(
map((event) => event.data),
filter((data): data is ThemePluginEvent => data.type === 'theme'),
map((data) => {
return data.content;
})
)
)
);
color1 = computed(() => {
return this.selection().at(-2);
});
color2 = computed(() => {
return this.selection().at(-1);
});
result = computed<number>(() => {
const color1 = this.color1();
const color2 = this.color2();
if (!color1 || !color2) {
return 0;
}
const lum1 = this.#getLuminosity(color1) + 0.05;
const lum2 = this.#getLuminosity(color2) + 0.05;
const result = lum1 > lum2 ? lum1 / lum2 : lum2 / lum1;
return Number(result.toFixed(2));
});
contrastStandards = {
AA: {
normal: 4.5,
large: 3,
},
AAA: {
normal: 7,
large: 4.5,
},
graphics: 3,
} as const;
constructor() {
this.#sendMessage({ type: 'ready' });
}
#getLuminosity(color: string) {
const rgb = this.#hexToRgb(color);
const a = rgb.map((v) => {
v /= 255;
return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
});
return 0.2126 * a[0] + 0.7152 * a[1] + 0.0722 * a[2];
}
#hexToRgb(hex: string) {
const r = parseInt(hex.slice(1, 3), 16);
const g = parseInt(hex.slice(3, 5), 16);
const b = parseInt(hex.slice(5, 7), 16);
return [r, g, b];
}
#getShapeColor(shape?: PenpotShape): string | undefined {
return shape?.fills?.[0]?.fillColor ?? shape?.strokes?.[0]?.strokeColor;
}
#sendMessage(message: PluginUIEvent) {
parent.postMessage(message, '*');
}
}

View file

@ -0,0 +1,6 @@
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
export const appConfig: ApplicationConfig = {
providers: [provideRouter([])],
};

View file

@ -1,101 +0,0 @@
.wrapper {
&[data-theme='dark'] {
color: var(--app-white);
}
&[data-theme='light'] {
color: var(--app-black);
}
}
.color {
align-items: center;
display: flex;
margin-inline-end: var(--spacing-16);
}
.color-preview {
block-size: var(--spacing-36);
border: 1px solid var(--df-secondary);
border-radius: var(--spacing-4);
display: block;
inline-size: var(--spacing-36);
margin-inline-end: var(--spacing-16);
}
.fail {
background-color: var(--error-500);
}
.good {
background-color: var(--success-500);
}
.title {
margin-block-end: var(--spacing-8);
}
.list {
display: flex;
margin-block-end: var(--spacing-16);
}
.tag {
border-radius: var(--spacing-4);
color: var(--db-primary);
margin-inline-end: var(--spacing-16);
padding: var(--spacing-4) var(--spacing-8);
text-transform: uppercase;
}
.contrast-preview {
align-items: center;
border: 1px solid var(--df-secondary);
border-radius: var(--spacing-4);
box-sizing: content-box;
block-size: calc(2 * var(--spacing-40));
display: flex;
flex-direction: column;
justify-content: center;
inline-size: calc(100% - var(--spacing-16));
margin-block-end: var(--spacing-16);
padding-block: var(--spacing-24);
}
.empty-preview {
position: absolute;
}
.text {
color: transparent;
margin-block-end: var(--spacing-8);
&.small {
font-size: 18px;
}
&.large {
font-size: 24px;
}
}
.icons-list {
display: flex;
gap: var(--spacing-8);
margin-block-start: var(--spacing-8);
}
.shape {
block-size: var(--spacing-24);
inline-size: var(--spacing-24);
}
.circle {
border-radius: 50%;
}
.triangle {
border-left: var(--spacing-12) solid transparent;
border-right: var(--spacing-12) solid transparent;
border-bottom: var(--spacing-24) solid transparent;
}

View file

@ -1,212 +0,0 @@
/* eslint-disable */
import 'plugins-styles/lib/styles.css';
import './app.element.css';
export class AppElement extends HTMLElement {
public static observedAttributes = [];
public shapes: any;
calculateContrast(firstColor: string, secondColor: string) {
const luminosityFirstColor = this.getLuminosity(firstColor);
const luminositySecondColor = this.getLuminosity(secondColor);
const result =
(luminosityFirstColor + 0.05) / (luminositySecondColor + 0.05);
this.setColors(firstColor, secondColor);
this.setResult(result.toFixed(2).toString());
this.setA11yTags(result);
}
getLuminosity(color: string) {
const rgb = this.hexToRgb(color);
const a = rgb.map((v) => {
v /= 255;
return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
});
return 0.2126 * a[0] + 0.7152 * a[1] + 0.0722 * a[2];
}
hexToRgb(hex: string) {
const r = parseInt(hex.slice(1, 3), 16);
const g = parseInt(hex.slice(3, 5), 16);
const b = parseInt(hex.slice(5, 7), 16);
return [r, g, b];
}
setResult(text: string) {
const selector = document.getElementById('result');
if (selector) {
selector.innerText = `${text} : 1`;
}
}
setColors(firstColor: string | null, secondColor: string | null) {
const color1 = document.getElementById('first-color');
const color2 = document.getElementById('second-color');
const code1 = document.getElementById('first-color-code');
const code2 = document.getElementById('second-color-code');
const contrastPreview = document.getElementById('contrast-preview');
const smallText = document.getElementById('small-text');
const largeText = document.getElementById('large-text');
const circle = document.getElementById('circle');
const square = document.getElementById('square');
const triangle = document.getElementById('triangle');
if (color1 && code1) {
color1.style.background = firstColor ? firstColor : 'transparent';
code1.innerText = firstColor ? firstColor : '';
}
if (color2 && code2) {
color2.style.background = secondColor ? secondColor : 'transparent';
code2.innerText = secondColor ? secondColor : '';
}
if (
contrastPreview &&
smallText &&
largeText &&
circle &&
square &&
triangle
) {
contrastPreview.style.background = secondColor
? secondColor
: 'transparent';
smallText.style.color = firstColor ? firstColor : 'transparent';
largeText.style.color = firstColor ? firstColor : 'transparent';
circle.style.background = firstColor ? firstColor : 'transparent';
square.style.background = firstColor ? firstColor : 'transparent';
triangle.style.borderBottom = firstColor
? `var(--spacing-24) solid ${firstColor}`
: 'var(--spacing-24) solid transparent';
}
const emptyPreview = document.getElementById('empty-preview');
if (!firstColor && !secondColor && emptyPreview) {
emptyPreview.style.display = 'block';
} else if (emptyPreview) {
emptyPreview.style.display = 'none';
}
}
setA11yTags(result: number) {
const selectors = {
aa: document.getElementById('aa'),
aaa: document.getElementById('aaa'),
aaLg: document.getElementById('aa-lg'),
aaaLg: document.getElementById('aaa-lg'),
graphics: document.getElementById('graphics'),
};
const fail = 'tag fail';
const good = 'tag good';
function setClass(selector: HTMLElement | null, className: string) {
if (selector) {
selector.className = className;
}
}
if (result > 7) {
setClass(selectors.aa, good);
setClass(selectors.aaa, good);
setClass(selectors.aaLg, good);
setClass(selectors.aaaLg, good);
setClass(selectors.graphics, good);
} else if (result > 4.5) {
setClass(selectors.aa, good);
setClass(selectors.aaa, fail);
setClass(selectors.aaLg, good);
setClass(selectors.aaaLg, good);
setClass(selectors.graphics, good);
} else if (result > 3) {
setClass(selectors.aa, fail);
setClass(selectors.aaa, fail);
setClass(selectors.aaLg, good);
setClass(selectors.aaaLg, fail);
setClass(selectors.graphics, good);
} else {
setClass(selectors.aa, fail);
setClass(selectors.aaa, fail);
setClass(selectors.aaLg, fail);
setClass(selectors.aaaLg, fail);
setClass(selectors.graphics, fail);
}
}
initCalculate(shapes: any) {
const obj0 = shapes[0]?.fills?.[0]?.fillColor;
const obj1 = shapes[1]?.fills?.[0]?.fillColor;
if (obj0 && obj1) {
this.calculateContrast(obj0, obj1);
}
}
connectedCallback() {
window.addEventListener('message', (event) => {
if (event.data.type === 'init') {
this.setAttribute('data-theme', event.data.content.theme);
if (event.data.content.selection.length >= 2) {
this.initCalculate(event.data.content.shapes);
}
} else if (event.data.type === 'selection') {
if (event.data.content.shapes.length >= 2) {
this.initCalculate(event.data.content.shapes);
} else {
this.setColors(null, null);
this.setResult('0');
this.setA11yTags(0);
}
} else if (event.data.type === 'theme') {
this.setAttribute('data-theme', event.data.content.theme);
}
});
this.innerHTML = `
<div class="wrapper">
<div id="contrast-preview" class="contrast-preview">
<p id="empty-preview" class="empty-preview">Select two colors to calculate contrast</p>
<p id="small-text" data-color="text" data-second class="text small">SMALL sample text</p>
<p id="large-text" data-color="text" data-second class="text large">LARGE sample text</p>
<ul class="icons-list">
<span id="circle" class="shape circle"></span>
<span id="square" class="shape square"></span>
<span id="triangle" class="triangle"></span>
</ul>
</div>
<p class="title body-l">Selected colors:</p>
<ul class="list">
<li class="color">
<span id="first-color" data-first class="color-preview"></span>
<code id="first-color-code"></code>
</li>
<li class="color">
<span id="second-color" data-second class="color-preview"></span>
<code id="second-color-code"></code>
</li>
</ul>
<p class="title body-l">Contrast ratio: <span id="result">0 : 1</span></p>
<p class="title body-l">Normal text:</p>
<ul class="list">
<li id="aa" class="tag fail">AA</li>
<li id="aaa" class="tag fail">AAA</li>
</ul>
<p class="title body-l">Large text (24px or 19px + bold):</p>
<ul class="list">
<li id="aa-lg" class="tag fail">AA</li>
<li id="aaa-lg" class="tag fail">AAA</li>
</ul>
<p class="title body-l">Graphics (such as form input borders):</p>
<ul class="list">
<li id="graphics" class="tag fail">AA</li>
</ul>
</div>
`;
parent.postMessage({ content: 'ready' }, '*');
}
}
customElements.define('app-root', AppElement);

View file

@ -0,0 +1,7 @@
{
"name": "Contrast",
"host": "http://localhost:4302",
"description": "Measure contrast plugin",
"code": "/assets/plugin.js",
"permissions": ["page:read", "file:read", "selection:read"]
}

View file

@ -2,15 +2,11 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<title>ContrastPlugin</title>
<title>contrast-plugin</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<link rel="stylesheet" href="/src/styles.css" />
</head>
<body>
<app-root></app-root>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View file

@ -1 +1,7 @@
import './app/app.element';
import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component';
bootstrapApplication(AppComponent, appConfig).catch((err) =>
console.error(err)
);

View file

@ -0,0 +1,29 @@
import { PenpotShape } from '@penpot/plugin-types';
export interface InitPluginUIEvent {
type: 'ready';
}
export type PluginUIEvent = InitPluginUIEvent;
export interface InitPluginEvent {
type: 'init';
content: {
theme: string;
selection: PenpotShape[];
};
}
export interface SelectionPluginEvent {
type: 'selection';
content: PenpotShape[];
}
export interface ThemePluginEvent {
type: 'theme';
content: string;
}
export type PluginMessageEvent =
| InitPluginEvent
| SelectionPluginEvent
| ThemePluginEvent;

View file

@ -1,15 +1,17 @@
penpot.ui.open('Contrast plugin', '', {
width: 450,
height: 625,
import type { PluginMessageEvent, PluginUIEvent } from './model.js';
penpot.ui.open('CONTRAST PLUGIN', `?theme=${penpot.getTheme()}`, {
width: 235,
height: 445,
});
penpot.ui.onMessage<{ content: string }>((message) => {
if (message.content === 'ready') {
penpot.ui.sendMessage({
penpot.ui.onMessage<PluginUIEvent>((message) => {
if (message.type === 'ready') {
sendMessage({
type: 'init',
content: {
theme: penpot.getTheme(),
shapes: penpot.getSelectedShapes(),
selection: penpot.getSelectedShapes(),
},
});
}
@ -17,10 +19,14 @@ penpot.ui.onMessage<{ content: string }>((message) => {
penpot.on('selectionchange', () => {
const shapes = penpot.getSelectedShapes();
penpot.ui.sendMessage({ type: 'selection', content: { shapes } });
sendMessage({ type: 'selection', content: shapes });
});
penpot.on('themechange', () => {
const theme = penpot.getTheme();
penpot.ui.sendMessage({ type: 'theme', content: { theme } });
sendMessage({ type: 'theme', content: theme });
});
function sendMessage(message: PluginMessageEvent) {
penpot.ui.sendMessage(message);
}

View file

@ -1 +0,0 @@
/* You can add global styles to this file, and also import other style files */

View file

@ -2,8 +2,9 @@
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": ["node"]
"types": []
},
"exclude": ["src/**/*.spec.ts", "src/**/*.test.ts"],
"include": ["src/**/*.ts", "../../libs/plugin-types/index.d.ts"]
"files": ["src/main.ts"],
"include": ["src/**/*.d.ts"],
"exclude": ["src/**/*.test.ts", "src/**/*.spec.ts"]
}

View file

@ -0,0 +1,7 @@
{
"extends": "./tsconfig.json",
"include": ["src/**/*.ts"],
"compilerOptions": {
"types": []
}
}

View file

@ -1,30 +1,33 @@
{
"extends": "../../tsconfig.base.json",
"files": [],
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ESNext", "DOM"],
"moduleResolution": "Node",
"strict": true,
"resolveJsonModule": true,
"isolatedModules": true,
"target": "es2022",
"useDefineForClassFields": false,
"esModuleInterop": true,
"noEmit": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"skipLibCheck": true,
"types": ["vite/client"]
"noFallthroughCasesInSwitch": true
},
"include": ["src"],
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.app.json"
},
{
"path": "./tsconfig.spec.json"
"path": "./tsconfig.editor.json"
},
{
"path": "./tsconfig.plugin.json"
}
]
],
"extends": "../../tsconfig.base.json",
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true
}
}

View file

@ -0,0 +1,8 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"types": []
},
"files": ["src/plugin.ts"],
"include": ["../../libs/plugin-types/index.d.ts"]
}

View file

@ -1,26 +0,0 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": [
"vitest/globals",
"vitest/importMeta",
"vite/client",
"node",
"vitest"
]
},
"include": [
"vite.config.ts",
"vitest.config.ts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
"src/**/*.test.tsx",
"src/**/*.spec.tsx",
"src/**/*.test.js",
"src/**/*.spec.js",
"src/**/*.test.jsx",
"src/**/*.spec.jsx",
"src/**/*.d.ts"
]
}

View file

@ -1,57 +1,19 @@
/// <reference types='vitest' />
import { defineConfig } from 'vite';
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
export default defineConfig({
root: __dirname,
cacheDir: '../../node_modules/.vite/apps/contrast-plugin',
server: {
port: 4302,
host: '0.0.0.0',
},
preview: {
port: 4302,
host: '0.0.0.0',
},
plugins: [nxViteTsPaths()],
// Uncomment this if you are using workers.
// worker: {
// plugins: [ nxViteTsPaths() ],
// },
build: {
outDir: '../../dist/apps/contrast-plugin',
reportCompressedSize: true,
commonjsOptions: {
transformMixedEsModules: true,
},
rollupOptions: {
input: {
plugin: 'src/plugin.ts',
index: './index.html',
},
output: {
entryFileNames: '[name].js',
},
},
},
cacheDir: '../node_modules/.vite/contrast-plugin',
test: {
globals: true,
cache: {
dir: '../../node_modules/.vitest',
dir: '../node_modules/.vitest',
},
environment: 'jsdom',
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
reporters: ['default'],
coverage: {
reportsDirectory: '../../coverage/apps/contrast-plugin',
reportsDirectory: '../coverage/contrast-plugin',
provider: 'v8',
},
},

View file

@ -7,6 +7,7 @@
--font-line-height-s: 1.2;
--font-line-height-m: 1.4;
--font-line-height-l: 1.5;
--font-size-xs: 10px;
--font-size-s: 12px;
--font-size-m: 14px;
--font-size-l: 16px;
@ -68,6 +69,12 @@ code {
text-transform: uppercase;
}
.body-xs {
font-weight: var(--font-weight-regular);
font-size: var(--font-size-xs);
line-height: var(--font-line-height-s);
}
.body-s {
font-weight: var(--font-weight-regular);
font-size: var(--font-size-s);

View file

@ -11,7 +11,7 @@
"start:plugin:all": "concurrently --kill-others \"npm:start:plugin:*(!all)\"",
"start:plugin:poc-state": "npx nx run-many --targets=buildPlugin,serve --projects=poc-state-plugin --watch --host 0.0.0.0 --port 4301",
"start:plugin:contrast": "npx nx run contrast-plugin:build --watch & npx nx run contrast-plugin:preview",
"start:contrast-plugin": "npx nx run-many --targets=buildPlugin,serve --projects=contrast-plugin --watch --host 0.0.0.0 --port 4302",
"start:plugin:icons": "npx nx run-many --targets=buildPlugin,serve --projects=icons-plugin --watch --host 0.0.0.0 --port 4303",
"start:plugin:loremipsum": "npx nx run-many --targets=buildPlugin,serve --projects=lorem-ipsum-plugin --watch --port 4304",
"start:plugin:palette": "npx nx run create-palette-plugin:build --watch & npx nx run create-palette-plugin:preview",

View file

@ -10,7 +10,7 @@
"importHelpers": true,
"target": "es2015",
"module": "esnext",
"lib": ["es2020", "dom"],
"lib": ["es2022", "dom"],
"skipLibCheck": true,
"skipDefaultLibCheck": true,
"baseUrl": ".",