attempt from scratch

This commit is contained in:
Mira
2026-04-15 14:27:39 +03:00
parent fe93824063
commit 1acf0ce9cd
+217
View File
@@ -0,0 +1,217 @@
import plugin from "tailwindcss/plugin";
import flattenColorPalette from "tailwindcss/lib/util/flattenColorPalette";
const DEFAULT_GAP = "calc(var(--spacing, 0.25rem) * 2)";
const DEFAULT_LENGTH = "calc(var(--spacing, 0.25rem) * 3)";
const DEFAULT_OFFSET = "0px";
const DEFAULT_WIDTH = "1px";
const DEFAULT_COLOR = "currentColor";
const DEFAULT_RADIUS = "0px";
const DASH_ARRAY = "var(--tw-dashed-border-length) var(--tw-dashed-border-gap)";
const INSET = "calc(var(--tw-dashed-border-width) / 2)";
const INNER_SIZE = "calc(100% - var(--tw-dashed-border-width))";
const ADJUSTED_RADIUS =
"max(0px, calc(var(--tw-dashed-border-radius) - (var(--tw-dashed-border-width) / 2)))";
const ALL_TRANSITION_PROPERTIES =
"stroke-dasharray, stroke-dashoffset, stroke-width, stroke, rx";
const COLOR_TRANSITION_PROPERTIES = "stroke";
const TRANSITION_DURATION =
"var(--tw-duration, var(--default-transition-duration))";
const TRANSITION_TIMING_FUNCTION =
"var(--tw-ease, var(--default-transition-timing-function))";
type ThemeLeafMap = Record<string, string>;
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
function flattenThemeLeaves(
value: unknown,
path: string[] = [],
): ThemeLeafMap {
if (!isRecord(value)) {
return {};
}
const flattened: ThemeLeafMap = {};
for (const [key, entry] of Object.entries(value)) {
if (key === "__CSS_VALUES__") {
continue;
}
const nextPath = key === "DEFAULT" ? path : [...path, key];
if (isRecord(entry)) {
Object.assign(flattened, flattenThemeLeaves(entry, nextPath));
continue;
}
if (typeof entry === "string" || typeof entry === "number") {
const flattenedKey = nextPath.join("-");
flattened[flattenedKey === "" ? "DEFAULT" : flattenedKey] = String(entry);
}
}
return flattened;
}
function omitDefaultKey(values: ThemeLeafMap): ThemeLeafMap {
const { DEFAULT: _default, ...rest } = values;
return rest;
}
function createTransitionStyles(properties: string) {
return {
"transition-property": properties,
"transition-duration": TRANSITION_DURATION,
"transition-timing-function": TRANSITION_TIMING_FUNCTION,
};
}
const dashedBorderPlugin: ReturnType<typeof plugin> = plugin(
({ addBase, addUtilities, matchUtilities, theme }) => {
const spacingValues = flattenThemeLeaves(theme("spacing"));
const borderWidthValues = omitDefaultKey(
flattenThemeLeaves(theme("borderWidth")),
);
const borderRadiusValues = flattenThemeLeaves(theme("borderRadius"));
const colorValues = {
...flattenColorPalette(theme("colors") ?? {}),
current: "currentColor",
inherit: "inherit",
transparent: "transparent",
};
addBase({
"@property --tw-dashed-border-gap": {
syntax: "<length>",
inherits: "true",
"initial-value": DEFAULT_GAP,
},
"@property --tw-dashed-border-length": {
syntax: "<length>",
inherits: "true",
"initial-value": DEFAULT_LENGTH,
},
"@property --tw-dashed-border-offset": {
syntax: "<length>",
inherits: "true",
"initial-value": DEFAULT_OFFSET,
},
"@property --tw-dashed-border-width": {
syntax: "<length>",
inherits: "true",
"initial-value": DEFAULT_WIDTH,
},
"@property --tw-dashed-border-radius": {
syntax: "<length>",
inherits: "true",
"initial-value": DEFAULT_RADIUS,
},
".dashed-border": {
position: "relative",
"--tw-dashed-border-gap": DEFAULT_GAP,
"--tw-dashed-border-length": DEFAULT_LENGTH,
"--tw-dashed-border-offset": DEFAULT_OFFSET,
"--tw-dashed-border-width": DEFAULT_WIDTH,
"--tw-dashed-border-color": DEFAULT_COLOR,
"--tw-dashed-border-radius": DEFAULT_RADIUS,
},
".dashed-border-svg": {
position: "absolute",
inset: "0",
width: "100%",
height: "100%",
overflow: "hidden",
"pointer-events": "none",
},
".dashed-border-rect": {
fill: "none",
stroke: "var(--tw-dashed-border-color)",
"stroke-width": "var(--tw-dashed-border-width)",
"stroke-dasharray": DASH_ARRAY,
"stroke-dashoffset": "var(--tw-dashed-border-offset)",
"vector-effect": "non-scaling-stroke",
x: INSET,
y: INSET,
width: INNER_SIZE,
height: INNER_SIZE,
rx: ADJUSTED_RADIUS,
},
});
matchUtilities(
{
"dashed-border-gap": (value) => ({
"--tw-dashed-border-gap": value,
}),
"dashed-border-length": (value) => ({
"--tw-dashed-border-length": value,
}),
"dashed-border-offset": (value) => ({
"--tw-dashed-border-offset": value,
}),
},
{
type: "length",
values: spacingValues,
},
);
matchUtilities(
{
"dashed-border-width": (value) => ({
"--tw-dashed-border-width": value,
}),
},
{
type: "length",
values: borderWidthValues,
},
);
matchUtilities(
{
"dashed-border-color": (value) => ({
"--tw-dashed-border-color": value,
}),
},
{
type: "color",
values: colorValues,
},
);
const radiusUtilities = Object.fromEntries(
Object.entries(borderRadiusValues).map(([key, value]) => {
const roundedClass = key === "DEFAULT" ? "rounded" : `rounded-${key}`;
return [
`.dashed-border.${roundedClass}`,
{
"--tw-dashed-border-radius": value,
},
];
}),
);
addUtilities({
...radiusUtilities,
".dashed-border-transition .dashed-border-rect": createTransitionStyles(
ALL_TRANSITION_PROPERTIES,
),
".dashed-border.transition .dashed-border-rect": createTransitionStyles(
ALL_TRANSITION_PROPERTIES,
),
".dashed-border.transition-all .dashed-border-rect":
createTransitionStyles(ALL_TRANSITION_PROPERTIES),
".dashed-border.transition-colors .dashed-border-rect":
createTransitionStyles(COLOR_TRANSITION_PROPERTIES),
});
},
);
export default dashedBorderPlugin;