vendor plugins
This commit is contained in:
461
src/vendor/forms.ts
vendored
Normal file
461
src/vendor/forms.ts
vendored
Normal file
@ -0,0 +1,461 @@
|
||||
// https://github.com/tailwindlabs/tailwindcss-forms/commit/c9d9da3e010b194a1f0e9c36fbd98c83e4762840
|
||||
import plugin from "tailwindcss/plugin";
|
||||
import defaultTheme from "tailwindcss/defaultTheme";
|
||||
import colors from "tailwindcss/colors";
|
||||
import type { CSSRuleObject } from "tailwindcss/types/config";
|
||||
|
||||
const shorterNames = {
|
||||
aqua: /#00ffff(ff)?(?!\w)|#0ff(f)?(?!\w)/gi,
|
||||
azure: /#f0ffff(ff)?(?!\w)/gi,
|
||||
beige: /#f5f5dc(ff)?(?!\w)/gi,
|
||||
bisque: /#ffe4c4(ff)?(?!\w)/gi,
|
||||
black: /#000000(ff)?(?!\w)|#000(f)?(?!\w)/gi,
|
||||
blue: /#0000ff(ff)?(?!\w)|#00f(f)?(?!\w)/gi,
|
||||
brown: /#a52a2a(ff)?(?!\w)/gi,
|
||||
coral: /#ff7f50(ff)?(?!\w)/gi,
|
||||
cornsilk: /#fff8dc(ff)?(?!\w)/gi,
|
||||
crimson: /#dc143c(ff)?(?!\w)/gi,
|
||||
cyan: /#00ffff(ff)?(?!\w)|#0ff(f)?(?!\w)/gi,
|
||||
darkblue: /#00008b(ff)?(?!\w)/gi,
|
||||
darkcyan: /#008b8b(ff)?(?!\w)/gi,
|
||||
darkgrey: /#a9a9a9(ff)?(?!\w)/gi,
|
||||
darkred: /#8b0000(ff)?(?!\w)/gi,
|
||||
deeppink: /#ff1493(ff)?(?!\w)/gi,
|
||||
dimgrey: /#696969(ff)?(?!\w)/gi,
|
||||
gold: /#ffd700(ff)?(?!\w)/gi,
|
||||
green: /#008000(ff)?(?!\w)/gi,
|
||||
grey: /#808080(ff)?(?!\w)/gi,
|
||||
honeydew: /#f0fff0(ff)?(?!\w)/gi,
|
||||
hotpink: /#ff69b4(ff)?(?!\w)/gi,
|
||||
indigo: /#4b0082(ff)?(?!\w)/gi,
|
||||
ivory: /#fffff0(ff)?(?!\w)/gi,
|
||||
khaki: /#f0e68c(ff)?(?!\w)/gi,
|
||||
lavender: /#e6e6fa(ff)?(?!\w)/gi,
|
||||
lime: /#00ff00(ff)?(?!\w)|#0f0(f)?(?!\w)/gi,
|
||||
linen: /#faf0e6(ff)?(?!\w)/gi,
|
||||
maroon: /#800000(ff)?(?!\w)/gi,
|
||||
moccasin: /#ffe4b5(ff)?(?!\w)/gi,
|
||||
navy: /#000080(ff)?(?!\w)/gi,
|
||||
oldlace: /#fdf5e6(ff)?(?!\w)/gi,
|
||||
olive: /#808000(ff)?(?!\w)/gi,
|
||||
orange: /#ffa500(ff)?(?!\w)/gi,
|
||||
orchid: /#da70d6(ff)?(?!\w)/gi,
|
||||
peru: /#cd853f(ff)?(?!\w)/gi,
|
||||
pink: /#ffc0cb(ff)?(?!\w)/gi,
|
||||
plum: /#dda0dd(ff)?(?!\w)/gi,
|
||||
purple: /#800080(ff)?(?!\w)/gi,
|
||||
red: /#ff0000(ff)?(?!\w)|#f00(f)?(?!\w)/gi,
|
||||
salmon: /#fa8072(ff)?(?!\w)/gi,
|
||||
seagreen: /#2e8b57(ff)?(?!\w)/gi,
|
||||
seashell: /#fff5ee(ff)?(?!\w)/gi,
|
||||
sienna: /#a0522d(ff)?(?!\w)/gi,
|
||||
silver: /#c0c0c0(ff)?(?!\w)/gi,
|
||||
skyblue: /#87ceeb(ff)?(?!\w)/gi,
|
||||
snow: /#fffafa(ff)?(?!\w)/gi,
|
||||
tan: /#d2b48c(ff)?(?!\w)/gi,
|
||||
teal: /#008080(ff)?(?!\w)/gi,
|
||||
thistle: /#d8bfd8(ff)?(?!\w)/gi,
|
||||
tomato: /#ff6347(ff)?(?!\w)/gi,
|
||||
violet: /#ee82ee(ff)?(?!\w)/gi,
|
||||
wheat: /#f5deb3(ff)?(?!\w)/gi,
|
||||
white: /#ffffff(ff)?(?!\w)|#fff(f)?(?!\w)/gi,
|
||||
};
|
||||
|
||||
const REGEX = {
|
||||
whitespace: /\s+/g,
|
||||
urlHexPairs: /%[\dA-F]{2}/g,
|
||||
quotes: /"/g,
|
||||
};
|
||||
|
||||
function collapseWhitespace(str: string) {
|
||||
return str.trim().replace(REGEX.whitespace, " ");
|
||||
}
|
||||
|
||||
function dataURIPayload(string: string) {
|
||||
return encodeURIComponent(string).replace(REGEX.urlHexPairs, specialHexEncode);
|
||||
}
|
||||
|
||||
// `#` gets converted to `%23`, so quite a few CSS named colors are shorter than
|
||||
// their equivalent URL-encoded hex codes.
|
||||
function colorCodeToShorterNames(string: string) {
|
||||
for (const [key, value] of Object.entries(shorterNames)) {
|
||||
if (value.test(string)) {
|
||||
string = string.replace(value, key);
|
||||
}
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
function specialHexEncode(match: string) {
|
||||
// Browsers tolerate these characters, and they're frequent
|
||||
switch (match) {
|
||||
case "%20":
|
||||
return " ";
|
||||
case "%3D":
|
||||
return "=";
|
||||
case "%3A":
|
||||
return ":";
|
||||
case "%2F":
|
||||
return "/";
|
||||
default:
|
||||
return match.toLowerCase(); // compresses better
|
||||
}
|
||||
}
|
||||
|
||||
function svgToDataUri(svgString: string) {
|
||||
// Strip the Byte-Order Mark if the SVG has one
|
||||
// eslint-disable-next-line unicorn/number-literal-case
|
||||
if (svgString.charCodeAt(0) === 0xfeff) {
|
||||
svgString = svgString.slice(1);
|
||||
}
|
||||
|
||||
const body = colorCodeToShorterNames(collapseWhitespace(svgString)).replace(
|
||||
REGEX.quotes,
|
||||
"'"
|
||||
);
|
||||
return "data:image/svg+xml," + dataURIPayload(body);
|
||||
}
|
||||
|
||||
const [baseFontSize, { lineHeight: baseLineHeight }] = defaultTheme.fontSize.base;
|
||||
const { spacing, borderWidth, borderRadius } = defaultTheme;
|
||||
|
||||
type Strategy = "base" | "class";
|
||||
|
||||
export default plugin.withOptions<{ strategy?: Strategy }>(
|
||||
options =>
|
||||
function ({ addBase, addComponents, theme }) {
|
||||
const strategy =
|
||||
options?.strategy === undefined ? ["base", "class"] : [options.strategy];
|
||||
|
||||
const rules = [
|
||||
{
|
||||
base: [
|
||||
"[type='text']",
|
||||
"input:where(:not([type]))",
|
||||
"[type='email']",
|
||||
"[type='url']",
|
||||
"[type='password']",
|
||||
"[type='number']",
|
||||
"[type='date']",
|
||||
"[type='datetime-local']",
|
||||
"[type='month']",
|
||||
"[type='search']",
|
||||
"[type='tel']",
|
||||
"[type='time']",
|
||||
"[type='week']",
|
||||
"[multiple]",
|
||||
"textarea",
|
||||
"select",
|
||||
],
|
||||
class: [".form-input", ".form-textarea", ".form-select", ".form-multiselect"],
|
||||
styles: {
|
||||
appearance: "none",
|
||||
"background-color": "#fff",
|
||||
"border-color": theme("colors.gray.500", colors.gray[500]),
|
||||
"border-width": borderWidth["DEFAULT"],
|
||||
"border-radius": borderRadius.none,
|
||||
"padding-top": spacing[2],
|
||||
"padding-right": spacing[3],
|
||||
"padding-bottom": spacing[2],
|
||||
"padding-left": spacing[3],
|
||||
"font-size": baseFontSize,
|
||||
"line-height": baseLineHeight,
|
||||
"--tw-shadow": "0 0 #0000",
|
||||
"&:focus": {
|
||||
outline: "2px solid transparent",
|
||||
"outline-offset": "2px",
|
||||
"--tw-ring-inset": "var(--tw-empty,/*!*/ /*!*/)",
|
||||
"--tw-ring-offset-width": "0px",
|
||||
"--tw-ring-offset-color": "#fff",
|
||||
"--tw-ring-color": theme("colors.blue.600", colors.blue[600]),
|
||||
"--tw-ring-offset-shadow": `var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)`,
|
||||
"--tw-ring-shadow": `var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)`,
|
||||
"box-shadow": `var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)`,
|
||||
"border-color": theme("colors.blue.600", colors.blue[600]),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
base: ["input::placeholder", "textarea::placeholder"],
|
||||
class: [".form-input::placeholder", ".form-textarea::placeholder"],
|
||||
styles: {
|
||||
color: theme("colors.gray.500", colors.gray[500]),
|
||||
opacity: "1",
|
||||
},
|
||||
},
|
||||
{
|
||||
base: ["::-webkit-datetime-edit-fields-wrapper"],
|
||||
class: [".form-input::-webkit-datetime-edit-fields-wrapper"],
|
||||
styles: {
|
||||
padding: "0",
|
||||
},
|
||||
},
|
||||
{
|
||||
// Unfortunate hack until https://bugs.webkit.org/show_bug.cgi?id=198959 is fixed.
|
||||
// This sucks because users can't change line-height with a utility on date inputs now.
|
||||
// Reference: https://github.com/twbs/bootstrap/pull/31993
|
||||
base: ["::-webkit-date-and-time-value"],
|
||||
class: [".form-input::-webkit-date-and-time-value"],
|
||||
styles: {
|
||||
"min-height": "1.5em",
|
||||
},
|
||||
},
|
||||
{
|
||||
// In Safari on iOS date and time inputs are centered instead of left-aligned and can't be
|
||||
// changed with `text-align` utilities on the input by default. Resetting this to `inherit`
|
||||
// makes them left-aligned by default and makes it possible to override the alignment with
|
||||
// utility classes without using an arbitrary variant to target the pseudo-elements.
|
||||
base: ["::-webkit-date-and-time-value"],
|
||||
class: [".form-input::-webkit-date-and-time-value"],
|
||||
styles: {
|
||||
"text-align": "inherit",
|
||||
},
|
||||
},
|
||||
{
|
||||
// In Safari on macOS date time inputs that are set to `display: block` have unexpected
|
||||
// extra bottom spacing. This can be corrected by setting the `::-webkit-datetime-edit`
|
||||
// pseudo-element to `display: inline-flex`, instead of the browser default of
|
||||
// `display: inline-block`.
|
||||
base: ["::-webkit-datetime-edit"],
|
||||
class: [".form-input::-webkit-datetime-edit"],
|
||||
styles: {
|
||||
display: "inline-flex",
|
||||
},
|
||||
},
|
||||
{
|
||||
// In Safari on macOS date time inputs are 4px taller than normal inputs
|
||||
// This is because there is extra padding on the datetime-edit and datetime-edit-{part}-field pseudo elements
|
||||
// See https://github.com/tailwindlabs/tailwindcss-forms/issues/95
|
||||
base: [
|
||||
"::-webkit-datetime-edit",
|
||||
"::-webkit-datetime-edit-year-field",
|
||||
"::-webkit-datetime-edit-month-field",
|
||||
"::-webkit-datetime-edit-day-field",
|
||||
"::-webkit-datetime-edit-hour-field",
|
||||
"::-webkit-datetime-edit-minute-field",
|
||||
"::-webkit-datetime-edit-second-field",
|
||||
"::-webkit-datetime-edit-millisecond-field",
|
||||
"::-webkit-datetime-edit-meridiem-field",
|
||||
],
|
||||
class: [
|
||||
".form-input::-webkit-datetime-edit",
|
||||
".form-input::-webkit-datetime-edit-year-field",
|
||||
".form-input::-webkit-datetime-edit-month-field",
|
||||
".form-input::-webkit-datetime-edit-day-field",
|
||||
".form-input::-webkit-datetime-edit-hour-field",
|
||||
".form-input::-webkit-datetime-edit-minute-field",
|
||||
".form-input::-webkit-datetime-edit-second-field",
|
||||
".form-input::-webkit-datetime-edit-millisecond-field",
|
||||
".form-input::-webkit-datetime-edit-meridiem-field",
|
||||
],
|
||||
styles: {
|
||||
"padding-top": 0,
|
||||
"padding-bottom": 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
base: ["select"],
|
||||
class: [".form-select"],
|
||||
styles: {
|
||||
"background-image": `url("${svgToDataUri(
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20"><path stroke="${theme(
|
||||
"colors.gray.500",
|
||||
colors.gray[500]
|
||||
)}" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M6 8l4 4 4-4"/></svg>`
|
||||
)}")`,
|
||||
"background-position": `right ${spacing[2]} center`,
|
||||
"background-repeat": `no-repeat`,
|
||||
"background-size": `1.5em 1.5em`,
|
||||
"padding-right": spacing[10],
|
||||
"print-color-adjust": `exact`,
|
||||
},
|
||||
},
|
||||
{
|
||||
base: ["[multiple]", '[size]:where(select:not([size="1"]))'],
|
||||
class: ['.form-select:where([size]:not([size="1"]))'],
|
||||
styles: {
|
||||
"background-image": "initial",
|
||||
"background-position": "initial",
|
||||
"background-repeat": "unset",
|
||||
"background-size": "initial",
|
||||
"padding-right": spacing[3],
|
||||
"print-color-adjust": "unset",
|
||||
},
|
||||
},
|
||||
{
|
||||
base: [`[type='checkbox']`, `[type='radio']`],
|
||||
class: [".form-checkbox", ".form-radio"],
|
||||
styles: {
|
||||
appearance: "none",
|
||||
padding: "0",
|
||||
"print-color-adjust": "exact",
|
||||
display: "inline-block",
|
||||
"vertical-align": "middle",
|
||||
"background-origin": "border-box",
|
||||
"user-select": "none",
|
||||
"flex-shrink": "0",
|
||||
height: spacing[4],
|
||||
width: spacing[4],
|
||||
color: theme("colors.blue.600", colors.blue[600]),
|
||||
"background-color": "#fff",
|
||||
"border-color": theme("colors.gray.500", colors.gray[500]),
|
||||
"border-width": borderWidth["DEFAULT"],
|
||||
"--tw-shadow": "0 0 #0000",
|
||||
},
|
||||
},
|
||||
{
|
||||
base: [`[type='checkbox']`],
|
||||
class: [".form-checkbox"],
|
||||
styles: {
|
||||
"border-radius": borderRadius["none"],
|
||||
},
|
||||
},
|
||||
{
|
||||
base: [`[type='radio']`],
|
||||
class: [".form-radio"],
|
||||
styles: {
|
||||
"border-radius": "100%",
|
||||
},
|
||||
},
|
||||
{
|
||||
base: [`[type='checkbox']:focus`, `[type='radio']:focus`],
|
||||
class: [".form-checkbox:focus", ".form-radio:focus"],
|
||||
styles: {
|
||||
outline: "2px solid transparent",
|
||||
"outline-offset": "2px",
|
||||
"--tw-ring-inset": "var(--tw-empty,/*!*/ /*!*/)",
|
||||
"--tw-ring-offset-width": "2px",
|
||||
"--tw-ring-offset-color": "#fff",
|
||||
"--tw-ring-color": theme("colors.blue.600", colors.blue[600]),
|
||||
"--tw-ring-offset-shadow": `var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)`,
|
||||
"--tw-ring-shadow": `var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color)`,
|
||||
"box-shadow": `var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)`,
|
||||
},
|
||||
},
|
||||
{
|
||||
base: [`[type='checkbox']:checked`, `[type='radio']:checked`],
|
||||
class: [".form-checkbox:checked", ".form-radio:checked"],
|
||||
styles: {
|
||||
"border-color": `transparent`,
|
||||
"background-color": `currentColor`,
|
||||
"background-size": `100% 100%`,
|
||||
"background-position": `center`,
|
||||
"background-repeat": `no-repeat`,
|
||||
},
|
||||
},
|
||||
{
|
||||
base: [`[type='checkbox']:checked`],
|
||||
class: [".form-checkbox:checked"],
|
||||
styles: {
|
||||
"background-image": `url("${svgToDataUri(
|
||||
`<svg viewBox="0 0 16 16" fill="white" xmlns="http://www.w3.org/2000/svg"><path d="M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z"/></svg>`
|
||||
)}")`,
|
||||
|
||||
"@media (forced-colors: active) ": {
|
||||
appearance: "auto",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
base: [`[type='radio']:checked`],
|
||||
class: [".form-radio:checked"],
|
||||
styles: {
|
||||
"background-image": `url("${svgToDataUri(
|
||||
`<svg viewBox="0 0 16 16" fill="white" xmlns="http://www.w3.org/2000/svg"><circle cx="8" cy="8" r="3"/></svg>`
|
||||
)}")`,
|
||||
|
||||
"@media (forced-colors: active) ": {
|
||||
appearance: "auto",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
base: [
|
||||
`[type='checkbox']:checked:hover`,
|
||||
`[type='checkbox']:checked:focus`,
|
||||
`[type='radio']:checked:hover`,
|
||||
`[type='radio']:checked:focus`,
|
||||
],
|
||||
class: [
|
||||
".form-checkbox:checked:hover",
|
||||
".form-checkbox:checked:focus",
|
||||
".form-radio:checked:hover",
|
||||
".form-radio:checked:focus",
|
||||
],
|
||||
styles: {
|
||||
"border-color": "transparent",
|
||||
"background-color": "currentColor",
|
||||
},
|
||||
},
|
||||
{
|
||||
base: [`[type='checkbox']:indeterminate`],
|
||||
class: [".form-checkbox:indeterminate"],
|
||||
styles: {
|
||||
"background-image": `url("${svgToDataUri(
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16"><path stroke="white" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 8h8"/></svg>`
|
||||
)}")`,
|
||||
"border-color": `transparent`,
|
||||
"background-color": `currentColor`,
|
||||
"background-size": `100% 100%`,
|
||||
"background-position": `center`,
|
||||
"background-repeat": `no-repeat`,
|
||||
|
||||
"@media (forced-colors: active) ": {
|
||||
appearance: "auto",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
base: [
|
||||
`[type='checkbox']:indeterminate:hover`,
|
||||
`[type='checkbox']:indeterminate:focus`,
|
||||
],
|
||||
class: [
|
||||
".form-checkbox:indeterminate:hover",
|
||||
".form-checkbox:indeterminate:focus",
|
||||
],
|
||||
styles: {
|
||||
"border-color": "transparent",
|
||||
"background-color": "currentColor",
|
||||
},
|
||||
},
|
||||
{
|
||||
base: [`[type='file']`],
|
||||
class: null,
|
||||
styles: {
|
||||
background: "unset",
|
||||
"border-color": "inherit",
|
||||
"border-width": "0",
|
||||
"border-radius": "0",
|
||||
padding: "0",
|
||||
"font-size": "unset",
|
||||
"line-height": "inherit",
|
||||
},
|
||||
},
|
||||
{
|
||||
base: [`[type='file']:focus`],
|
||||
class: null,
|
||||
styles: {
|
||||
outline: [`1px solid ButtonText`, `1px auto -webkit-focus-ring-color`],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const getStrategyRules = (strategy: Strategy): CSSRuleObject[] =>
|
||||
rules
|
||||
.map(rule => {
|
||||
if (rule[strategy] === null) return null;
|
||||
return { [rule[strategy].join(", ")]: rule.styles } as CSSRuleObject;
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
if (strategy.includes("base")) {
|
||||
addBase(getStrategyRules("base"));
|
||||
}
|
||||
|
||||
if (strategy.includes("class")) {
|
||||
addComponents(getStrategyRules("class"));
|
||||
}
|
||||
}
|
||||
);
|
Reference in New Issue
Block a user