Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/green-toys-tease.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@xeho91/lib-css": minor
---

✨ Initial library package with features: `css` & `global`
7 changes: 1 addition & 6 deletions libs/css/src/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,7 @@ export class Block<const TChildren extends Item[] = Item[]> extends IterableInst
public toString(): Stringified<TChildren> {
const { children } = this;
let results = "";
let index = 0;
for (const child of children) {
results += child.toString();
if (!this.is_index_last(index)) results += ";";
index++;
}
for (const child of children) results += child.toString();
return `{${results}}` as Stringified<TChildren>;
}

Expand Down
37 changes: 37 additions & 0 deletions libs/css/src/combinator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { readonly_set } from "@xeho91/lib-snippet/set";
import type { IterableElement } from "@xeho91/lib-type/iterable";
import type { Display } from "@xeho91/lib-type/trait/display";
import type { Combinator as CombinatorAST } from "css-tree";

import type { ToAST } from "#ast";

export type CombinatorName = IterableElement<typeof Combinator.NAMES>;

export class Combinator<TName extends string = string> implements Display, ToAST {
public static readonly NAMES = readonly_set([" ", ">"]);

public static [Symbol.iterator](): IterableIterator<CombinatorName> {
return Combinator.NAMES[Symbol.iterator]();
}

public static CHILD = new Combinator(">");
public static SPACE = new Combinator(" ");

public readonly name: TName;

private constructor(name: TName) {
this.name = name;
}

public toString(): TName {
return this.name;
}

public to_ast(): CombinatorAST {
const { name } = this;
return {
type: "Combinator",
name,
};
}
}
4 changes: 2 additions & 2 deletions libs/css/src/declaration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export class Declaration<TProperty extends Property = Property, TValue extends V

public toString(): Stringified<TProperty, TValue> {
const { property, value } = this;
return `${property}:${value}` as Stringified<TProperty, TValue>;
return `${property}:${value};` as Stringified<TProperty, TValue>;
}

public to_ast(): DeclarationAST {
Expand All @@ -37,4 +37,4 @@ export class Declaration<TProperty extends Property = Property, TValue extends V
}
}

type Stringified<TProperty extends Property, TValue extends Value> = `${ToString<TProperty>}:${ToString<TValue>}`;
type Stringified<TProperty extends Property, TValue extends Value> = `${ToString<TProperty>}:${ToString<TValue>};`;
1 change: 1 addition & 0 deletions libs/css/src/operator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export class Operator<TKind extends OperatorKind = OperatorKind> implements Disp
return Operator.OPERATORS[Symbol.iterator]();
}

public static COLON = new Operator(":");
public static COMMA = new Operator(",");
public static FORWARD_SLASH = new Operator("/");

Expand Down
8 changes: 4 additions & 4 deletions libs/css/src/property/box-shadow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ if (import.meta.vitest) {
}),
);
const stringified = box_shadow.toString();
const expected = "box-shadow:0px 0px 0px 0px transparent";
const expected = "box-shadow:0px 0px 0px 0px transparent;";
expect(stringified).toBe(expected);
expectTypeOf(stringified).toEqualTypeOf<typeof expected>();
});
Expand Down Expand Up @@ -299,7 +299,7 @@ if (import.meta.vitest) {
const box_shadow = new BoxShadow(layer1, layer2, layer3);
const stringified = box_shadow.toString();
const expected =
"box-shadow:0px 0px 0px 0px transparent , 2px 2px 0px 0px transparent , 3px 3px 0px 0px oklch(0% 0% 0deg / 100%)";
"box-shadow:0px 0px 0px 0px transparent , 2px 2px 0px 0px transparent , 3px 3px 0px 0px oklch(0% 0% 0deg / 100%);";
expect(stringified).toBe(expected);
expectTypeOf(stringified).toEqualTypeOf<typeof expected>();
});
Expand All @@ -312,7 +312,7 @@ if (import.meta.vitest) {
const { declaration } = box_shadow;
expect(declaration).toBeInstanceOf(Declaration);
expect(declaration.toString()).toMatchInlineSnapshot(
`"box-shadow:var(--box-shadow-1-x) var(--box-shadow-1-y) var(--box-shadow-1-blur) var(--box-shadow-1-spread) var(--box-shadow-1-color)"`,
`"box-shadow:var(--box-shadow-1-x) var(--box-shadow-1-y) var(--box-shadow-1-blur) var(--box-shadow-1-spread) var(--box-shadow-1-color);"`,
);
});

Expand All @@ -324,7 +324,7 @@ if (import.meta.vitest) {
const { declaration } = box_shadow;
expect(declaration).toBeInstanceOf(Declaration);
expect(declaration.toString()).toMatchInlineSnapshot(
`"box-shadow:var(--box-shadow-1-x) var(--box-shadow-1-y) var(--box-shadow-1-blur) var(--box-shadow-1-spread) var(--box-shadow-1-color) , var(--box-shadow-2-x) var(--box-shadow-2-y) var(--box-shadow-2-blur) var(--box-shadow-2-spread) var(--box-shadow-2-color) , var(--box-shadow-3-x) var(--box-shadow-3-y) var(--box-shadow-3-blur) var(--box-shadow-3-spread) var(--box-shadow-3-color)"`,
`"box-shadow:var(--box-shadow-1-x) var(--box-shadow-1-y) var(--box-shadow-1-blur) var(--box-shadow-1-spread) var(--box-shadow-1-color) , var(--box-shadow-2-x) var(--box-shadow-2-y) var(--box-shadow-2-blur) var(--box-shadow-2-spread) var(--box-shadow-2-color) , var(--box-shadow-3-x) var(--box-shadow-3-y) var(--box-shadow-3-blur) var(--box-shadow-3-spread) var(--box-shadow-3-color);"`,
);
});
});
Expand Down
4 changes: 2 additions & 2 deletions libs/css/src/property/text-shadow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ if (import.meta.vitest) {
}),
);
const stringified = text_shadow.toString();
const expected = "text-shadow:0px 0px 0px transparent";
const expected = "text-shadow:0px 0px 0px transparent;";
expect(stringified).toBe(expected);
expectTypeOf(stringified).toEqualTypeOf<typeof expected>();
});
Expand All @@ -271,7 +271,7 @@ if (import.meta.vitest) {
const text_shadow = new TextShadow(layer1, layer2, layer3);
const stringified = text_shadow.toString();
const expected =
"text-shadow:0px 0px 0px transparent , 2 2 0px transparent , 3px 3px 0px oklch(0% 0% 0deg / 100%)";
"text-shadow:0px 0px 0px transparent , 2 2 0px transparent , 3px 3px 0px oklch(0% 0% 0deg / 100%);";
expect(stringified).toBe(expected);
expectTypeOf(stringified).toEqualTypeOf<typeof expected>();
});
Expand Down
14 changes: 10 additions & 4 deletions libs/css/src/ruleset.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import type { Display, ToString } from "@xeho91/lib-type/trait/display";
import type { Rule } from "css-tree";

import type { ToAST } from "#ast";
import type { Block } from "#block";
import { RulesetsList } from "#rulesets-list";
import type { SelectorsList } from "#selector/list";

export class Ruleset<const TSelectors extends SelectorsList = SelectorsList, const TBlock extends Block = Block>
implements Display
implements Display, ToAST
{
public selectors: TSelectors;
public block: TBlock;
Expand All @@ -19,8 +20,13 @@ export class Ruleset<const TSelectors extends SelectorsList = SelectorsList, con
return `${this.selectors}${this.block}` as Stringified<TSelectors, TBlock>;
}

public to_list(): RulesetsList<[typeof this]> {
return new RulesetsList(this);
public to_ast(): Rule {
const { block, selectors } = this;
return {
prelude: selectors.to_ast(),
type: "Rule",
block: block.to_ast(),
};
}
}

Expand Down
34 changes: 0 additions & 34 deletions libs/css/src/rulesets-list.ts

This file was deleted.

18 changes: 4 additions & 14 deletions libs/css/src/selector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { readonly_set } from "@xeho91/lib-snippet/set";
import type { IterableElement } from "@xeho91/lib-type/iterable";
import type { Display } from "@xeho91/lib-type/trait/display";

import type { Identifier } from "#identifier";
import { SelectorAttribute } from "#selector/attribute";
import type { SelectorBase } from "#selector/base";
import { SelectorClass } from "#selector/class";
Expand All @@ -10,31 +11,22 @@ import { SelectorsJoint } from "#selector/joint";
import { type PseudoClassName, SelectorPseudoClass } from "#selector/pseudo-class";
import { type PseudoElementName, SelectorPseudoElement } from "#selector/pseudo-element";
import { type HTMLTag, SelectorType } from "#selector/type";
import { SelectorUniversal } from "#selector/universal";

export type SelectorKind = IterableElement<typeof Selector.KINDS>;

// biome-ignore lint/complexity/noStaticOnlyClass: FIXME: What's the alternative then, to have statics?
export class Selector implements Display {
public static readonly KINDS = readonly_set([
"attribute",
"class",
"id",
"pseudo-class",
"pseudo-element",
"type",
"universal",
]);
public static readonly KINDS = readonly_set(["attribute", "class", "id", "pseudo-class", "pseudo-element", "type"]);

public static [Symbol.iterator](): IterableIterator<SelectorKind> {
return Selector.KINDS[Symbol.iterator]();
}

public static attribute = <
TAttribute extends string,
TName extends Identifier,
const TData extends ConstructorParameters<typeof SelectorAttribute>[1],
>(
attribute: TAttribute,
attribute: TName,
data: TData,
) => new SelectorAttribute(attribute, data);
public static class = <TName extends string>(name: TName) => new SelectorClass(name);
Expand All @@ -48,8 +40,6 @@ export class Selector implements Display {
element: <TName extends PseudoElementName>(name: TName) => new SelectorPseudoElement(name),
} as const;

public static readonly universal = new SelectorUniversal();

public static joint = <const TSelectors extends SelectorBase[]>(selectors: TSelectors) =>
new SelectorsJoint(...selectors);
}
83 changes: 50 additions & 33 deletions libs/css/src/selector/attribute.ts
Original file line number Diff line number Diff line change
@@ -1,60 +1,77 @@
import type { Display } from "@xeho91/lib-type/trait/display";
import type { Display, ToString } from "@xeho91/lib-type/trait/display";
import type { AttributeSelector } from "css-tree";

import type { Join } from "@xeho91/lib-type/array";
import type { Identifier } from "#identifier";
import { SelectorBase } from "#selector/base";
import type { StringCSS } from "#value/string";

type Value = string | number | boolean;
// TODO: Idea: could create map with aliases for these operators
export type AttributeOperator = "=" | "~=" | "|=" | "^=" | "$=" | "*=";
export type AttributeMatcher = "=" | "~=" | "|=" | "^=" | "$=" | "*=";
// TODO: Idea: could create map with aliases for these modifiers
export type AttributeModifier = "i" | "s";
export type AttributeFlags = "i" | "s";

export class SelectorAttribute<
TName extends string = string,
TOperator extends AttributeOperator = "=",
TValue extends Value | undefined = undefined,
TModifier extends AttributeModifier | undefined = undefined,
TName extends Identifier = Identifier,
TMatcher extends AttributeMatcher | undefined = undefined,
TValue extends Identifier | StringCSS | undefined = undefined,
TFlags extends AttributeFlags[] | undefined = undefined,
>
extends SelectorBase<"attribute">
implements Display
{
public name: string;
public operator: TOperator;
public name: TName;
public matcher: TMatcher | undefined;
public value: TValue | undefined;
public modifier: TModifier | undefined;
public flags: TFlags | undefined;

constructor(name: TName, data: { operator?: TOperator; value?: TValue; modifier?: TModifier } = {}) {
constructor(name: TName, data: { matcher?: TMatcher; value?: TValue; flags?: TFlags } = {}) {
super("attribute");
const { operator = "=", value, modifier } = data;
const { matcher, value, flags } = data;
this.name = name;
this.operator = operator as TOperator;
this.matcher = matcher;
this.value = value;
this.modifier = modifier;
this.flags = flags;
}

public override toString(): Stringified<TName, TOperator, TValue, TModifier> {
const { name, operator, value, modifier } = this;
let content = `${name}`;
public override toString(): Stringified<TName, TMatcher, TValue, TFlags> {
const { name, matcher, value, flags } = this;
let results = name.toString();
if (value) {
content += operator;
content += `"${value}"`;
results += matcher;
results += `"${value}"`;
}
if (modifier) {
content += " ";
content += modifier;
if (flags) {
results += " ";
results += flags.join("");
}
return `[${content}]` as Stringified<TName, TOperator, TValue, TModifier>;
return `[${results}]` as Stringified<TName, TMatcher, TValue, TFlags>;
}

public override to_ast(): AttributeSelector {
const { name, matcher, value, flags } = this;
return {
type: "AttributeSelector",
name: name.to_ast(),
matcher: matcher ?? null,
value: value?.to_ast() ?? null,
flags: flags?.join("") ?? null,
};
}
}

type Stringified<
TName extends string,
TOperator extends AttributeOperator,
TValue extends Value | undefined,
TModifier extends AttributeModifier | undefined,
TName extends Identifier,
TMatcher extends AttributeMatcher | undefined,
TValue extends Identifier | StringCSS | undefined,
TFlags extends AttributeFlags[] | undefined,
> = TValue extends Value
? TModifier extends AttributeModifier
? `[${TName}${TOperator}"${TValue}" ${TModifier}]`
: `[${TName}${TOperator}"${TValue}"]`
: TModifier extends AttributeModifier
? `[${TName} ${TModifier}]`
: `[${TName}]`;
? TFlags extends AttributeFlags[]
? `[${ToString<TName>}${TMatcher}"${TValue}" ${JoinedFlags<TFlags>}]`
: `[${ToString<TName>}${TMatcher}"${TValue}"]`
: TFlags extends AttributeFlags[]
? `[${ToString<TName>} ${JoinedFlags<TFlags>}]`
: `[${ToString<TName>}]`;

type JoinedFlags<TFlags extends AttributeFlags[]> = Join<TFlags, "">;
6 changes: 5 additions & 1 deletion libs/css/src/selector/base.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import type { Display } from "@xeho91/lib-type/trait/display";
import type { CssNode } from "css-tree";

import type { ToAST } from "#ast";
import type { SelectorKind } from "#selector";
import { SelectorsJoint } from "#selector/joint";
import { SelectorsList } from "#selector/list";

export abstract class SelectorBase<TKind extends SelectorKind = SelectorKind> implements Display {
export abstract class SelectorBase<TKind extends SelectorKind = SelectorKind> implements Display, ToAST {
public readonly kind: TKind;

protected constructor(type: TKind) {
Expand All @@ -13,6 +15,8 @@ export abstract class SelectorBase<TKind extends SelectorKind = SelectorKind> im

public abstract toString(): string;

public abstract to_ast(): CssNode;

public to_joint(): SelectorsJoint<[typeof this]> {
return new SelectorsJoint(this);
}
Expand Down
Loading