mirror of
https://github.com/Novaloop-AG/healthchat-web.git
synced 2026-11-07 06:58:25 +00:00
Add CSS cascade layers for Compound, shared components, and app/web styles (#33302)
* Layer Compound and shared component CSS * Layer app theme CSS * Remove !important flags from ActionBarView * Remove unnecessary !important statements from shared components * Avoid dead code errors for *.pcss just because layer is specified after @import url * Remove unnecessary !important styling * Override Banner defaults in RoomStatusBarView * Updated snaps * Updated snaps * Fix styling of media body in app/web * Fix styling for Compound anchors * Fix styling issues in app/web * More styling fixes * Fix a problem extracting css for HTMLExport * Revert changes * Fix for theme styling * Add test to improve coverage * Prettier * Fix styling issues * Add data-kind attribute to avoid global styling override * Update screenshot that now is correct * Revert data-kind attribute * Handle LinkPreview styling in .pcss * Fix flaky test: Avoid racing the lazy-loaded ManageEventIndexDialog * Take care of review comments * Updated snaps * Updated snaps again after merge * Remove !important from RoomStatusBar
This commit is contained in:
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
@@ -163,9 +163,10 @@ b {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
a:hover,
|
/* Keep the legacy link colour without overriding Compound anchors. */
|
||||||
a:link,
|
a:where(:not([data-kind])):hover,
|
||||||
a:visited {
|
a:where(:not([data-kind])):link,
|
||||||
|
a:where(:not([data-kind])):visited {
|
||||||
color: $accent-alt;
|
color: $accent-alt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
/* Shared cascade order: Compound tokens, Compound Web, shared components, then app overrides. */
|
||||||
|
@layer compound-tokens, compound-web, shared-components, app-web;
|
||||||
|
|
||||||
/* Modules bundled with compound apply compound lastly. In order to catch issue due to css class ordering, we put compound at the end */
|
/* Modules bundled with compound apply compound lastly. In order to catch issue due to css class ordering, we put compound at the end */
|
||||||
@import url("@vector-im/compound-design-tokens/assets/web/css/compound-design-tokens.css") layer(compound);
|
@import url("@vector-im/compound-design-tokens/assets/web/css/compound-design-tokens.css") layer(compound-tokens);
|
||||||
@import url("@vector-im/compound-web/dist/style.css");
|
@import url("@vector-im/compound-web/dist/style.css") layer(compound-web);
|
||||||
|
|||||||
@@ -11,9 +11,8 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
|
|
||||||
/* Container for live recording and playback controls */
|
/* Container for live recording and playback controls */
|
||||||
.mx_MediaBody.mx_VoiceMessagePrimaryContainer {
|
.mx_MediaBody.mx_VoiceMessagePrimaryContainer {
|
||||||
/* The waveform (right) has a 1px padding on it that we want to account for, otherwise */
|
/* Match mx_MediaBody spacing, offsetting the waveform's 1px internal right padding. */
|
||||||
/* inherit from mx_MediaBody */
|
padding: 6px 11px 6px 12px;
|
||||||
padding-right: 11px;
|
|
||||||
|
|
||||||
/* Cheat at alignment a bit */
|
/* Cheat at alignment a bit */
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
.mx_CompleteSecurityBody {
|
.mx_CompleteSecurityBody {
|
||||||
color: $authpage-primary-color;
|
color: $authpage-primary-color;
|
||||||
background-color: $background;
|
background-color: $background;
|
||||||
border-radius: 4px;
|
border-radius: 24px;
|
||||||
padding: 20px 20px 60px 20px;
|
padding: 20px 20px 60px 20px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
.mx_MFileBody [data-type="download"] {
|
.mx_MFileBody [data-type="download"] {
|
||||||
color: $accent;
|
|
||||||
height: var(--cpd-space-9x);
|
height: var(--cpd-space-9x);
|
||||||
|
|
||||||
& object {
|
& object {
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
color: $secondary-content;
|
color: $secondary-content;
|
||||||
font: var(--cpd-font-body-md-regular);
|
font: var(--cpd-font-body-md-regular);
|
||||||
line-height: $font-24px;
|
line-height: $font-24px;
|
||||||
|
}
|
||||||
padding: 6px 12px;
|
|
||||||
|
.mx_MAudioBody > .mx_MediaBody {
|
||||||
|
border-radius: var(--MBody-border-radius);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,3 +19,12 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
opacity: unset; /* Unset the opacity value specified above on the search results panel */
|
opacity: unset; /* Unset the opacity value specified above on the search results panel */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_TextualBody_urlPreviews {
|
||||||
|
/* Let shared-components own preview link colours instead of the app-wide anchor colour. */
|
||||||
|
a:where(:not([data-kind])):hover,
|
||||||
|
a:where(:not([data-kind])):link,
|
||||||
|
a:where(:not([data-kind])):visited {
|
||||||
|
color: revert-layer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -26,12 +26,22 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
|
|
||||||
&.mx_ThemeChoicePanel_themeSelector_disabled {
|
&.mx_ThemeChoicePanel_themeSelector_disabled {
|
||||||
border-color: var(--cpd-color-border-disabled);
|
border-color: var(--cpd-color-border-disabled);
|
||||||
|
|
||||||
|
.mx_ThemeChoicePanel_themeSelector_Label {
|
||||||
|
color: var(--cpd-color-text-disabled);
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_ThemeChoicePanel_themeSelector_Label {
|
.mx_ThemeChoicePanel_themeSelector_Label {
|
||||||
color: var(--cpd-color-text-primary);
|
|
||||||
font: var(--cpd-font-body-md-semibold);
|
font: var(--cpd-font-body-md-semibold);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:not(.mx_ThemeChoicePanel_themeSelector_disabled) {
|
||||||
|
.mx_ThemeChoicePanel_themeSelector_Label {
|
||||||
|
color: var(--cpd-color-text-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
|
|
||||||
color: $primary-content;
|
color: $primary-content;
|
||||||
|
|
||||||
a {
|
/* Compound links carry data-kind and provide their own colour. */
|
||||||
|
a:not([data-kind]) {
|
||||||
color: $links;
|
color: $links;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
|
|
||||||
color: $primary-content;
|
color: $primary-content;
|
||||||
|
|
||||||
a {
|
/* Compound links carry data-kind and provide their own colour. */
|
||||||
|
a:not([data-kind]) {
|
||||||
color: $links;
|
color: $links;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
@import "../../../../res/css/_font-sizes.pcss";
|
@layer compound-tokens, compound-web, shared-components, app-web;
|
||||||
@import "../../legacy-light/css/_fonts.pcss";
|
|
||||||
@import "../../legacy-light/css/_legacy-light.pcss";
|
@import "../../../../res/css/_font-sizes.pcss" layer(app-web);
|
||||||
@import "../../legacy-dark/css/_legacy-dark.pcss";
|
@import "../../legacy-light/css/_fonts.pcss" layer(app-web);
|
||||||
@import "../../light-custom/css/_custom.pcss";
|
@import "../../legacy-light/css/_legacy-light.pcss" layer(app-web);
|
||||||
@import "../../../../res/css/_components.pcss";
|
@import "../../legacy-dark/css/_legacy-dark.pcss" layer(app-web);
|
||||||
|
@import "../../light-custom/css/_custom.pcss" layer(app-web);
|
||||||
|
@import "../../../../res/css/_components.pcss" layer(app-web);
|
||||||
@import "../../../../res/css/_compound.pcss";
|
@import "../../../../res/css/_compound.pcss";
|
||||||
@import url("highlight.js/styles/atom-one-light.min.css");
|
@import url("highlight.js/styles/atom-one-light.min.css") layer(app-web);
|
||||||
@import url("github-markdown-css/github-markdown-dark.css");
|
@import url("github-markdown-css/github-markdown-dark.css") layer(app-web);
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
@import "../../../../res/css/_font-sizes.pcss";
|
@layer compound-tokens, compound-web, shared-components, app-web;
|
||||||
@import "../../light/css/_fonts.pcss";
|
|
||||||
@import "../../light/css/_light.pcss";
|
@import "../../../../res/css/_font-sizes.pcss" layer(app-web);
|
||||||
@import "_dark.pcss";
|
@import "../../light/css/_fonts.pcss" layer(app-web);
|
||||||
@import "../../light/css/_mods.pcss";
|
@import "../../light/css/_light.pcss" layer(app-web);
|
||||||
@import "../../../../res/css/_components.pcss";
|
@import "_dark.pcss" layer(app-web);
|
||||||
|
@import "../../light/css/_mods.pcss" layer(app-web);
|
||||||
|
@import "../../../../res/css/_components.pcss" layer(app-web);
|
||||||
@import "../../../../res/css/_compound.pcss";
|
@import "../../../../res/css/_compound.pcss";
|
||||||
@import url("highlight.js/styles/atom-one-dark.min.css");
|
@import url("highlight.js/styles/atom-one-dark.min.css") layer(app-web);
|
||||||
@import url("github-markdown-css/github-markdown-dark.css");
|
@import url("github-markdown-css/github-markdown-dark.css") layer(app-web);
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
@import "../../../../res/css/_font-sizes.pcss";
|
@layer compound-tokens, compound-web, shared-components, app-web;
|
||||||
@import "../../legacy-light/css/_fonts.pcss";
|
|
||||||
@import "../../legacy-light/css/_legacy-light.pcss";
|
@import "../../../../res/css/_font-sizes.pcss" layer(app-web);
|
||||||
@import "_legacy-dark.pcss";
|
@import "../../legacy-light/css/_fonts.pcss" layer(app-web);
|
||||||
@import "../../../../res/css/_components.pcss";
|
@import "../../legacy-light/css/_legacy-light.pcss" layer(app-web);
|
||||||
|
@import "_legacy-dark.pcss" layer(app-web);
|
||||||
|
@import "../../../../res/css/_components.pcss" layer(app-web);
|
||||||
@import "../../../../res/css/_compound.pcss";
|
@import "../../../../res/css/_compound.pcss";
|
||||||
@import url("highlight.js/styles/atom-one-dark.min.css");
|
@import url("highlight.js/styles/atom-one-dark.min.css") layer(app-web);
|
||||||
@import url("github-markdown-css/github-markdown-dark.css");
|
@import url("github-markdown-css/github-markdown-dark.css") layer(app-web);
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
@import "../../../../res/css/_font-sizes.pcss";
|
@layer compound-tokens, compound-web, shared-components, app-web;
|
||||||
@import "_fonts.pcss";
|
|
||||||
@import "_legacy-light.pcss";
|
@import "../../../../res/css/_font-sizes.pcss" layer(app-web);
|
||||||
@import "../../../../res/css/_components.pcss";
|
@import "_fonts.pcss" layer(app-web);
|
||||||
|
@import "_legacy-light.pcss" layer(app-web);
|
||||||
|
@import "../../../../res/css/_components.pcss" layer(app-web);
|
||||||
@import "../../../../res/css/_compound.pcss";
|
@import "../../../../res/css/_compound.pcss";
|
||||||
@import url("highlight.js/styles/atom-one-light.min.css");
|
@import url("highlight.js/styles/atom-one-light.min.css") layer(app-web);
|
||||||
@import url("github-markdown-css/github-markdown-light.css");
|
@import url("github-markdown-css/github-markdown-light.css") layer(app-web);
|
||||||
|
|||||||
@@ -25,16 +25,17 @@ $panels: var(--panels, var(--cpd-color-gray-600));
|
|||||||
$panel-actions: var(--panels-actions, var(--cpd-color-gray-300));
|
$panel-actions: var(--panels-actions, var(--cpd-color-gray-300));
|
||||||
|
|
||||||
/* --timeline-background-color */
|
/* --timeline-background-color */
|
||||||
$button-secondary-bg-color: var(--timeline-background-color);
|
$custom-theme-background: var(--timeline-background-color, var(--cpd-color-bg-canvas-default));
|
||||||
$lightbox-border-color: var(--timeline-background-color);
|
$button-secondary-bg-color: $custom-theme-background;
|
||||||
$menu-bg-color: var(--timeline-background-color);
|
$lightbox-border-color: $custom-theme-background;
|
||||||
$message-action-bar-bg-color: var(--timeline-background-color);
|
$menu-bg-color: $custom-theme-background;
|
||||||
$background: var(--timeline-background-color);
|
$message-action-bar-bg-color: $custom-theme-background;
|
||||||
|
$background: $custom-theme-background;
|
||||||
$togglesw-ball-color: var(--cpd-color-bg-action-primary-rest);
|
$togglesw-ball-color: var(--cpd-color-bg-action-primary-rest);
|
||||||
$togglesw-off-color: var(--togglesw-off-color);
|
$togglesw-off-color: var(--togglesw-off-color);
|
||||||
$droptarget-bg-color: var(--timeline-background-color-50pct); /* still needs alpha at .5 */
|
$droptarget-bg-color: var(--timeline-background-color-50pct); /* still needs alpha at .5 */
|
||||||
$authpage-modal-bg-color: var(--timeline-background-color-50pct); /* still needs alpha at .59 */
|
$authpage-modal-bg-color: var(--timeline-background-color-50pct); /* still needs alpha at .59 */
|
||||||
$roomheader-bg-color: var(--timeline-background-color);
|
$roomheader-bg-color: $custom-theme-background;
|
||||||
|
|
||||||
/* --roomlist-highlights-color */
|
/* --roomlist-highlights-color */
|
||||||
$panel-actions: var(--roomlist-highlights-color);
|
$panel-actions: var(--roomlist-highlights-color);
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
@import "../../../../res/css/_font-sizes.pcss";
|
@layer compound-tokens, compound-web, shared-components, app-web;
|
||||||
@import "../../legacy-light/css/_fonts.pcss";
|
|
||||||
@import "../../legacy-light/css/_legacy-light.pcss";
|
@import "../../../../res/css/_font-sizes.pcss" layer(app-web);
|
||||||
@import "_custom.pcss";
|
@import "../../legacy-light/css/_fonts.pcss" layer(app-web);
|
||||||
@import "../../../../res/css/_components.pcss";
|
@import "../../legacy-light/css/_legacy-light.pcss" layer(app-web);
|
||||||
|
@import "_custom.pcss" layer(app-web);
|
||||||
|
@import "../../../../res/css/_components.pcss" layer(app-web);
|
||||||
@import "../../../../res/css/_compound.pcss";
|
@import "../../../../res/css/_compound.pcss";
|
||||||
@import url("highlight.js/styles/atom-one-light.min.css");
|
@import url("highlight.js/styles/atom-one-light.min.css") layer(app-web);
|
||||||
@import url("github-markdown-css/github-markdown-light.css");
|
@import url("github-markdown-css/github-markdown-light.css") layer(app-web);
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
@import "../../../../res/css/_font-sizes.pcss";
|
@layer compound-tokens, compound-web, shared-components, app-web;
|
||||||
@import "../../light/css/_fonts.pcss";
|
|
||||||
@import "../../light/css/_light.pcss";
|
@import "../../../../res/css/_font-sizes.pcss" layer(app-web);
|
||||||
@import "_light-high-contrast.pcss";
|
@import "../../light/css/_fonts.pcss" layer(app-web);
|
||||||
@import "../../light/css/_mods.pcss";
|
@import "../../light/css/_light.pcss" layer(app-web);
|
||||||
@import "../../../../res/css/_components.pcss";
|
@import "_light-high-contrast.pcss" layer(app-web);
|
||||||
|
@import "../../light/css/_mods.pcss" layer(app-web);
|
||||||
|
@import "../../../../res/css/_components.pcss" layer(app-web);
|
||||||
@import "../../../../res/css/_compound.pcss";
|
@import "../../../../res/css/_compound.pcss";
|
||||||
@import url("highlight.js/styles/atom-one-light.min.css");
|
@import url("highlight.js/styles/atom-one-light.min.css") layer(app-web);
|
||||||
@import url("github-markdown-css/github-markdown-light.css");
|
@import url("github-markdown-css/github-markdown-light.css") layer(app-web);
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
@import "../../../../res/css/_font-sizes.pcss";
|
@layer compound-tokens, compound-web, shared-components, app-web;
|
||||||
@import "_fonts.pcss";
|
|
||||||
@import "_light.pcss";
|
@import "../../../../res/css/_font-sizes.pcss" layer(app-web);
|
||||||
@import "_mods.pcss";
|
@import "_fonts.pcss" layer(app-web);
|
||||||
@import "../../../../res/css/_components.pcss";
|
@import "_light.pcss" layer(app-web);
|
||||||
|
@import "_mods.pcss" layer(app-web);
|
||||||
|
@import "../../../../res/css/_components.pcss" layer(app-web);
|
||||||
@import "../../../../res/css/_compound.pcss";
|
@import "../../../../res/css/_compound.pcss";
|
||||||
@import url("highlight.js/styles/atom-one-light.min.css");
|
@import url("highlight.js/styles/atom-one-light.min.css") layer(app-web);
|
||||||
@import url("github-markdown-css/github-markdown-light.css");
|
@import url("github-markdown-css/github-markdown-light.css") layer(app-web);
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ export function TextualBodyFactory(props: Readonly<IBodyProps>): JSX.Element {
|
|||||||
vm={textualBodyVm}
|
vm={textualBodyVm}
|
||||||
body={<EventContentBodyView vm={eventContentBodyVm} as={willHaveWrapper ? "span" : "div"} />}
|
body={<EventContentBodyView vm={eventContentBodyVm} as={willHaveWrapper ? "span" : "div"} />}
|
||||||
bodyRef={contentRef}
|
bodyRef={contentRef}
|
||||||
urlPreviews={<UrlPreviewGroupView vm={urlPreviewVm} />}
|
urlPreviews={<UrlPreviewGroupView vm={urlPreviewVm} className="mx_TextualBody_urlPreviews" />}
|
||||||
className={getTextualBodyClassName(content.msgtype as MsgType | undefined)}
|
className={getTextualBodyClassName(content.msgtype as MsgType | undefined)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -207,9 +207,10 @@ function generateCustomCompoundCSS(theme: CompoundTheme): string {
|
|||||||
for (const [token, value] of Object.entries(theme))
|
for (const [token, value] of Object.entries(theme))
|
||||||
if (COMPOUND_TOKEN.test(token)) properties.push(`${token}: ${value};`);
|
if (COMPOUND_TOKEN.test(token)) properties.push(`${token}: ${value};`);
|
||||||
else logger.warn(`'${token}' is not a valid Compound token`);
|
else logger.warn(`'${token}' is not a valid Compound token`);
|
||||||
// Insert the design token overrides into the 'custom' cascade layer as
|
// Insert the design token overrides into the existing Compound tokens
|
||||||
// documented at https://compound.element.io/?path=/docs/develop-theming--docs
|
// layer so custom themes win over the imported default tokens by source
|
||||||
return `@layer compound.custom { :root, [class*="cpd-theme-"] { ${properties.join(" ")} } }`;
|
// order without creating a lower-priority nested layer.
|
||||||
|
return `@layer compound-tokens { :root, [class*="cpd-theme-"] { ${properties.join(" ")} } }`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -6,11 +6,12 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
|||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Rule, StyleSheet } from "css-tree";
|
import type { CssNode, Rule, StyleSheet } from "css-tree";
|
||||||
|
|
||||||
import customCSS from "!!raw-loader!./exportCustomCSS.css";
|
import customCSS from "!!raw-loader!./exportCustomCSS.css";
|
||||||
|
|
||||||
const cssSelectorTextClassesRegex = /\.[\w-]+/g;
|
const cssSelectorTextClassesRegex = /\.[\w-]+/g;
|
||||||
|
const appLayerName = "app-web";
|
||||||
|
|
||||||
function mutateCssText(css: string): string {
|
function mutateCssText(css: string): string {
|
||||||
// replace used fonts so that we don't have to bundle Inter & Fira Code
|
// replace used fonts so that we don't have to bundle Inter & Fira Code
|
||||||
@@ -41,6 +42,21 @@ function includeRule(rule: Rule, usedClasses: Set<string>): boolean {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function includeNode(node: CssNode, usedClasses: Set<string>): boolean {
|
||||||
|
if (node.type === "Atrule") {
|
||||||
|
if (node.name === "font-face") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.block) {
|
||||||
|
node.block.children = node.block.children.filter((child) => includeNode(child, usedClasses));
|
||||||
|
return !node.block.children.isEmpty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return node.type !== "Rule" || includeRule(node, usedClasses);
|
||||||
|
}
|
||||||
|
|
||||||
// naively culls unused css rules based on which classes are present in the html,
|
// naively culls unused css rules based on which classes are present in the html,
|
||||||
// doesn't cull rules which won't apply due to the full selector not matching but gets rid of a LOT of cruft anyway.
|
// doesn't cull rules which won't apply due to the full selector not matching but gets rid of a LOT of cruft anyway.
|
||||||
// We cannot use document.styleSheets as it does not handle variables in shorthand properties sanely,
|
// We cannot use document.styleSheets as it does not handle variables in shorthand properties sanely,
|
||||||
@@ -69,13 +85,7 @@ const getExportCSS = async (usedClasses: Set<string>): Promise<string> => {
|
|||||||
}) as StyleSheet;
|
}) as StyleSheet;
|
||||||
|
|
||||||
for (const rule of ast.children) {
|
for (const rule of ast.children) {
|
||||||
if (rule.type === "Atrule") {
|
if (!includeNode(rule, usedClasses)) {
|
||||||
if (rule.name === "font-face") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rule.type === "Rule" && !includeRule(rule, usedClasses)) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +93,7 @@ const getExportCSS = async (usedClasses: Set<string>): Promise<string> => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return css + customCSS;
|
return `${css}@layer ${appLayerName} {${customCSS}}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default getExportCSS;
|
export default getExportCSS;
|
||||||
|
|||||||
@@ -60,11 +60,6 @@ body {
|
|||||||
|
|
||||||
a.mx_reply_anchor {
|
a.mx_reply_anchor {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: #238cf5;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.mx_reply_anchor:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@-webkit-keyframes mx_snackbar_fadein {
|
@-webkit-keyframes mx_snackbar_fadein {
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||||
|
|
||||||
exports[`theme setTheme applies a custom Compound theme 1`] = `"@layer compound.custom { :root, [class*="cpd-theme-"] { --cpd-color-icon-accent-tertiary: var(--cpd-color-blue-800); --cpd-color-text-action-accent: var(--cpd-color-blue-900); } }"`;
|
exports[`theme setTheme applies a custom Compound theme 1`] = `"@layer compound-tokens { :root, [class*="cpd-theme-"] { --cpd-color-icon-accent-tertiary: var(--cpd-color-blue-800); --cpd-color-text-action-accent: var(--cpd-color-blue-900); } }"`;
|
||||||
|
|||||||
@@ -237,7 +237,7 @@ exports[`RoomView for a local room in state ERROR should match the snapshot 1`]
|
|||||||
class="_content_n7ud0_38"
|
class="_content_n7ud0_38"
|
||||||
>
|
>
|
||||||
<p
|
<p
|
||||||
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60 _title_1xryk_24"
|
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60 _title_mcq5y_24"
|
||||||
id="_r_1cl_"
|
id="_r_1cl_"
|
||||||
>
|
>
|
||||||
Could not start a chat with this user
|
Could not start a chat with this user
|
||||||
@@ -247,7 +247,7 @@ exports[`RoomView for a local room in state ERROR should match the snapshot 1`]
|
|||||||
class="_actions_n7ud0_61"
|
class="_actions_n7ud0_61"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="_button_1nw83_8 _primaryAction_1xryk_20 _has-icon_1nw83_60"
|
class="_button_1nw83_8 _primaryAction_mcq5y_20 _has-icon_1nw83_60"
|
||||||
data-kind="primary"
|
data-kind="primary"
|
||||||
data-size="md"
|
data-size="md"
|
||||||
role="button"
|
role="button"
|
||||||
|
|||||||
+8
-8
@@ -89,13 +89,13 @@ exports[`<MessageEditHistory /> should match the snapshot 1`] = `
|
|||||||
<div
|
<div
|
||||||
aria-label="Message Actions"
|
aria-label="Message Actions"
|
||||||
aria-live="off"
|
aria-live="off"
|
||||||
class="_flex_4dswl_9 mx_ThreadActionBar mx_HistoryActionBar _toolbar_1ax4y_8"
|
class="_flex_4dswl_9 mx_ThreadActionBar mx_HistoryActionBar _toolbar_1ksun_8"
|
||||||
role="toolbar"
|
role="toolbar"
|
||||||
style="--mx-flex-display: inline-flex; --mx-flex-direction: row; --mx-flex-align: start; --mx-flex-justify: start; --mx-flex-gap: 0; --mx-flex-wrap: nowrap;"
|
style="--mx-flex-display: inline-flex; --mx-flex-direction: row; --mx-flex-align: start; --mx-flex-justify: start; --mx-flex-gap: 0; --mx-flex-wrap: nowrap;"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
aria-label="Remove"
|
aria-label="Remove"
|
||||||
class="_button_1nw83_8 _toolbar_item_1ax4y_14"
|
class="_button_1nw83_8 _toolbar_item_1ksun_14"
|
||||||
data-kind="tertiary"
|
data-kind="tertiary"
|
||||||
data-presentation="label"
|
data-presentation="label"
|
||||||
data-size="md"
|
data-size="md"
|
||||||
@@ -236,13 +236,13 @@ exports[`<MessageEditHistory /> should support events with 1`] = `
|
|||||||
<div
|
<div
|
||||||
aria-label="Message Actions"
|
aria-label="Message Actions"
|
||||||
aria-live="off"
|
aria-live="off"
|
||||||
class="_flex_4dswl_9 mx_ThreadActionBar mx_HistoryActionBar _toolbar_1ax4y_8"
|
class="_flex_4dswl_9 mx_ThreadActionBar mx_HistoryActionBar _toolbar_1ksun_8"
|
||||||
role="toolbar"
|
role="toolbar"
|
||||||
style="--mx-flex-display: inline-flex; --mx-flex-direction: row; --mx-flex-align: start; --mx-flex-justify: start; --mx-flex-gap: 0; --mx-flex-wrap: nowrap;"
|
style="--mx-flex-display: inline-flex; --mx-flex-direction: row; --mx-flex-align: start; --mx-flex-justify: start; --mx-flex-gap: 0; --mx-flex-wrap: nowrap;"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
aria-label="Remove"
|
aria-label="Remove"
|
||||||
class="_button_1nw83_8 _toolbar_item_1ax4y_14"
|
class="_button_1nw83_8 _toolbar_item_1ksun_14"
|
||||||
data-kind="tertiary"
|
data-kind="tertiary"
|
||||||
data-presentation="label"
|
data-presentation="label"
|
||||||
data-size="md"
|
data-size="md"
|
||||||
@@ -298,13 +298,13 @@ exports[`<MessageEditHistory /> should support events with 1`] = `
|
|||||||
<div
|
<div
|
||||||
aria-label="Message Actions"
|
aria-label="Message Actions"
|
||||||
aria-live="off"
|
aria-live="off"
|
||||||
class="_flex_4dswl_9 mx_ThreadActionBar mx_HistoryActionBar _toolbar_1ax4y_8"
|
class="_flex_4dswl_9 mx_ThreadActionBar mx_HistoryActionBar _toolbar_1ksun_8"
|
||||||
role="toolbar"
|
role="toolbar"
|
||||||
style="--mx-flex-display: inline-flex; --mx-flex-direction: row; --mx-flex-align: start; --mx-flex-justify: start; --mx-flex-gap: 0; --mx-flex-wrap: nowrap;"
|
style="--mx-flex-display: inline-flex; --mx-flex-direction: row; --mx-flex-align: start; --mx-flex-justify: start; --mx-flex-gap: 0; --mx-flex-wrap: nowrap;"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
aria-label="Remove"
|
aria-label="Remove"
|
||||||
class="_button_1nw83_8 _toolbar_item_1ax4y_14"
|
class="_button_1nw83_8 _toolbar_item_1ksun_14"
|
||||||
data-kind="tertiary"
|
data-kind="tertiary"
|
||||||
data-presentation="label"
|
data-presentation="label"
|
||||||
data-size="md"
|
data-size="md"
|
||||||
@@ -342,13 +342,13 @@ exports[`<MessageEditHistory /> should support events with 1`] = `
|
|||||||
<div
|
<div
|
||||||
aria-label="Message Actions"
|
aria-label="Message Actions"
|
||||||
aria-live="off"
|
aria-live="off"
|
||||||
class="_flex_4dswl_9 mx_ThreadActionBar mx_HistoryActionBar _toolbar_1ax4y_8"
|
class="_flex_4dswl_9 mx_ThreadActionBar mx_HistoryActionBar _toolbar_1ksun_8"
|
||||||
role="toolbar"
|
role="toolbar"
|
||||||
style="--mx-flex-display: inline-flex; --mx-flex-direction: row; --mx-flex-align: start; --mx-flex-justify: start; --mx-flex-gap: 0; --mx-flex-wrap: nowrap;"
|
style="--mx-flex-display: inline-flex; --mx-flex-direction: row; --mx-flex-align: start; --mx-flex-justify: start; --mx-flex-gap: 0; --mx-flex-wrap: nowrap;"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
aria-label="Remove"
|
aria-label="Remove"
|
||||||
class="_button_1nw83_8 _toolbar_item_1ax4y_14"
|
class="_button_1nw83_8 _toolbar_item_1ksun_14"
|
||||||
data-kind="tertiary"
|
data-kind="tertiary"
|
||||||
data-presentation="label"
|
data-presentation="label"
|
||||||
data-size="md"
|
data-size="md"
|
||||||
|
|||||||
+3
-3
@@ -3,7 +3,7 @@
|
|||||||
exports[`MBodyFactory renderMBody fallback shows m.audio generic placeholder when showFileInfo is true 1`] = `
|
exports[`MBodyFactory renderMBody fallback shows m.audio generic placeholder when showFileInfo is true 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<span
|
<span
|
||||||
class="_content_f1s5h_8 mx_MFileBody"
|
class="_content_1t2mx_8 mx_MFileBody"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="mx_MediaBody _mediaBody_rgndh_8"
|
class="mx_MediaBody _mediaBody_rgndh_8"
|
||||||
@@ -44,7 +44,7 @@ exports[`MBodyFactory renderMBody fallback shows m.audio generic placeholder whe
|
|||||||
exports[`MBodyFactory renderMBody fallback shows m.file generic placeholder when showFileInfo is true 1`] = `
|
exports[`MBodyFactory renderMBody fallback shows m.file generic placeholder when showFileInfo is true 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<span
|
<span
|
||||||
class="_content_f1s5h_8 mx_MFileBody"
|
class="_content_1t2mx_8 mx_MFileBody"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="mx_MediaBody _mediaBody_rgndh_8"
|
class="mx_MediaBody _mediaBody_rgndh_8"
|
||||||
@@ -82,7 +82,7 @@ exports[`MBodyFactory renderMBody fallback shows m.file generic placeholder when
|
|||||||
exports[`MBodyFactory renderMBody renders download button for m.file in file rendering type 1`] = `
|
exports[`MBodyFactory renderMBody renders download button for m.file in file rendering type 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<span
|
<span
|
||||||
class="_content_f1s5h_8 mx_MFileBody"
|
class="_content_1t2mx_8 mx_MFileBody"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
data-type="download"
|
data-type="download"
|
||||||
|
|||||||
+2
-2
@@ -93,7 +93,7 @@ exports[`<MImageBody/> should open ImageView using thumbnail for encrypted svg 1
|
|||||||
</div>
|
</div>
|
||||||
<a
|
<a
|
||||||
aria-live="off"
|
aria-live="off"
|
||||||
class="mx_MessageTimestamp _content_kc5mt_8"
|
class="mx_MessageTimestamp _content_1r034_8"
|
||||||
href="https://matrix.to/#/!room:server/undefined"
|
href="https://matrix.to/#/!room:server/undefined"
|
||||||
>
|
>
|
||||||
Thu, Jan 15, 1970, 06:56
|
Thu, Jan 15, 1970, 06:56
|
||||||
@@ -300,7 +300,7 @@ exports[`<MImageBody/> should open ImageView using thumbnail for encrypted svg 1
|
|||||||
exports[`<MImageBody/> should render MFileBody for svg with no thumbnail 1`] = `
|
exports[`<MImageBody/> should render MFileBody for svg with no thumbnail 1`] = `
|
||||||
<DocumentFragment>
|
<DocumentFragment>
|
||||||
<span
|
<span
|
||||||
class="_content_f1s5h_8 mx_MFileBody"
|
class="_content_1t2mx_8 mx_MFileBody"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="mx_MediaBody _mediaBody_rgndh_8"
|
class="mx_MediaBody _mediaBody_rgndh_8"
|
||||||
|
|||||||
+4
-4
@@ -3,13 +3,13 @@
|
|||||||
exports[`<RoomListSearch /> renders 1`] = `
|
exports[`<RoomListSearch /> renders 1`] = `
|
||||||
<DocumentFragment>
|
<DocumentFragment>
|
||||||
<div
|
<div
|
||||||
class="_flex_4dswl_9 _view_z7ks9_8"
|
class="_flex_4dswl_9 _view_zk0ja_8"
|
||||||
data-testid="room-list-search"
|
data-testid="room-list-search"
|
||||||
role="search"
|
role="search"
|
||||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-2x); --mx-flex-wrap: nowrap;"
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-2x); --mx-flex-wrap: nowrap;"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="_button_1nw83_8 _search_z7ks9_16 _has-icon_1nw83_60"
|
class="_button_1nw83_8 _search_zk0ja_16 _has-icon_1nw83_60"
|
||||||
data-kind="secondary"
|
data-kind="secondary"
|
||||||
data-size="md"
|
data-size="md"
|
||||||
id="room-list-search-button"
|
id="room-list-search-button"
|
||||||
@@ -29,11 +29,11 @@ exports[`<RoomListSearch /> renders 1`] = `
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
<span
|
<span
|
||||||
class="_flex_4dswl_9 _search_container_z7ks9_29"
|
class="_flex_4dswl_9 _search_container_zk0ja_28"
|
||||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: start; --mx-flex-justify: space-between; --mx-flex-gap: 0; --mx-flex-wrap: nowrap;"
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: start; --mx-flex-justify: space-between; --mx-flex-gap: 0; --mx-flex-wrap: nowrap;"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="_search_text_z7ks9_41"
|
class="_search_text_zk0ja_40"
|
||||||
>
|
>
|
||||||
Search
|
Search
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -6,13 +6,103 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
|||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import fetchMock from "@fetch-mock/jest";
|
||||||
|
|
||||||
import getExportCSS from "../../../../src/utils/exportUtils/exportCSS";
|
import getExportCSS from "../../../../src/utils/exportUtils/exportCSS";
|
||||||
|
|
||||||
describe("exportCSS", () => {
|
describe("exportCSS", () => {
|
||||||
describe("getExportCSS", () => {
|
describe("getExportCSS", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
document.head.replaceChildren();
|
||||||
|
});
|
||||||
|
|
||||||
it("supports documents missing stylesheets", async () => {
|
it("supports documents missing stylesheets", async () => {
|
||||||
const css = await getExportCSS(new Set());
|
const css = await getExportCSS(new Set());
|
||||||
expect(css).not.toContain("color-scheme: light");
|
expect(css).not.toContain("color-scheme: light");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("fetches export stylesheets and filters unused css", async () => {
|
||||||
|
document.head.innerHTML = `
|
||||||
|
<link rel="stylesheet" href="/bundle.css" />
|
||||||
|
<link rel="stylesheet" href="/theme-light.css" />
|
||||||
|
<link rel="stylesheet" href="/theme-dark.css" />
|
||||||
|
`;
|
||||||
|
|
||||||
|
fetchMock.get(
|
||||||
|
"end:/bundle.css",
|
||||||
|
`
|
||||||
|
@font-face { font-family: Inter; src: url(inter.woff2); }
|
||||||
|
body { margin: 0; }
|
||||||
|
.mx_Used { font-family: Inter; color: #111111; }
|
||||||
|
.mx_Code { font-family: Fira Code; }
|
||||||
|
.mx_Unused { color: #123456; }
|
||||||
|
.mx_Empty {}
|
||||||
|
.mx_Used, .mx_UnusedComma { color: #abcdef; }
|
||||||
|
@media screen {
|
||||||
|
.mx_Used { --cpd-font-family-sans: "Inter"; }
|
||||||
|
.mx_UnusedNested { color: #654321; }
|
||||||
|
}
|
||||||
|
@supports (display: grid) {
|
||||||
|
.mx_UnusedSupported { color: #fedcba; }
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
);
|
||||||
|
fetchMock.get("end:/theme-light.css", ".mx_Theme { color: #222222; }");
|
||||||
|
|
||||||
|
const css = await getExportCSS(new Set(["mx_Used", "mx_Code", "mx_Theme"]));
|
||||||
|
|
||||||
|
expect(fetchMock).toHaveFetchedTimes(1, "end:/bundle.css");
|
||||||
|
expect(fetchMock).toHaveFetchedTimes(1, "end:/theme-light.css");
|
||||||
|
expect(fetchMock).not.toHaveFetched("end:/theme-dark.css");
|
||||||
|
|
||||||
|
expect(css).toContain("margin:0");
|
||||||
|
expect(css).toContain("#111");
|
||||||
|
expect(css).toContain("#222");
|
||||||
|
expect(css).toContain("#abcdef");
|
||||||
|
|
||||||
|
expect(css).not.toContain("@font-face");
|
||||||
|
expect(css).not.toContain("#123456");
|
||||||
|
expect(css).not.toContain("#654321");
|
||||||
|
expect(css).not.toContain("#fedcba");
|
||||||
|
|
||||||
|
expect(css).not.toContain("font-family:Inter");
|
||||||
|
expect(css).not.toContain("font-family:Fira Code");
|
||||||
|
expect(css).toContain("BlinkMacSystemFont");
|
||||||
|
expect(css).toContain("Menlo, Consolas");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps export-only css in the app cascade layer after layered font rules", async () => {
|
||||||
|
document.head.innerHTML = `
|
||||||
|
<link rel="stylesheet" href="/bundle.css" />
|
||||||
|
`;
|
||||||
|
|
||||||
|
fetchMock.get(
|
||||||
|
"end:/bundle.css",
|
||||||
|
`
|
||||||
|
@layer compound-tokens, compound-web, shared-components, app-web;
|
||||||
|
@layer compound-web {
|
||||||
|
.mx_Typography {
|
||||||
|
font: var(--cpd-font-heading-lg-regular);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@layer app-web {
|
||||||
|
body {
|
||||||
|
font: var(--cpd-font-body-md-regular) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const css = await getExportCSS(new Set(["mx_Typography"]));
|
||||||
|
|
||||||
|
expect(css).toContain("@layer compound-web{.mx_Typography{font:var(--cpd-font-heading-lg-regular)}}");
|
||||||
|
expect(css).toContain("@layer app-web{body{font:var(--cpd-font-body-md-regular)!important}}");
|
||||||
|
|
||||||
|
const exportCssLayerIndex = css.indexOf("@layer app-web {");
|
||||||
|
expect(exportCssLayerIndex).toBeGreaterThan(
|
||||||
|
css.indexOf("@layer app-web{body{font:var(--cpd-font-body-md-regular)!important}}"),
|
||||||
|
);
|
||||||
|
expect(css.slice(exportCssLayerIndex)).toBe("@layer app-web {css-file-stub}");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -65,13 +65,8 @@ export default {
|
|||||||
ignoreExportsUsedInFile: true,
|
ignoreExportsUsedInFile: true,
|
||||||
compilers: {
|
compilers: {
|
||||||
pcss: (text: string) =>
|
pcss: (text: string) =>
|
||||||
[...text.matchAll(/(?<=@)import[^;]+/g)]
|
[...text.matchAll(/@import\s+(?:url\()?["']([^"']+)["']\)?[^;]*;/g)]
|
||||||
.map(([line]) => {
|
.map(([, specifier]) => `import "${specifier}";`)
|
||||||
if (line.startsWith("import url(")) {
|
|
||||||
return line.replace("url(", "").slice(0, -1);
|
|
||||||
}
|
|
||||||
return line;
|
|
||||||
})
|
|
||||||
.join("\n"),
|
.join("\n"),
|
||||||
},
|
},
|
||||||
nx: {
|
nx: {
|
||||||
|
|||||||
@@ -5,5 +5,8 @@
|
|||||||
* Please see LICENSE files in the repository root for full details.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@import url("@vector-im/compound-design-tokens/assets/web/css/compound-design-tokens.css") layer(compound);
|
/* Shared cascade order: Compound tokens, Compound Web, shared components, then app overrides. */
|
||||||
@import url("@vector-im/compound-web/dist/style.css");
|
@layer compound-tokens, compound-web, shared-components, app-web;
|
||||||
|
|
||||||
|
@import url("@vector-im/compound-design-tokens/assets/web/css/compound-design-tokens.css") layer(compound-tokens);
|
||||||
|
@import url("@vector-im/compound-web/dist/style.css") layer(compound-web);
|
||||||
|
|||||||
@@ -8,11 +8,13 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
import type { StorybookConfig } from "@storybook/react-vite";
|
import type { StorybookConfig } from "@storybook/react-vite";
|
||||||
import fs from "node:fs";
|
import fs from "node:fs";
|
||||||
import { nodePolyfills } from "vite-plugin-node-polyfills";
|
import { nodePolyfills } from "vite-plugin-node-polyfills";
|
||||||
import { mergeConfig } from "vite";
|
import { mergeConfig, normalizePath, type Plugin } from "vite";
|
||||||
import { dirname, join } from "node:path";
|
import { dirname, join } from "node:path";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
|
const srcRoot = normalizePath(join(__dirname, "..", "src"));
|
||||||
|
const sharedComponentsLayer = "shared-components";
|
||||||
|
|
||||||
// Get a list of available languages so the language selector can display them at runtime
|
// Get a list of available languages so the language selector can display them at runtime
|
||||||
const languageFiles = fs.readdirSync(join(__dirname, "..", "src", "i18n", "strings")).map((f) => f.slice(0, -5));
|
const languageFiles = fs.readdirSync(join(__dirname, "..", "src", "i18n", "strings")).map((f) => f.slice(0, -5));
|
||||||
@@ -36,6 +38,24 @@ function getAbsolutePath(value: string): any {
|
|||||||
return dirname(fileURLToPath(import.meta.resolve(`${value}/package.json`)));
|
return dirname(fileURLToPath(import.meta.resolve(`${value}/package.json`)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function layerSharedComponentCssModules(): Plugin {
|
||||||
|
return {
|
||||||
|
name: "element-web-shared-components-storybook-css-layer",
|
||||||
|
enforce: "pre",
|
||||||
|
transform(code, id) {
|
||||||
|
const cssPath = normalizePath(id.split("?")[0]);
|
||||||
|
if (!cssPath.startsWith(srcRoot) || !cssPath.endsWith(".module.css")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
code: `@layer ${sharedComponentsLayer} {\n${code}\n}\n`,
|
||||||
|
map: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const config: StorybookConfig = {
|
const config: StorybookConfig = {
|
||||||
stories: ["../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
|
stories: ["../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
|
||||||
addons: [
|
addons: [
|
||||||
@@ -61,6 +81,7 @@ const config: StorybookConfig = {
|
|||||||
async viteFinal(config) {
|
async viteFinal(config) {
|
||||||
return mergeConfig(config, {
|
return mergeConfig(config, {
|
||||||
plugins: [
|
plugins: [
|
||||||
|
layerSharedComponentCssModules(),
|
||||||
// Needed for counterpart to work
|
// Needed for counterpart to work
|
||||||
nodePolyfills({ include: ["util"], globals: { global: false } }),
|
nodePolyfills({ include: ["util"], globals: { global: false } }),
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,6 +6,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
border-radius: 32px !important;
|
border-radius: 32px;
|
||||||
background-color: var(--cpd-color-bg-subtle-primary) !important;
|
background-color: var(--cpd-color-bg-subtle-primary);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -7,5 +7,5 @@
|
|||||||
|
|
||||||
.title {
|
.title {
|
||||||
/* For first title, there is already enough space at the top */
|
/* For first title, there is already enough space at the top */
|
||||||
margin-top: 0 !important;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-3
@@ -16,9 +16,8 @@
|
|||||||
.search {
|
.search {
|
||||||
/* The search button should take all the remaining space */
|
/* The search button should take all the remaining space */
|
||||||
flex: 1;
|
flex: 1;
|
||||||
/* !important is needed to override compound button in EW */
|
font: var(--cpd-font-body-md-regular);
|
||||||
font: var(--cpd-font-body-md-regular) !important;
|
color: var(--cpd-color-text-secondary);
|
||||||
color: var(--cpd-color-text-secondary) !important;
|
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
|
|||||||
@@ -7,18 +7,18 @@
|
|||||||
|
|
||||||
.container {
|
.container {
|
||||||
color: var(--cpd-color-text-primary);
|
color: var(--cpd-color-text-primary);
|
||||||
svg {
|
|
||||||
/* Ensure button icons are primary too */
|
|
||||||
color: var(--cpd-color-text-primary) !important;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.secondaryAction svg {
|
.container svg {
|
||||||
color: var(--cpd-color-text-secondary) !important;
|
color: var(--cpd-color-text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.primaryAction svg {
|
.secondaryAction.secondaryAction[data-kind="secondary"] > svg {
|
||||||
color: var(--cpd-color-text-on-solid-primary) !important;
|
color: var(--cpd-color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.primaryAction.primaryAction[data-kind="primary"] > svg {
|
||||||
|
color: var(--cpd-color-text-on-solid-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
|
|||||||
+5
-5
@@ -6,15 +6,15 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
.picker_menu {
|
.picker_menu {
|
||||||
max-inline-size: none !important;
|
max-inline-size: none;
|
||||||
padding: 0 !important;
|
padding: 0;
|
||||||
gap: 0 !important;
|
gap: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.picker_menu_item {
|
.picker_menu_item {
|
||||||
padding: var(--cpd-space-3x) var(--cpd-space-5x) !important;
|
padding: var(--cpd-space-3x) var(--cpd-space-5x);
|
||||||
}
|
}
|
||||||
|
|
||||||
.picker_separator {
|
.picker_separator {
|
||||||
margin-inline: 0 !important;
|
margin-inline: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-6
@@ -6,15 +6,15 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
.picker_menu_item {
|
.picker_menu_item {
|
||||||
padding: var(--cpd-space-3x) var(--cpd-space-5x) !important;
|
padding: var(--cpd-space-3x) var(--cpd-space-5x);
|
||||||
}
|
}
|
||||||
|
|
||||||
.picker_form {
|
.picker_form {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row !important;
|
flex-direction: row;
|
||||||
flex-wrap: wrap !important;
|
flex-wrap: wrap;
|
||||||
padding: 0 !important;
|
padding: 0;
|
||||||
gap: var(--cpd-space-2x) !important;
|
gap: var(--cpd-space-2x);
|
||||||
color: var(--cpd-color-text-primary);
|
color: var(--cpd-color-text-primary);
|
||||||
font: var(--cpd-font-body-md-medium);
|
font: var(--cpd-font-body-md-medium);
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
.picker_input_date {
|
.picker_input_date {
|
||||||
font: inherit;
|
font: inherit;
|
||||||
color-scheme: light;
|
color-scheme: light;
|
||||||
padding: var(--cpd-space-2x) !important;
|
padding: var(--cpd-space-2x);
|
||||||
|
|
||||||
:global(.cpd-theme-dark) &,
|
:global(.cpd-theme-dark) &,
|
||||||
:global(.cpd-theme-dark-hc) & {
|
:global(.cpd-theme-dark-hc) & {
|
||||||
|
|||||||
+9
-2
@@ -25,7 +25,14 @@ export interface UrlPreviewGroupViewSnapshot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface UrlPreviewGroupViewProps {
|
export interface UrlPreviewGroupViewProps {
|
||||||
|
/**
|
||||||
|
* The view model for the component.
|
||||||
|
*/
|
||||||
vm: ViewModel<UrlPreviewGroupViewSnapshot> & UrlPreviewGroupViewActions;
|
vm: ViewModel<UrlPreviewGroupViewSnapshot> & UrlPreviewGroupViewActions;
|
||||||
|
/**
|
||||||
|
* Extra CSS classes to apply to the component.
|
||||||
|
*/
|
||||||
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UrlPreviewGroupViewActions {
|
export interface UrlPreviewGroupViewActions {
|
||||||
@@ -42,7 +49,7 @@ export type UrlPreviewGroupViewModel = ViewModel<UrlPreviewGroupViewSnapshot, Ur
|
|||||||
* The view lays out one or more link previews, can collapse or expand
|
* The view lays out one or more link previews, can collapse or expand
|
||||||
* overflowed previews, and exposes a control to hide the group.
|
* overflowed previews, and exposes a control to hide the group.
|
||||||
*/
|
*/
|
||||||
export function UrlPreviewGroupView({ vm }: UrlPreviewGroupViewProps): JSX.Element | null {
|
export function UrlPreviewGroupView({ vm, className }: UrlPreviewGroupViewProps): JSX.Element | null {
|
||||||
const { translate: _t } = useI18n();
|
const { translate: _t } = useI18n();
|
||||||
const { previews, totalPreviewCount, previewsLimited, overPreviewLimit, compactLayout } = useViewModel(vm);
|
const { previews, totalPreviewCount, previewsLimited, overPreviewLimit, compactLayout } = useViewModel(vm);
|
||||||
if (previews.length === 0) {
|
if (previews.length === 0) {
|
||||||
@@ -61,7 +68,7 @@ export function UrlPreviewGroupView({ vm }: UrlPreviewGroupViewProps): JSX.Eleme
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.wrapper}>
|
<div className={classNames(className, styles.wrapper)}>
|
||||||
<div className={classNames(styles.previewGroup, compactLayout && styles.compactLayout)}>
|
<div className={classNames(styles.previewGroup, compactLayout && styles.compactLayout)}>
|
||||||
{previews.map((preview) => (
|
{previews.map((preview) => (
|
||||||
<LinkPreview key={preview.link} onImageClick={() => vm.onImageClick(preview)} {...preview} />
|
<LinkPreview key={preview.link} onImageClick={() => vm.onImageClick(preview)} {...preview} />
|
||||||
|
|||||||
+11
-11
@@ -14,31 +14,31 @@
|
|||||||
.toolbar_item {
|
.toolbar_item {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 0 !important;
|
padding: 0;
|
||||||
min-block-size: 0 !important;
|
min-block-size: 0;
|
||||||
|
|
||||||
margin: 3px !important;
|
margin: 3px;
|
||||||
border-radius: 6px !important;
|
border-radius: 6px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--cpd-color-bg-subtle-secondary) !important;
|
background-color: var(--cpd-color-bg-subtle-secondary);
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.toolbar_item[data-presentation="icon"] {
|
.toolbar_item[data-presentation="icon"] {
|
||||||
block-size: 28px !important;
|
block-size: 28px;
|
||||||
inline-size: 28px !important;
|
inline-size: 28px;
|
||||||
|
|
||||||
color: var(--cpd-color-icon-secondary) !important;
|
color: var(--cpd-color-icon-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.toolbar_item[data-presentation="label"] {
|
.toolbar_item[data-presentation="label"] {
|
||||||
padding-inline-start: var(--cpd-space-2x) !important;
|
padding-inline-start: var(--cpd-space-2x);
|
||||||
padding-inline-end: var(--cpd-space-2x) !important;
|
padding-inline-end: var(--cpd-space-2x);
|
||||||
|
|
||||||
font: var(--cpd-font-body-md-regular);
|
font: var(--cpd-font-body-md-regular);
|
||||||
text-decoration: none !important;
|
text-decoration: none;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -6,8 +6,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
.audioPlayer {
|
.audioPlayer {
|
||||||
padding: var(--cpd-space-4x) var(--cpd-space-3x) var(--cpd-space-3x) var(--cpd-space-3x) !important;
|
padding: var(--cpd-space-4x) var(--cpd-space-3x) var(--cpd-space-3x) var(--cpd-space-3x);
|
||||||
border-radius: 8px !important;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mediaInfo {
|
.mediaInfo {
|
||||||
|
|||||||
+2
-3
@@ -26,8 +26,8 @@
|
|||||||
|
|
||||||
& button {
|
& button {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin: 0 !important;
|
margin: 0;
|
||||||
padding: 0 !important;
|
padding: 0;
|
||||||
border: none;
|
border: none;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-block-size: unset;
|
min-block-size: unset;
|
||||||
@@ -57,7 +57,6 @@
|
|||||||
|
|
||||||
.content [data-type="download"] {
|
.content [data-type="download"] {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
color: var(--cpd-color-text-action-accent);
|
|
||||||
|
|
||||||
& iframe {
|
& iframe {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
color: var(--cpd-color-text-secondary) !important; /* override anchor color */
|
color: var(--cpd-color-text-secondary); /* override anchor color */
|
||||||
font-size: var(--cpd-font-size-body-xs);
|
font-size: var(--cpd-font-size-body-xs);
|
||||||
font-variant-numeric: tabular-nums;
|
font-variant-numeric: tabular-nums;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|||||||
@@ -6,12 +6,36 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { readFileSync, writeFileSync } from "node:fs";
|
||||||
import { dirname, resolve } from "node:path";
|
import { dirname, resolve } from "node:path";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
import { defineConfig, esmExternalRequirePlugin } from "vite";
|
import { defineConfig, esmExternalRequirePlugin, type Plugin } from "vite";
|
||||||
import dts from "unplugin-dts/vite";
|
import dts from "unplugin-dts/vite";
|
||||||
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
|
const cssLayerOrder = "@layer compound-tokens, compound-web, shared-components, app-web;";
|
||||||
|
const sharedComponentsLayer = "shared-components";
|
||||||
|
|
||||||
|
function layerCssAssets(): Plugin {
|
||||||
|
return {
|
||||||
|
name: "element-web-shared-components-css-layer",
|
||||||
|
writeBundle(_options, bundle): void {
|
||||||
|
for (const asset of Object.values(bundle)) {
|
||||||
|
if (asset.type !== "asset" || asset.fileName !== "element-web-shared-components.css") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cssPath = resolve(__dirname, "dist", asset.fileName);
|
||||||
|
const source = readFileSync(cssPath, "utf-8");
|
||||||
|
if (source.startsWith(cssLayerOrder)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeFileSync(cssPath, `${cssLayerOrder}\n@layer ${sharedComponentsLayer} {\n${source}\n}\n`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
build: {
|
build: {
|
||||||
@@ -50,6 +74,7 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
layerCssAssets(),
|
||||||
dts({
|
dts({
|
||||||
bundleTypes: true,
|
bundleTypes: true,
|
||||||
include: ["src/**/*.{ts,tsx}"],
|
include: ["src/**/*.{ts,tsx}"],
|
||||||
|
|||||||
Reference in New Issue
Block a user