This repository has been archived on 2024-05-31. You can view files and clone it, but cannot push or open issues or pull requests.
authentik/web/src/common/utils.ts
Ken Sternberg f179d6572e
web: Storybook css import fix (#5964)
* web: fix storybook `build` css import issue

This is an incredibly frustrating issue, because Storybook works
in `dev` mode but not in `build` mode, and that's not at all what
you'd expecte from a mature piece of software.  Lit uses the native
CSS adoptedStylesheets field, which takes only a constructedStylesheet.
Lit provides a way of generating those, but the imports from
Patternfly (or any `.css` file) are text, and converting those to
stylesheets required a bit of magic.

What this means going forward is that any Storied components will
have to have their CSS wrapped in a way that ensures it is managed
correctly by Lit (well, to be pedantic, by the
shadowDOM.adoptedStylesheets).  That wrapper is provided and the
components that need it have been wrapped.

This problem deserves further investigation, but for the time
being this actually does solve it with a minimum amount of surgical
pain.

* web: fix storybook build issue

This commit further fixes the typing issues around strings, CSSResults,
and CSSStyleSheets by providing overloaded functions that assist
consumers in knowing that if they send an array to expect an array
in return, and if they send a scalar expect a scalar in return.

* replace any with unknown

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2023-06-16 13:36:04 +02:00

145 lines
5.4 KiB
TypeScript

import { SentryIgnoredError } from "@goauthentik/common/errors";
import { CSSResult, css } from "lit";
export function getCookie(name: string): string {
let cookieValue = "";
if (document.cookie && document.cookie !== "") {
const cookies = document.cookie.split(";");
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === name + "=") {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
export function convertToSlug(text: string): string {
return text
.toLowerCase()
.replace(/ /g, "-")
.replace(/[^\w-]+/g, "");
}
export function convertToTitle(text: string): string {
return text.replace(/\w\S*/g, function (txt) {
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
});
}
/**
* Truncate a string based on maximum word count
*/
export function truncateWords(string: string, length = 10): string {
string = string || "";
const array = string.trim().split(" ");
const ellipsis = array.length > length ? "..." : "";
return array.slice(0, length).join(" ") + ellipsis;
}
/**
* Truncate a string based on character count
*/
export function truncate(string: string, length = 10): string {
return string.length > length ? `${string.substring(0, length)}...` : string;
}
export function camelToSnake(key: string): string {
const result = key.replace(/([A-Z])/g, " $1");
return result.split(" ").join("_").toLowerCase();
}
export function groupBy<T>(objects: T[], callback: (obj: T) => string): Array<[string, T[]]> {
const m = new Map<string, T[]>();
objects.forEach((obj) => {
const group = callback(obj);
if (!m.has(group)) {
m.set(group, []);
}
const tProviders = m.get(group) || [];
tProviders.push(obj);
});
return Array.from(m).sort();
}
export function first<T>(...args: Array<T | undefined | null>): T {
for (let index = 0; index < args.length; index++) {
const element = args[index];
if (element !== undefined && element !== null) {
return element;
}
}
throw new SentryIgnoredError(`No compatible arg given: ${args}`);
}
export function hexEncode(buf: Uint8Array): string {
return Array.from(buf)
.map(function (x) {
return ("0" + x.toString(16)).substr(-2);
})
.join("");
}
// Taken from python's string module
export const ascii_lowercase = "abcdefghijklmnopqrstuvwxyz";
export const ascii_uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
export const ascii_letters = ascii_lowercase + ascii_uppercase;
export const digits = "0123456789";
export const hexdigits = digits + "abcdef" + "ABCDEF";
export const octdigits = "01234567";
export const punctuation = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
export function randomString(len: number, charset: string): string {
const chars = [];
const array = new Uint8Array(len);
self.crypto.getRandomValues(array);
for (let index = 0; index < len; index++) {
chars.push(charset[Math.floor(charset.length * (array[index] / Math.pow(2, 8)))]);
}
return chars.join("");
}
export function dateTimeLocal(date: Date): string {
// So for some reason, the datetime-local input field requires ISO Datetime as value
// But the standard javascript date.toISOString() returns everything with seconds and
// milliseconds, which the input field doesn't like (on chrome, on firefox its fine)
// On chrome, setting .valueAsNumber works, but that causes an error on firefox, so go
// figure.
// Additionally, toISOString always returns the date without timezone, which we would like
// to include for better usability
const tzOffset = new Date().getTimezoneOffset() * 60000; //offset in milliseconds
const localISOTime = new Date(date.getTime() - tzOffset).toISOString().slice(0, -1);
const parts = localISOTime.split(":");
return `${parts[0]}:${parts[1]}`;
}
// Lit is extremely well-typed with regard to CSS, and Storybook's `build` does not currently have a
// coherent way of importing CSS-as-text into CSSStyleSheet. It works well when Storybook is running
// in `dev,` but in `build` it fails. Storied components will have to map their textual CSS imports
// using the function below.
type AdaptableStylesheet = Readonly<string | CSSResult | CSSStyleSheet>;
type AdaptedStylesheets = CSSStyleSheet | CSSStyleSheet[];
const isCSSResult = (v: unknown): v is CSSResult =>
v instanceof CSSResult && v.styleSheet !== undefined;
// prettier-ignore
export const _adaptCSS = (sheet: AdaptableStylesheet): CSSStyleSheet =>
(typeof sheet === "string" ? css([sheet] as unknown as TemplateStringsArray, ...[]).styleSheet
: isCSSResult(sheet) ? sheet.styleSheet
: sheet) as CSSStyleSheet;
// Overloaded function definitions inform consumers that if you pass it an array, expect an array in
// return; if you pass it a scaler, expect a scalar in return.
export function adaptCSS(sheet: AdaptableStylesheet): CSSStyleSheet;
export function adaptCSS(sheet: AdaptableStylesheet[]): CSSStyleSheet[];
export function adaptCSS(sheet: AdaptableStylesheet | AdaptableStylesheet[]): AdaptedStylesheets {
return Array.isArray(sheet) ? sheet.map(_adaptCSS) : _adaptCSS(sheet);
}