
objectos.way.CssConfig Maven / Gradle / Ivy
/*
* Copyright (C) 2023-2024 Objectos Software LTDA.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package objectos.way;
import java.nio.file.Path;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
final class CssConfig {
private Note.Sink noteSink = Note.NoOpSink.INSTANCE;
private List breakpoints = List.of(
new Css.Breakpoint(0, "sm", "640px"),
new Css.Breakpoint(1, "md", "768px"),
new Css.Breakpoint(2, "lg", "1024px"),
new Css.Breakpoint(3, "xl", "1280px"),
new Css.Breakpoint(4, "2xl", "1536px")
);
private List baseLayer;
private final Set> classes;
private Map components;
private Set directories;
private final Map extensions = new EnumMap<>(Css.Key.class);
private final Map overrides = new EnumMap<>(Css.Key.class);
private final Map> prefixes = new HashMap<>();
private Css.PropertyType propertyType = Css.PHYSICAL;
private final Map resolvers = new EnumMap<>(Css.Key.class);
boolean skipDefaultRootRule;
boolean skipReset;
private final Map staticUtilities = Util.createMap();
private Map variants;
private boolean variantsInitialized;
public CssConfig() {
classes = Util.createSet();
}
public final void addComponent(String name, String definition) {
if (components == null) {
components = Util.createMap();
}
String existing;
existing = components.put(name, definition);
if (existing != null) {
throw new IllegalArgumentException(
"The class name " + name + " is mapped to an existing component."
);
}
}
public final void addDirectory(Path directory) {
if (directories == null) {
directories = Util.createSet();
}
directories.add(directory);
}
public final void addUtility(String className, CssProperties properties) {
Css.StaticUtility utility;
utility = new Css.StaticUtility(Css.Key.CUSTOM, properties);
Css.StaticUtility maybeExisting;
maybeExisting = staticUtilities.put(className, utility);
if (maybeExisting != null) {
throw new IllegalArgumentException(
"The class name " + className + " is mapped to an existing utility."
);
}
}
public final void addVariants(CssProperties props) {
if (variants == null) {
variants = Util.createMap();
}
for (Map.Entry entry : props) {
String variantName;
variantName = entry.getKey();
String formatString;
formatString = entry.getValue();
Css.Variant variant;
variant = Css.parseVariant(formatString);
putVariant(variantName, variant);
}
}
public final void baseLayer(String contents) {
if (baseLayer == null) {
baseLayer = Util.createList();
}
baseLayer.add(contents);
}
public final void breakpoints(CssProperties properties) {
int index;
index = 0;
List builder;
builder = Util.createList();
for (var entry : properties) {
String name;
name = entry.getKey();
String value;
value = entry.getValue();
Css.Breakpoint breakpoint;
breakpoint = new Css.Breakpoint(index++, name, value);
builder.add(breakpoint);
}
breakpoints = Util.toUnmodifiableList(builder);
}
public final void classes(Set> set) {
classes.addAll(set);
}
public final void noteSink(Note.Sink noteSink) {
this.noteSink = noteSink;
}
public final CssResolver getResolver(Css.Key key) {
return resolvers.get(key);
}
public final Css.StaticUtility getStatic(String value) {
return staticUtilities.get(value);
}
public final void useLogicalProperties() {
propertyType = Css.LOGICAL;
}
final void extend(Css.Key key, CssProperties properties) {
extensions.put(key, properties);
}
final Set getCandidates(String prefix) {
return prefixes.get(prefix);
}
final void override(Css.Key key, CssProperties properties) {
overrides.put(key, properties);
}
//
final List breakpoints() {
return breakpoints;
}
final Iterable baseLayer() {
return baseLayer != null ? baseLayer : List.of();
}
final Iterable> classes() {
return classes != null ? classes : List.of();
}
final Iterable directories() {
return directories != null ? directories : Set.of();
}
final String getComponent(String value) {
return components != null ? components.get(value) : null;
}
final Css.Variant getVariant(String variantName) {
return variants().get(variantName);
}
final Note.Sink noteSink() {
return noteSink;
}
final Css.PropertyType propertyType() {
return propertyType;
}
private Map variants() {
if (variants == null) {
variants = Util.createMap();
}
if (!variantsInitialized) {
for (var breakpoint : breakpoints) {
putVariant(breakpoint.name(), breakpoint);
}
putVariant("focus", new Css.ClassNameFormat("", ":focus"));
putVariant("hover", new Css.ClassNameFormat("", ":hover"));
putVariant("active", new Css.ClassNameFormat("", ":active"));
putVariant("visited", new Css.ClassNameFormat("", ":visited"));
putVariant("disabled", new Css.ClassNameFormat("", ":disabled"));
putVariant("ltr", new Css.ClassNameFormat("", ":where([dir=\"ltr\"], [dir=\"ltr\"] *)"));
putVariant("rtl", new Css.ClassNameFormat("", ":where([dir=\"rtl\"], [dir=\"rtl\"] *)"));
putVariant("after", new Css.ClassNameFormat("", "::after"));
putVariant("before", new Css.ClassNameFormat("", "::before"));
putVariant("*", new Css.ClassNameFormat("", " > *"));
variantsInitialized = true;
}
return variants;
}
private void putVariant(String name, Css.Variant variant) {
if (variants.containsKey(name)) {
throw new IllegalArgumentException("Variant already defined: " + name);
}
variants.put(name, variant);
}
//
// ValueFormatter implementations
//
private static final CssValueFormatter IDENTITY = new CssValueFormatter() {
@Override
public final String format(String value, boolean negative) { return value; }
};
private static final CssValueFormatter NEGATIVE = new CssValueFormatter() {
@Override
public final String format(String value, boolean negative) { return negative ? "-" + value : value; }
};
private CssValueFormatter ofFunc(Function function) {
return (value, negative) -> function.apply(value);
}
private CssValueFormatter ofFuncNeg(String functionName) {
return (value, negative) -> negative
? functionName + "(-" + value + ")"
: functionName + "(" + value + ")";
}
private static final Pattern REPLACE_COLOR = Pattern.compile(
"(rgb\\([0-9]{1,3} [0-9]{1,3} [0-9]{1,3}(:?\\s*\\/\\s*[0-9\\.]+)?\\)|#[a-fA-F0-9]{3,6}|var\\(.+\\))"
);
static String replaceColor(String raw, String replacement) {
Matcher matcher;
matcher = REPLACE_COLOR.matcher(raw);
return matcher.replaceAll(replacement);
}
//
// SPEC
//
private static final Set INTEGER = EnumSet.of(
CssValueType.ZERO,
CssValueType.INTEGER,
CssValueType.KEYWORD
);
private static final Set KEYWORD = EnumSet.of(
CssValueType.KEYWORD
);
private static final Set LENGTH = EnumSet.of(
CssValueType.LENGTH_PX,
CssValueType.DIMENSION,
CssValueType.KEYWORD
);
private static final Set L_OR_P = EnumSet.of(
CssValueType.LENGTH_PX,
CssValueType.DIMENSION,
CssValueType.PERCENTAGE,
CssValueType.KEYWORD
);
private static final Set STRING = EnumSet.of(
CssValueType.STRING
);
private Map colors() {
return values(Css.Key._COLORS, Css.DEFAULT_COLORS);
}
final void spec() {
// be mindful of method size
var colors = values(Css.Key._COLORS, Css.DEFAULT_COLORS);
var spacing = values(Css.Key._SPACING, Css.DEFAULT_SPACING);
var inset = values(
Css.Key.INSET,
"""
auto: auto
1/2: 50%
1/3: 33.333333%
2/3: 66.666667%
1/4: 25%
2/4: 50%
3/4: 75%
full: 100%
"""
);
// A
specA();
// B
specB(spacing, inset);
// C
staticUtility(
Css.Key.CLEAR,
propertyType == Css.PHYSICAL
? """
clear-left | clear: left
clear-right | clear: right
clear-both | clear: both
clear-none | clear: none
"""
: """
clear-left | clear: inline-start
clear-right | clear: inline-end
clear-both | clear: both
clear-none | clear: none
"""
);
StringBuilder containerDef;
containerDef = new StringBuilder();
containerDef.append("w-full");
for (var breakpoint : breakpoints()) {
containerDef.append(" ");
String name;
name = breakpoint.name();
containerDef.append(name);
containerDef.append(':');
containerDef.append("max-w-screen-");
containerDef.append(breakpoint.name());
}
addComponent("container", containerDef.toString());
funcUtility(
Css.Key.CONTENT,
values(
Css.Key.CONTENT,
"""
none: none
"""
),
STRING,
"content"
);
funcUtility(
Css.Key.CURSOR,
values(
Css.Key.CURSOR,
"""
auto: auto
default: default
pointer: pointer
wait: wait
text: text
move: move
help: help
not-allowed: not-allowed
none: none
context-menu: context-menu
progress: progress
cell: cell
crosshair: crosshair
vertical-text: vertical-text
alias: alias
copy: copy
no-drop: no-drop
grab: grab
grabbing: grabbing
all-scroll: all-scroll
col-resize: col-resize
row-resize: row-resize
n-resize: n-resize
e-resize: e-resize
s-resize: s-resize
w-resize: w-resize
ne-resize: ne-resize
nw-resize: nw-resize
se-resize: se-resize
sw-resize: sw-resize
ew-resize: ew-resize
ns-resize: ns-resize
nesw-resize: nesw-resize
nwse-resize: nwse-resize
zoom-in: zoom-in
zoom-out: zoom-out
"""
),
"cursor"
);
// D
staticUtility(
Css.Key.DISPLAY,
"""
block | display: block
inline-block | display: inline-block
inline | display: inline
flex | display: flex
inline-flex | display: inline-flex
table | display: table
inline-table | display: inline-table
table-caption | display: table-caption
table-cell | display: table-cell
table-column | display: table-column
table-column-group | display: table-column-group
table-footer-group | display: table-footer-group
table-header-group | display: table-header-group
table-row-group | display: table-row-group
table-row | display: table-row
flow-root | display: flow-root
grid | display: grid
inline-grid | display: inline-grid
contents | display: contents
list-item | display: list-item
hidden | display: none
"""
);
// F
specF();
// G
specG();
// H
specH();
// I
funcUtility(Css.Key.INSET, inset, NEGATIVE, L_OR_P, "inset", "inset");
funcUtility(Css.Key.INSET_X, inset, NEGATIVE, L_OR_P, "inset-x", propertyType.left(), propertyType.right());
funcUtility(Css.Key.INSET_Y, inset, NEGATIVE, L_OR_P, "inset-y", propertyType.top(), propertyType.bottom());
// J
staticUtility(
Css.Key.JUSTIFY_CONTENT,
"""
justify-normal | justify-content: normal
justify-start | justify-content: flex-start
justify-end | justify-content: flex-end
justify-center | justify-content: center
justify-between | justify-content: space-between
justify-around | justify-content: space-around
justify-evenly | justify-content: space-evenly
justify-stretch | justify-content: stretch
"""
);
// L
specL(inset);
// M
specM();
// O
funcUtility(
Css.Key.OPACITY,
values(
Css.Key.OPACITY,
"""
0: 0
5: 0.05
10: 0.1
15: 0.15
20: 0.2
25: 0.25
30: 0.3
35: 0.35
40: 0.4
45: 0.45
50: 0.5
55: 0.55
60: 0.6
65: 0.65
70: 0.7
75: 0.75
80: 0.8
85: 0.85
90: 0.9
95: 0.95
100: 1
"""
),
"opacity"
);
colorUtility(
Css.Key.OUTLINE_COLOR,
values(
Css.Key.OUTLINE_COLOR,
colors
),
"outline", "outline-color"
);
funcUtility(
Css.Key.OUTLINE_OFFSET,
values(
Css.Key.OUTLINE_OFFSET,
"""
0: 0px
1: 1px
2: 2px
4: 4px
8: 8px
"""
),
NEGATIVE,
"outline-offset"
);
staticUtility(
Css.Key.OUTLINE_STYLE,
"""
outline-none | outline: 2px solid transparent
| outline-offset: 2px
outline | outline-style: solid
outline-dashed | outline-style: dashed
outline-dotted | outline-style: dotted
outline-double | outline-style: double
"""
);
funcUtility(
Css.Key.OUTLINE_WIDTH,
values(
Css.Key.OUTLINE_WIDTH,
"""
0: 0px
1: 1px
2: 2px
4: 4px
8: 8px
"""
),
NEGATIVE,
"outline", "outline-width"
);
staticUtility(
Css.Key.OVERFLOW,
"""
overflow-auto | overflow: auto
overflow-hidden | overflow: hidden
overflow-clip | overflow: clip
overflow-visible | overflow: visible
overflow-scroll | overflow: scroll
"""
);
staticUtility(
Css.Key.OVERFLOW_X,
"""
overflow-x-auto | overflow-x: auto
overflow-x-hidden | overflow-x: hidden
overflow-x-clip | overflow-x: clip
overflow-x-visible | overflow-x: visible
overflow-x-scroll | overflow-x: scroll
"""
);
staticUtility(
Css.Key.OVERFLOW_Y,
"""
overflow-y-auto | overflow-y: auto
overflow-y-hidden | overflow-y: hidden
overflow-y-clip | overflow-y: clip
overflow-y-visible | overflow-y: visible
overflow-y-scroll | overflow-y: scroll
"""
);
// P
specP();
// R
specR(colors, inset);
// S
specS(colors);
// T
specT(colors, spacing, inset);
// U
staticUtility(
Css.Key.USER_SELECT,
"""
select-none | user-select: none
select-text | user-select: text
select-all | user-select: all
select-auto | user-select: auto
"""
);
// V
staticUtility(
Css.Key.VERTICAL_ALIGN,
"""
align-baseline | vertical-align: baseline
align-top | vertical-align: top
align-middle | vertical-align: middle
align-bottom | vertical-align: bottom
align-text-top | vertical-align: text-top
align-text-bottom | vertical-align: text-bottom
align-sub | vertical-align: sub
align-super | vertical-align: super
"""
);
staticUtility(
Css.Key.VISIBILITY,
"""
visible | visibility: visible
invisible | visibility: hidden
collapse | visibility: collapse
"""
);
// W
staticUtility(
Css.Key.WHITE_SPACE,
"""
whitespace-normal | white-space: normal
whitespace-nowrap | white-space: nowrap
whitespace-pre | white-space: pre
whitespace-pre-line | white-space: pre-line
whitespace-pre-wrap | white-space: pre-wrap
whitespace-break-spaces | white-space: break-spaces
"""
);
funcUtility(
Css.Key.WIDTH,
values(
Css.Key.WIDTH,
() -> Css.merge(
"""
auto: auto
1/2: 50%
1/3: 33.333333%
2/3: 66.666667%
1/4: 25%
2/4: 50%
3/4: 75%
1/5: 20%
2/5: 40%
3/5: 60%
4/5: 80%
1/6: 16.666667%
2/6: 33.333333%
3/6: 50%
4/6: 66.666667%
5/6: 83.333333%
1/12: 8.333333%
2/12: 16.666667%
3/12: 25%
4/12: 33.333333%
5/12: 41.666667%
6/12: 50%
7/12: 58.333333%
8/12: 66.666667%
9/12: 75%
10/12: 83.333333%
11/12: 91.666667%
full: 100%
screen: 100vw
svw: 100svw
lvw: 100lvw
dvw: 100dvw
min: min-content
max: max-content
fit: fit-content
""",
spacing
)
),
L_OR_P,
"w",
propertyType.width()
);
// Z
specZ();
}
private void specA() {
staticUtility(
Css.Key.ACCESSIBILITY,
"""
sr-only | position: absolute
| width: 1px
| height: 1px
| padding: 0
| margin: -1px
| overflow: hidden
| clip: rect(0, 0, 0, 0)
| white-space: nowrap
| border-width: 0
not-sr-only | position: static
| width: auto
| height: auto
| padding: 0
| margin: 0
| overflow: visible
| clip: auto
| white-space: normal
"""
);
funcUtility(
Css.Key.ALIGN_CONTENT,
"align-content",
KEYWORD
);
funcUtility(
Css.Key.ALIGN_ITEMS,
"align-items",
KEYWORD
);
funcUtility(
Css.Key.ALIGN_SELF,
"align-self",
KEYWORD
);
funcUtility(
Css.Key.APPEARANCE,
"appearance",
KEYWORD
);
funcUtility(
Css.Key.ASPECT_RATIO,
"aspect-ratio",
EnumSet.of(
CssValueType.INTEGER,
CssValueType.DECIMAL,
CssValueType.RATIO,
CssValueType.KEYWORD
)
);
}
private void specB(Map spacing, Map inset) {
var colors = colors();
colorUtility(
Css.Key.BACKGROUND_COLOR,
values(Css.Key.BACKGROUND_COLOR, colors),
"bg", "background-color"
);
staticUtility(
Css.Key.BORDER_COLLAPSE,
"""
border-collapse | border-collapse: collapse
border-separate | border-collapse: separate
"""
);
var borderColor = values(Css.Key.BORDER_COLOR, colors);
colorUtility(Css.Key.BORDER_COLOR, borderColor, "border", "border-color");
colorUtility(Css.Key.BORDER_COLOR_TOP, borderColor, "border-t", propertyType.borderColorTop());
colorUtility(Css.Key.BORDER_COLOR_RIGHT, borderColor, "border-r", propertyType.borderColorRight());
colorUtility(Css.Key.BORDER_COLOR_BOTTOM, borderColor, "border-b", propertyType.borderColorBottom());
colorUtility(Css.Key.BORDER_COLOR_LEFT, borderColor, "border-l", propertyType.borderColorLeft());
colorUtility(Css.Key.BORDER_COLOR_X, borderColor, "border-x", propertyType.borderColorLeft(), propertyType.borderColorRight());
colorUtility(Css.Key.BORDER_COLOR_Y, borderColor, "border-y", propertyType.borderColorTop(), propertyType.borderColorBottom());
var rounded = values(
Css.Key.BORDER_RADIUS,
"""
none: 0px
sm: 0.125rem
: 0.25rem
md: 0.375rem
lg: 0.5rem
xl: 0.75rem
2xl: 1rem
3xl: 1.5rem
full: 9999px
"""
);
funcUtility(Css.Key.BORDER_RADIUS, rounded, "rounded", "border-radius");
funcUtility(Css.Key.BORDER_RADIUS_TL, rounded, "rounded-tl", propertyType.borderRadiusTopLeft());
funcUtility(Css.Key.BORDER_RADIUS_TR, rounded, "rounded-tr", propertyType.borderRadiusTopRight());
funcUtility(Css.Key.BORDER_RADIUS_BR, rounded, "rounded-br", propertyType.borderRadiusBottomRight());
funcUtility(Css.Key.BORDER_RADIUS_BL, rounded, "rounded-bl", propertyType.borderRadiusBottomLeft());
funcUtility(Css.Key.BORDER_RADIUS_T, rounded, "rounded-t", propertyType.borderRadiusTopLeft(), propertyType.borderRadiusTopRight());
funcUtility(Css.Key.BORDER_RADIUS_R, rounded, "rounded-r", propertyType.borderRadiusTopRight(), propertyType.borderRadiusBottomRight());
funcUtility(Css.Key.BORDER_RADIUS_B, rounded, "rounded-b", propertyType.borderRadiusBottomRight(), propertyType.borderRadiusBottomLeft());
funcUtility(Css.Key.BORDER_RADIUS_L, rounded, "rounded-l", propertyType.borderRadiusBottomLeft(), propertyType.borderRadiusTopLeft());
var borderSpacing = values(Css.Key.BORDER_SPACING, spacing);
funcUtility(Css.Key.BORDER_SPACING, borderSpacing, ofFunc(s -> s + " " + s), "border-spacing", "border-spacing");
funcUtility(Css.Key.BORDER_SPACING_X, borderSpacing, ofFunc(s -> s + " 0"), "border-spacing-x", "border-spacing");
funcUtility(Css.Key.BORDER_SPACING_Y, borderSpacing, ofFunc(s -> "0 " + s), "border-spacing-y", "border-spacing");
var borderWidth = values(
Css.Key.BORDER_WIDTH,
"""
: 1px
0: 0px
2: 2px
4: 4px
8: 8px
"""
);
staticUtility(
Css.Key.BORDER_STYLE,
"""
border-solid | border-style: solid
border-dashed | border-style: dashed
border-dotted | border-style: dotted
border-double | border-style: double
border-hidden | border-style: hidden
border-none | border-style: none
"""
);
funcUtility(Css.Key.BORDER_WIDTH, borderWidth, "border", "border-width");
funcUtility(Css.Key.BORDER_WIDTH_TOP, borderWidth, "border-t", propertyType.borderWidthTop());
funcUtility(Css.Key.BORDER_WIDTH_RIGHT, borderWidth, "border-r", propertyType.borderWidthRight());
funcUtility(Css.Key.BORDER_WIDTH_BOTTOM, borderWidth, "border-b", propertyType.borderWidthBottom());
funcUtility(Css.Key.BORDER_WIDTH_LEFT, borderWidth, "border-l", propertyType.borderWidthLeft());
funcUtility(Css.Key.BORDER_WIDTH_X, borderWidth, "border-x", propertyType.borderWidthLeft(), propertyType.borderWidthRight());
funcUtility(Css.Key.BORDER_WIDTH_Y, borderWidth, "border-y", propertyType.borderWidthTop(), propertyType.borderWidthBottom());
funcUtility(Css.Key.BOTTOM, inset, NEGATIVE, L_OR_P, "bottom", propertyType.bottom());
customUtility(
Css.Key.BOX_SHADOW,
"shadow",
new CssResolverOfBoxShadow(
values(
Css.Key.BOX_SHADOW,
"""
sm: 0 1px 2px 0 rgb(0 0 0 / 0.05)
: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)
md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)
lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)
xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)
2xl: 0 25px 50px -12px rgb(0 0 0 / 0.25)
inner: inset 0 2px 4px 0 rgb(0 0 0 / 0.05)
none: none
"""
)
)
);
customUtility(
Css.Key.BOX_SHADOW_COLOR,
"shadow",
new CssResolverOfBoxShadowColor(
values(Css.Key.BOX_SHADOW_COLOR, colors)
)
);
}
private void specF() {
var colors = values(Css.Key._COLORS, Css.DEFAULT_COLORS);
funcUtility(
Css.Key.FILL,
values(
Css.Key.FILL,
() -> Css.merge(
"""
none: none
""",
colors
)
),
"fill"
);
funcUtility(
Css.Key.FLEX,
values(
Css.Key.FLEX,
"""
1: 1 1 0%
auto: 1 1 auto
initial: 0 1 auto
none: none
"""
),
STRING,
"flex"
);
funcUtility(
Css.Key.FLEX_BASIS,
values(
Css.Key.FLEX_BASIS,
"""
auto: auto
1/2: 50%
1/3: 33.333333%
2/3: 66.666667%
1/4: 25%
2/4: 50%
3/4: 75%
1/5: 20%
2/5: 40%
3/5: 60%
4/5: 80%
1/6: 16.666667%
2/6: 33.333333%
3/6: 50%
4/6: 66.666667%
5/6: 83.333333%
1/12: 8.333333%
2/12: 16.666667%
3/12: 25%
4/12: 33.333333%
5/12: 41.666667%
6/12: 50%
7/12: 58.333333%
8/12: 66.666667%
9/12: 75%
10/12: 83.333333%
11/12: 91.666667%
full: 100%
"""
),
L_OR_P,
"basis", "flex-basis"
);
staticUtility(
Css.Key.FLEX_DIRECTION,
"""
flex-row | flex-direction: row
flex-row-reverse | flex-direction: row-reverse
flex-col | flex-direction: column
flex-col-reverse | flex-direction: column-reverse
"""
);
funcUtility(
Css.Key.FLEX_GROW,
values(
Css.Key.FLEX_GROW,
"""
: 1
0: 0
"""
),
INTEGER,
"grow", "flex-grow"
);
funcUtility(
Css.Key.FLEX_SHRINK,
values(
Css.Key.FLEX_SHRINK,
"""
: 1
0: 0
"""
),
INTEGER,
"shrink", "flex-shrink"
);
staticUtility(
Css.Key.FLEX_WRAP,
"""
flex-wrap | flex-wrap: wrap
flex-wrap-reverse | flex-wrap: wrap-reverse
flex-nowrap | flex-wrap: nowrap
"""
);
staticUtility(
Css.Key.FLOAT,
propertyType == Css.PHYSICAL
? """
float-left | float: left
float-right | float: right
float-none | float: none
"""
: """
float-left | float: inline-start
float-right | float: inline-end
float-none | float: none
"""
);
funcUtility(
Css.Key.FONT_SIZE,
prefixes("text", "font-size"),
"font-size",
L_OR_P,
"""
base: 1rem
"""
);
staticUtility(
Css.Key.FONT_STYLE,
"""
italic | font-style: italic
not-italic | font-style: normal
"""
);
funcUtility(
Css.Key.FONT_WEIGHT,
values(
Css.Key.FONT_WEIGHT,
"""
thin: 100
extralight: 200
light: 300
normal: 400
medium: 500
semibold: 600
bold: 700
extrabold: 800
black: 900
"""
),
"font", "font-weight"
);
}
private void specG() {
var spacing = values(Css.Key._SPACING, Css.DEFAULT_SPACING);
var gap = values(Css.Key.GAP, spacing);
funcUtility(Css.Key.GAP, gap, "gap", "gap");
funcUtility(Css.Key.GAP_X, gap, "gap-x", "column-gap");
funcUtility(Css.Key.GAP_Y, gap, "gap-y", "row-gap");
funcUtility(
Css.Key.GRID_COLUMN,
values(
Css.Key.GRID_COLUMN,
"""
auto: auto
span-1: span 1 / span 1
span-2: span 2 / span 2
span-3: span 3 / span 3
span-4: span 4 / span 4
span-5: span 5 / span 5
span-6: span 6 / span 6
span-7: span 7 / span 7
span-8: span 8 / span 8
span-9: span 9 / span 9
span-10: span 10 / span 10
span-11: span 11 / span 11
span-12: span 12 / span 12
span-full: 1 / -1
"""
),
"col", "grid-column"
);
funcUtility(
Css.Key.GRID_COLUMN_END,
values(
Css.Key.GRID_COLUMN_END,
"""
auto: auto
1: 1
2: 2
3: 3
4: 4
5: 5
6: 6
7: 7
8: 8
9: 9
10: 10
11: 11
12: 12
13: 13
"""
),
"col-end", "grid-column-end"
);
funcUtility(
Css.Key.GRID_COLUMN_START,
values(
Css.Key.GRID_COLUMN_START,
"""
auto: auto
1: 1
2: 2
3: 3
4: 4
5: 5
6: 6
7: 7
8: 8
9: 9
10: 10
11: 11
12: 12
13: 13
"""
),
"col-start", "grid-column-start"
);
funcUtility(
Css.Key.GRID_TEMPLATE_COLUMNS,
values(
Css.Key.GRID_TEMPLATE_COLUMNS,
"""
none: none
subgrid: subgrid
1: repeat(1, minmax(0, 1fr))
2: repeat(2, minmax(0, 1fr))
3: repeat(3, minmax(0, 1fr))
4: repeat(4, minmax(0, 1fr))
5: repeat(5, minmax(0, 1fr))
6: repeat(6, minmax(0, 1fr))
7: repeat(7, minmax(0, 1fr))
8: repeat(8, minmax(0, 1fr))
9: repeat(9, minmax(0, 1fr))
10: repeat(10, minmax(0, 1fr))
11: repeat(11, minmax(0, 1fr))
12: repeat(12, minmax(0, 1fr))
"""
),
STRING,
"grid-cols", "grid-template-columns"
);
funcUtility(
Css.Key.GRID_TEMPLATE_ROWS,
values(
Css.Key.GRID_TEMPLATE_ROWS,
"""
none: none
subgrid: subgrid
1: repeat(1, minmax(0, 1fr))
2: repeat(2, minmax(0, 1fr))
3: repeat(3, minmax(0, 1fr))
4: repeat(4, minmax(0, 1fr))
5: repeat(5, minmax(0, 1fr))
6: repeat(6, minmax(0, 1fr))
7: repeat(7, minmax(0, 1fr))
8: repeat(8, minmax(0, 1fr))
9: repeat(9, minmax(0, 1fr))
10: repeat(10, minmax(0, 1fr))
11: repeat(11, minmax(0, 1fr))
12: repeat(12, minmax(0, 1fr))
"""
),
STRING,
"grid-rows", "grid-template-rows"
);
}
private void specH() {
funcUtility(
Css.Key.HEIGHT,
values(
Css.Key.HEIGHT,
"""
auto: auto
1/2: 50%
1/3: 33.333333%
2/3: 66.666667%
1/4: 25%
2/4: 50%
3/4: 75%
1/5: 20%
2/5: 40%
3/5: 60%
4/5: 80%
1/6: 16.666667%
2/6: 33.333333%
3/6: 50%
4/6: 66.666667%
5/6: 83.333333%
full: 100%
screen: 100vh
svh: 100svh
lvh: 100lvh
dvh: 100dvh
min: min-content
max: max-content
fit: fit-content
"""
),
L_OR_P,
"h",
propertyType.height()
);
}
private void specL(Map inset) {
funcUtility(Css.Key.LEFT, inset, NEGATIVE, L_OR_P, "left", propertyType.left());
funcUtility(
Css.Key.LETTER_SPACING,
values(
Css.Key.LETTER_SPACING,
"""
tighter: -0.05em
tight: -0.025em
normal: 0em
wide: 0.025em
wider: 0.05em
widest: 0.1em
"""
),
NEGATIVE,
"tracking", "letter-spacing"
);
funcUtility(
Css.Key.LINE_HEIGHT,
prefixes("leading", "line-height"),
"line-height",
EnumSet.of(
CssValueType.INTEGER,
CssValueType.DECIMAL,
CssValueType.LENGTH_PX,
CssValueType.DIMENSION,
CssValueType.PERCENTAGE
)
);
funcUtility(
Css.Key.LIST_STYLE_TYPE,
"list-style-type",
KEYWORD
);
}
private void specM() {
var margin = values(
Css.Key.MARGIN,
"""
auto: auto
0: 0px
"""
);
funcUtility(Css.Key.MARGIN, margin, NEGATIVE, L_OR_P, "m", "margin");
funcUtility(Css.Key.MARGIN_TOP, margin, NEGATIVE, L_OR_P, "mt", propertyType.marginTop());
funcUtility(Css.Key.MARGIN_RIGHT, margin, NEGATIVE, L_OR_P, "mr", propertyType.marginRight());
funcUtility(Css.Key.MARGIN_BOTTOM, margin, NEGATIVE, L_OR_P, "mb", propertyType.marginBottom());
funcUtility(Css.Key.MARGIN_LEFT, margin, NEGATIVE, L_OR_P, "ml", propertyType.marginLeft());
funcUtility(Css.Key.MARGIN_X, margin, NEGATIVE, L_OR_P, "mx", propertyType.marginLeft(), propertyType.marginRight());
funcUtility(Css.Key.MARGIN_Y, margin, NEGATIVE, L_OR_P, "my", propertyType.marginTop(), propertyType.marginBottom());
funcUtility(
Css.Key.MAX_HEIGHT,
values(
Css.Key.MAX_HEIGHT,
"""
none: none
full: 100%
screen: 100vh
svh: 100svh
lvh: 100lvh
dvh: 100dvh
min: min-content
max: max-content
fit: fit-content
"""
),
L_OR_P,
"max-h",
propertyType.maxHeight()
);
funcUtility(
Css.Key.MAX_WIDTH,
values(
Css.Key.MAX_WIDTH,
() -> {
Map maxWidth;
maxWidth = Util.createMap();
maxWidth.put("none", "none");
maxWidth.put("xs", "20rem");
maxWidth.put("sm", "24rem");
maxWidth.put("md", "28rem");
maxWidth.put("lg", "32rem");
maxWidth.put("xl", "36rem");
maxWidth.put("2xl", "42rem");
maxWidth.put("3xl", "48rem");
maxWidth.put("4xl", "56rem");
maxWidth.put("5xl", "64rem");
maxWidth.put("6xl", "72rem");
maxWidth.put("7xl", "80rem");
maxWidth.put("full", "100%");
maxWidth.put("min", "min-content");
maxWidth.put("max", "max-content");
maxWidth.put("fit", "fit-content");
maxWidth.put("prose", "65ch");
for (var breakpoint : breakpoints) {
String screen;
screen = "screen-" + breakpoint.name();
maxWidth.put(screen, breakpoint.value());
}
return maxWidth;
}
),
L_OR_P,
"max-w",
propertyType.maxWidth()
);
funcUtility(
Css.Key.MIN_HEIGHT,
values(
Css.Key.MIN_HEIGHT,
"""
full: 100%
screen: 100vh
svh: 100svh
lvh: 100lvh
dvh: 100dvh
min: min-content
max: max-content
fit: fit-content
"""
),
L_OR_P,
"min-h",
propertyType.minHeight()
);
funcUtility(
Css.Key.MIN_WIDTH,
values(
Css.Key.MIN_WIDTH,
"""
full: 100%
min: min-content
max: max-content
fit: fit-content
"""
),
L_OR_P,
"min-w",
propertyType.minWidth()
);
}
private void specP() {
var padding = values(Css.Key.PADDING, Map.of("0", "0px"));
funcUtility(Css.Key.PADDING, padding, L_OR_P, "p", "padding");
funcUtility(Css.Key.PADDING_TOP, padding, L_OR_P, "pt", propertyType.paddingTop());
funcUtility(Css.Key.PADDING_RIGHT, padding, L_OR_P, "pr", propertyType.paddingRight());
funcUtility(Css.Key.PADDING_BOTTOM, padding, L_OR_P, "pb", propertyType.paddingBottom());
funcUtility(Css.Key.PADDING_LEFT, padding, L_OR_P, "pl", propertyType.paddingLeft());
funcUtility(Css.Key.PADDING_X, padding, L_OR_P, "px", propertyType.paddingLeft(), propertyType.paddingRight());
funcUtility(Css.Key.PADDING_Y, padding, L_OR_P, "py", propertyType.paddingTop(), propertyType.paddingBottom());
staticUtility(
Css.Key.POINTER_EVENTS,
"""
pointer-events-auto | pointer-events: auto
pointer-events-none | pointer-events: none
"""
);
staticUtility(
Css.Key.POSITION,
"""
static | position: static
fixed | position: fixed
absolute | position: absolute
relative | position: relative
sticky | position: sticky
"""
);
}
private void specR(Map colors, Map inset) {
funcUtility(Css.Key.RIGHT, inset, NEGATIVE, L_OR_P, "right", propertyType.right());
colorUtility(Css.Key.RING_COLOR, values(Css.Key.RING_COLOR, colors), "ring", "--tw-ring-color");
staticUtility(
Css.Key.RING_INSET,
"""
ring-inset | --tw-ring-inset: inset
"""
);
colorUtility(
Css.Key.RING_OFFSET_COLOR,
values(Css.Key.RING_OFFSET_COLOR, colors),
"ring-offset", "--tw-ring-offset-color"
);
funcUtility(
Css.Key.RING_OFFSET_WIDTH,
values(
Css.Key.RING_OFFSET_WIDTH,
"""
0: 0px
1: 1px
2: 2px
4: 4px
8: 8px
"""
),
LENGTH,
"ring-offset", "--tw-ring-offset-width"
);
customUtility(
Css.Key.RING_WIDTH,
"ring",
new CssResolverOfRingWidth(
values(
Css.Key.RING_WIDTH,
"""
0: 0px
1: 1px
2: 2px
: 3px
4: 4px
8: 8px
"""
)
)
);
}
private void specS(Map colors) {
funcUtility(
Css.Key.SIZE,
values(
Css.Key.SIZE,
"""
auto: auto
1/2: 50%
1/3: 33.333333%
2/3: 66.666667%
1/4: 25%
2/4: 50%
3/4: 75%
1/5: 20%
2/5: 40%
3/5: 60%
4/5: 80%
1/6: 16.666667%
2/6: 33.333333%
3/6: 50%
4/6: 66.666667%
5/6: 83.333333%
1/12: 8.333333%
2/12: 16.666667%
3/12: 25%
4/12: 33.333333%
5/12: 41.666667%
6/12: 50%
7/12: 58.333333%
8/12: 66.666667%
9/12: 75%
10/12: 83.333333%
11/12: 91.666667%
full: 100%
min: min-content
max: max-content
fit: fit-content
"""
),
L_OR_P,
"size",
propertyType.height(),
propertyType.width()
);
colorUtility(
Css.Key.STROKE,
values(
Css.Key.STROKE,
() -> Css.merge(
"""
none: none
""",
colors
)
),
"stroke", "stroke"
);
funcUtility(
Css.Key.STROKE_WIDTH,
values(
Css.Key.STROKE_WIDTH,
"""
0: 0
1: 1
2: 2
"""
),
"stroke", "stroke-width"
);
}
private void specT(Map colors, Map spacing, Map inset) {
staticUtility(
Css.Key.TABLE_LAYOUT,
"""
table-auto | table-layout: auto
table-fixed | table-layout: fixed
"""
);
staticUtility(
Css.Key.TEXT_ALIGN,
"""
text-left | text-align: left
text-center | text-align: center
text-right | text-align: right
text-justify | text-align: justify
text-start | text-align: start
text-end | text-align: end
"""
);
colorUtility(
Css.Key.TEXT_COLOR,
values(
Css.Key.TEXT_COLOR,
colors
),
"text", "color"
);
staticUtility(
Css.Key.TEXT_DECORATION,
"""
underline | text-decoration-line: underline
overline | text-decoration-line: overline
line-through | text-decoration-line: line-through
no-underline | text-decoration-line: none
"""
);
staticUtility(
Css.Key.TEXT_OVERFLOW,
"""
truncate | overflow: hidden
| text-overflow: ellipsis
| white-space: nowrap
text-ellipsis | text-overflow: ellipsis
text-clip | text-overflow: clip
"""
);
staticUtility(
Css.Key.TEXT_WRAP,
"""
text-wrap | text-wrap: wrap
text-nowrap | text-wrap: nowrap
text-balance | text-wrap: balance
text-pretty | text-wrap: pretty
"""
);
funcUtility(Css.Key.TOP, inset, NEGATIVE, L_OR_P, "top", propertyType.top());
staticUtility(
Css.Key.TRANSFORM,
"""
transform-none | transform: none
"""
);
funcUtility(
Css.Key.TRANSITION_DURATION,
values(
Css.Key.TRANSITION_DURATION,
"""
0: 0s
75: 75ms
100: 100ms
150: 150ms
200: 200ms
300: 300ms
500: 500ms
700: 700ms
1000: 1000ms
"""
),
"duration", "transition-duration"
);
customUtility(
Css.Key.TRANSITION_PROPERTY,
"transition",
new CssResolverOfTransitionProperty(
values(
Css.Key.TRANSITION_PROPERTY,
"""
none: none
all: all/cubic-bezier(0.4, 0, 0.2, 1)/150ms
: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter/cubic-bezier(0.4, 0, 0.2, 1)/150ms
colors: color, background-color, border-color, text-decoration-color, fill, stroke/cubic-bezier(0.4, 0, 0.2, 1)/150ms
opacity: opacity/cubic-bezier(0.4, 0, 0.2, 1)/150ms
shadow: box-shadow/cubic-bezier(0.4, 0, 0.2, 1)/150ms
transform: transform/cubic-bezier(0.4, 0, 0.2, 1)/150ms
"""
)
)
);
var translate = values(Css.Key.TRANSLATE, () -> Css.merge(
"""
1/2: 50%
1/3: 33.333333%
2/3: 66.666667%
1/4: 25%
2/4: 50%
3/4: 75%
full: 100%
""",
spacing
));
funcUtility(Css.Key.TRANSLATE_X, translate, ofFuncNeg("translateX"), "translate-x", "transform");
funcUtility(Css.Key.TRANSLATE_Y, translate, ofFuncNeg("translateY"), "translate-y", "transform");
}
private void specZ() {
funcUtility(
Css.Key.Z_INDEX,
values(
Css.Key.Z_INDEX,
"""
auto: auto
"""
),
NEGATIVE,
INTEGER,
"z", "z-index"
);
}
private void colorUtility(Css.Key key, Map values, String prefix, String propertyName) {
colorUtility(key, values, prefix, propertyName, null);
}
private void colorUtility(
Css.Key key,
Map values,
String prefix, String propertyName1, String propertyName2) {
CssResolver resolver;
resolver = new CssResolverOfColorAlpha(key, values, propertyName1, propertyName2);
customUtility(key, prefix, resolver);
}
private void customUtility(Css.Key key, String prefix, CssResolver resolver) {
prefix(key, prefix);
resolver(key, resolver);
}
private void resolver(Css.Key key, CssResolver resolver) {
CssResolver maybeExisting;
maybeExisting = resolvers.put(key, resolver);
if (maybeExisting != null) {
throw new IllegalArgumentException(
"Key " + key + " already mapped to " + maybeExisting
);
}
}
private void prefix(Css.Key key, String prefix) {
Set set;
set = prefixes.computeIfAbsent(prefix, s -> EnumSet.noneOf(Css.Key.class));
set.add(key);
}
private void prefixes(Css.Key key, String[] prefixes) {
for (String prefix : prefixes) {
prefix(key, prefix);
}
}
// new funcUtility
private void funcUtility(
Css.Key key,
String propertyName,
Set supportedTypes) {
prefix(key, propertyName);
Map values;
values = Map.of();
CssResolver resolver;
resolver = new CssResolverOfProperties(key, values, IDENTITY, supportedTypes, propertyName, null);
resolver(key, resolver);
}
private void funcUtility(
Css.Key key,
String[] prefixes,
String propertyName,
Set supportedTypes
) {
prefixes(key, prefixes);
Map values;
values = Map.of();
CssResolver resolver;
resolver = new CssResolverOfProperties(key, values, IDENTITY, supportedTypes, propertyName, null);
resolver(key, resolver);
}
private void funcUtility(
Css.Key key,
String[] prefixes,
String propertyName,
Set supportedTypes,
String defaultValues
) {
prefixes(key, prefixes);
Map values;
values = values(key, defaultValues);
CssResolver resolver;
resolver = new CssResolverOfProperties(key, values, IDENTITY, supportedTypes, propertyName, null);
resolver(key, resolver);
}
private String[] prefixes(String... values) {
// yeah... just don't mutate the array...
return values;
}
// current funcUtility
private void funcUtility(
Css.Key key,
Map values,
String prefix) {
funcUtility(key, values, IDENTITY, prefix, prefix, null);
}
private void funcUtility(
Css.Key key,
Map values, Set types,
String prefix) {
funcUtility(key, values, IDENTITY, types, prefix, prefix, null);
}
private void funcUtility(
Css.Key key,
Map values,
String prefix, String propertyName) {
funcUtility(key, values, IDENTITY, prefix, propertyName, null);
}
private void funcUtility(
Css.Key key,
Map values,
String prefix, String propertyName1, String propertyName2) {
funcUtility(key, values, IDENTITY, prefix, propertyName1, propertyName2);
}
private void funcUtility(
Css.Key key,
Map values, Set types,
String prefix, String propertyName) {
funcUtility(key, values, IDENTITY, types, prefix, propertyName, null);
}
private void funcUtility(
Css.Key key,
Map values, Set types,
String prefix, String propertyName1, String propertyName2) {
funcUtility(key, values, IDENTITY, types, prefix, propertyName1, propertyName2);
}
private void funcUtility(
Css.Key key,
Map values, CssValueFormatter formatter,
String prefix) {
funcUtility(key, values, formatter, prefix, prefix, null);
}
private void funcUtility(
Css.Key key,
Map values, CssValueFormatter formatter,
String prefix, String propertyName) {
funcUtility(key, values, formatter, prefix, propertyName, null);
}
private void funcUtility(
Css.Key key,
Map values, CssValueFormatter formatter,
String prefix, String propertyName1, String propertyName2) {
CssResolver resolver;
resolver = new CssResolverOfProperties(key, values, formatter, propertyName1, propertyName2);
customUtility(key, prefix, resolver);
}
private void funcUtility(
Css.Key key,
Map values, CssValueFormatter formatter, Set types,
String prefix, String propertyName) {
funcUtility(key, values, formatter, types, prefix, propertyName, null);
}
private void funcUtility(
Css.Key key,
Map values, CssValueFormatter formatter, Set types,
String prefix, String propertyName1, String propertyName2) {
CssResolver resolver;
resolver = new CssResolverOfProperties(key, values, formatter, types, propertyName1, propertyName2);
customUtility(key, prefix, resolver);
}
// visible for testing
final void staticUtility(Css.Key key, String text) {
Map table;
table = Css.parseTable(text);
for (Map.Entry entry : table.entrySet()) {
String className;
className = entry.getKey();
CssProperties properties;
properties = entry.getValue();
Css.StaticUtility utility;
utility = new Css.StaticUtility(key, properties);
Css.StaticUtility maybeExisting;
maybeExisting = staticUtilities.put(className, utility);
if (maybeExisting != null) {
throw new IllegalArgumentException(
"Class name " + className + " already mapped to " + maybeExisting
);
}
}
}
private Map values(Css.Key key, String defaults) {
CssProperties properties;
properties = overrides.get(key);
if (properties != null) {
return mergeExtensionsIfNecessary(key, properties);
}
CssProperties defaultProperties;
defaultProperties = Css.parseProperties(defaults);
return mergeExtensionsIfNecessary(key, defaultProperties);
}
private Map values(Css.Key key, Supplier
© 2015 - 2025 Weber Informatics LLC | Privacy Policy