Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion .storybook/preview.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import React from "react";

import ApplicationContainer from "../src/components/Application/ApplicationContainer";
import ApplicationContent from "../src/components/Application/ApplicationContent";

import "./styles.scss";

export const parameters = {
Expand All @@ -17,8 +22,17 @@ export const parameters = {
const preview = {
// Enables auto-generated documentation for all stories
// @see https://storybook.js.org/docs/writing-docs/autodocs
tags: ['autodocs'],
tags: ["autodocs"],
parameters,
decorators: [
(Story) => (
<ApplicationContainer style={{ position: "relative" }}>
<ApplicationContent>
<Story />
</ApplicationContent>
</ApplicationContainer>
),
],
};

export default preview;
11 changes: 11 additions & 0 deletions src/cmem/markdown/Markdown.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
import React from "react";
import { Meta, StoryFn } from "@storybook/react";

import { Card, CardContent } from "../../components/Card/index";

import { Markdown } from "./../../../index";

export default {
title: "Cmem/Markdown",
component: Markdown,
argTypes: {},
decorators: [
(Story) => (
<Card>
<CardContent>
<Story />
</CardContent>
</Card>
),
],
} as Meta<typeof Markdown>;

const Template: StoryFn<typeof Markdown> = (args) => <Markdown {...args} />;
Expand Down
143 changes: 142 additions & 1 deletion src/common/scss/_color-functions.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
@error "Need at least 1 color to create color tints.";
}

// we asume that it correct to give only start and end of tint weights
// we assume that it correct to give only start and end of tint weights
// only echo debug message if we have a 1, 3 or 4 color values
@if $count-colors != 2 {
@debug "Got only #{$count-colors} tints: #{$colorset}";
Expand Down Expand Up @@ -103,6 +103,147 @@
}
}

/**
* Reverse alpha compositing ("remove" a background color from a color).
*
* Given an opaque $color that visually results from compositing an unknown
* semi-transparent foreground color over $background, this returns that
* foreground color including the alpha channel, so that
* compositing the result over $background reproduces $color again.
*
* For every channel the minimal alpha is determined that keeps the resulting
* foreground channel inside the valid [0, 255] range; the largest of these
* per-channel alphas is used for all channels. This yields the most
* transparent foreground color possible for the given $background.
*/
@function eccgui-color-subtract-background($color, $background) {
@if meta.type-of($color) != "color" or meta.type-of($background) != "color" {
@error "eccgui-color-subtract-background() expects SASS color values for $color and $background.";
}

$channels: "red" "green" "blue";
$alpha: 0;

// determine the minimal alpha that keeps every foreground channel in range
@each $channel in $channels {
$target: color.channel($color, $channel, $space: rgb);
$base: color.channel($background, $channel, $space: rgb);
$channel-alpha: 0;

@if $target > $base {
// foreground channel goes towards 255, limited by the headroom (255 - base)
$channel-alpha: math.div($target - $base, 255 - $base);
} @else if $target < $base {
// foreground channel goes towards 0, limited by the available range (base)
$channel-alpha: math.div($base - $target, $base);
}

@if $channel-alpha > $alpha {
$alpha: $channel-alpha;
}
}

@if $alpha <= 0 {
// $color equals $background, the foreground is fully transparent
@return transparent;
}

// recover the foreground channels: foreground = base + (target - base) / alpha
$foreground: ();

@each $channel in $channels {
$target: color.channel($color, $channel, $space: rgb);
$base: color.channel($background, $channel, $space: rgb);
$foreground: list.append($foreground, $base + math.div($target - $base, $alpha));
}

@return color.change(
rgb(list.nth($foreground, 1), list.nth($foreground, 2), list.nth($foreground, 3)),
$alpha: $alpha
);
}

/**
* Alpha compositing ("flatten" a semi-transparent color onto a background).
*
* Given a $color that may carry an alpha channel and an opaque $background,
* this composites $color over $background and returns the resulting opaque
* color. It is the inverse of eccgui-color-subtract-background().
*
* Per channel: result = foreground * alpha + background * (1 - alpha).
*/
@function eccgui-color-flatten-foreground($color, $background) {
@if meta.type-of($color) != "color" or meta.type-of($background) != "color" {
@error "eccgui-color-blend-onto-background() expects SASS color values for $color and $background.";
}

$alpha: color.alpha($color);

@if $alpha >= 1 {
// fully opaque foreground, background has no influence
@return color.change($color, $alpha: 1);
}

$channels: "red" "green" "blue";
$blended: ();

@each $channel in $channels {
$foreground: color.channel($color, $channel, $space: rgb);
$base: color.channel($background, $channel, $space: rgb);
$blended: list.append($blended, $foreground * $alpha + $base * (1 - $alpha));
}

@return rgb(list.nth($blended, 1), list.nth($blended, 2), list.nth($blended, 3));
}

/**
* Switch the background color for a color value.
*/
@function eccgui-color-switch-background($color, $bg_old, $bg_new) {
@return eccgui-color-flatten-foreground(eccgui-color-subtract-background($color, $bg_old), $bg_new);
}

/**
* Process a color from a light palette to get used in a dark palette.
*/
@function eccgui-color-darkify($color) {
// very simple process, only invert lightness
$color-dark-simple: color.change($color, $lightness: 100% - color.channel($color, "lightness", $space: hsl));

// read values for lightness and saturation
// use them to improve the final darkified color
$l: color.channel($color-dark-simple, "lightness", $space: hsl);
$s: color.channel($color-dark-simple, "saturation", $space: hsl);

// improve color perception
// for light and saturated colors bring back a bit of the original color
@if $l > 45% and $s > 70% {
$color-dark-simple: color.mix($color, $color-dark-simple, 25%);
$l: color.channel($color-dark-simple, "lightness", $space: hsl);
$s: color.channel($color-dark-simple, "saturation", $space: hsl);
}

// improve lightness
// make dark colors a bit lighter again
@if $l < 50% {
$l: $l + (10% * math.div(50% - $l, 50%));
}

// improve saturation
// decrease it gradually based on lightness and saturation to prevent straining "neon" effects
$s: $s * (1 - 0.1 * math.div($l, 100%) - 0.1 * math.div($s, 100%));

// return improved darkified color, round rgb values
$color-dark-improved: color.change($color-dark-simple, $lightness: $l, $saturation: $s);

@return color.change(
$color-dark-improved,
$red: math.round(color.channel($color-dark-improved, "red")),
$green: math.round(color.channel($color-dark-improved, "green")),
$blue: math.round(color.channel($color-dark-improved, "blue"))
);
}

$debug-rgba-values: "yes";

/**
Expand Down
17 changes: 15 additions & 2 deletions src/components/Application/ApplicationContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,33 @@ export interface ApplicationContainerProps extends React.HTMLAttributes<HTMLDivE
* This need to match with a `dropzone-for` data attribute on available dropzones for dragged elements.
*/
monitorDropzonesFor?: string[];
/**
* Use a light or dark color palette for the GUI.
* On `auto` it depends on the system configuration.
*/
themeMode?: "dark" | "light" | "auto";
}

export const ApplicationContainer = ({
children,
className = "",
monitorDropzonesFor = [],
themeMode = "auto",
...otherDivProps
}: ApplicationContainerProps) => {
const containerRef = React.useRef<any>(null);
const containerRef = React.useRef(null);
useDropzoneMonitor(monitorDropzonesFor);

return (
<OverlaysProvider>
<div ref={containerRef} className={`${eccgui}-application__container ${className}`} {...otherDivProps}>
<div
ref={containerRef}
className={
`${eccgui}-application__container ${eccgui}-palette--${themeMode}` +
(className ? ` ${className}` : "")
}
{...otherDivProps}
>
{children}
</div>
</OverlaysProvider>
Expand Down
30 changes: 27 additions & 3 deletions src/components/Application/_colors.scss
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
@use "sass:map";
@use "sass:list";

:root {
// creating css custom properties from palette colors
@each $palette-group-name, $palette-group-tints in $eccgui-color-palette-light {
/**
* creating css custom properties from palette colors
*/
@mixin eccgui-custom-property-palette($color-palette) {
@each $palette-group-name, $palette-group-tints in $color-palette {
@each $palette-tint-name, $palette-tint-colors in $palette-group-tints {
@for $i from 1 through list.length($palette-tint-colors) {
$css-property-name: #{eccgui-color-name($palette-group-name, $palette-tint-name, ($i * 2 - 1) * 100)};
Expand All @@ -13,6 +15,16 @@
}
}
}
}

:root {
// css custom properties for light palette
@include eccgui-custom-property-palette($eccgui-color-palette-light);

@media (prefers-color-scheme: dark) {
// css custom properties for dark palette
@include eccgui-custom-property-palette($eccgui-color-palette-dark);
}

// set aliases for base colors
--#{$eccgui}-color-primary: #{$eccgui-color-primary};
Expand All @@ -28,3 +40,15 @@
--#{$eccgui}-color-danger-foreground: #{$eccgui-color-danger-text};
--#{$eccgui}-color-danger-background: #{$eccgui-color-danger-background};
}

.#{$eccgui}-palette--light,
html:has(.#{$eccgui}-application__container.#{$eccgui}-palette--light) {
// css custom properties for a forced light palette
@include eccgui-custom-property-palette($eccgui-color-palette-light);
}

.#{$eccgui}-palette--dark,
html:has(.#{$eccgui}-application__container.#{$eccgui}-palette--dark) {
// css custom properties for a forced dark palette
@include eccgui-custom-property-palette($eccgui-color-palette-dark);
}
5 changes: 5 additions & 0 deletions src/components/Application/_container.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
html,
.#{$eccgui}-application__container {
color: $eccgui-color-workspace-text;
background-color: $eccgui-color-workspace-background;
}
2 changes: 2 additions & 0 deletions src/components/Application/_sidebar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ $ui-02: $eccgui-color-workspace-background !default;

.#{$prefix}--side-nav__navigation {
padding: $eccgui-size-block-whitespace calc(0.5 * (#{mini-units(8)} - 30px));
color: eccgui-color-var("identity", "text", "900");
background-color: eccgui-color-var("identity", "background", "100");
transition: none;
}

Expand Down
1 change: 1 addition & 0 deletions src/components/Application/application.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// @import 'config';
@import "colors";
@import "container";
@import "header";
@import "toolbar";

Expand Down
14 changes: 13 additions & 1 deletion src/components/Application/stories/Application.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ interface ApplicationBasicExampleProps {
openUserMenu: boolean;
countNotifications: number;
colorBackgroundHeader?: string;
themeMode?: "light" | "dark" | "auto";
}

function ApplicationBasicExample(args: ApplicationBasicExampleProps) {
Expand All @@ -50,11 +51,15 @@ export default {
colorBackgroundHeader: {
control: { type: "color" },
},
themeMode: {
control: "select",
options: ["auto", "light", "dark"],
},
},
} as Meta<typeof ApplicationBasicExample>;

const TemplateBasicExample: StoryFn<typeof ApplicationBasicExample> = (args) => (
<ApplicationContainer>
<ApplicationContainer themeMode={args.themeMode}>
<ApplicationHeader
aria-label={"Application"}
style={
Expand Down Expand Up @@ -147,3 +152,10 @@ BasicExample.args = {
openUserMenu: false,
countNotifications: 234,
};
BasicExample.decorators = [
(Story) => (
<div style={{ margin: "calc(-1 * var(--eccgui-size-block-whitespace) - 8px)" }}>
<Story />
</div>
),
];
9 changes: 9 additions & 0 deletions src/components/Checkbox/checkbox.scss
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@ $switch-checked-background-color-disabled: eccgui-color-rgba(
// TODO: set set icon line colors here we need a custom js inject like blueprint uses
color: $eccgui-color-checkbox-checked;
background-image: url("~@carbon/icons/svg/32/checkbox.svg");

@media (prefers-color-scheme: dark) {
filter: invert(1);
}

html:has(.#{$eccgui}-application__container.#{$eccgui}-palette--dark) :not(.#{$eccgui}-palette--light) &,
.#{$eccgui}-palette--dark :not(.#{$eccgui}-palette--light) & {
filter: invert(1);
}
}
input:checked ~ .#{$ns}-control-indicator::before {
background-image: url("~@carbon/icons/svg/32/checkbox--checked.svg");
Expand Down
8 changes: 8 additions & 0 deletions src/components/Iframe/iframe.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
.#{eccgui}-iframe iframe {
width: 100%;
color-scheme: light dark;

.#{eccgui}-palette--light & {
color-scheme: light;
}
.#{eccgui}-palette--dark & {
color-scheme: dark;
}
}

// provide calc value to remove header + card title + white space from viewport height
Expand Down
9 changes: 9 additions & 0 deletions src/components/RadioButton/radiobutton.scss
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@

input ~ .#{$ns}-control-indicator::before {
background-image: url("~@carbon/icons/svg/32/radio-button.svg");

@media (prefers-color-scheme: dark) {
filter: invert(1);
}

html:has(.#{$eccgui}-application__container.#{$eccgui}-palette--dark) :not(.#{$eccgui}-palette--light) &,
.#{$eccgui}-palette--dark :not(.#{$eccgui}-palette--light) & {
filter: invert(1);
}
}
input:checked ~ .#{$ns}-control-indicator::before {
background-image: url("~@carbon/icons/svg/32/radio-button--checked.svg");
Expand Down
Loading
Loading