@lynx-js/tailwind-preset-canary 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/README.md +52 -77
- package/dist/core.d.ts +11 -2
- package/dist/helpers.d.ts +7 -14
- package/dist/lynx.cjs +120 -19
- package/dist/lynx.d.ts +3 -2
- package/dist/lynx.js +120 -19
- package/dist/plugins/lynx-ui/index.d.ts +1 -0
- package/dist/plugins/lynx-ui/plugin-registry.d.ts +20 -0
- package/dist/plugins/lynx-ui/uiVariants.d.ts +32 -0
- package/dist/types/plugin-types.d.ts +12 -18
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @lynx-js/tailwind-preset
|
|
2
2
|
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Add support for Lynx UI plugin system with configurable options. ([#1363](https://github.com/lynx-family/lynx-stack/pull/1363))
|
|
8
|
+
|
|
9
|
+
- Introduced `lynxUIPlugins` option in `createLynxPreset`, allowing userland opt-in to Lynx UI specific plugins.
|
|
10
|
+
|
|
11
|
+
- Implemented `uiVariants` plugin as the first UI plugin, supporting `ui-*` variant prefixes (e.g. `ui-checked`, `ui-open`) with customizable mappings.
|
|
12
|
+
|
|
3
13
|
## 0.1.2
|
|
4
14
|
|
|
5
15
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
# Lynx Tailwind Preset (V3)
|
|
2
2
|
|
|
3
|
-
A [Tailwind
|
|
3
|
+
A [Tailwind CSS v3](https://v3.tailwindcss.com/) preset for the Lynx ecosystem.
|
|
4
|
+
|
|
5
|
+
This preset is not a 1:1 port of Tailwind's core. Instead, it provides a **Lynx-native Tailwind experience** tailored for the platform's rendering model and ecosystem needs by:
|
|
6
|
+
|
|
7
|
+
- Including only CSS utilities that Lynx supports
|
|
8
|
+
|
|
9
|
+
- Reimagining certain utilities to align with Lynx's styling constraints and runtime behavior
|
|
10
|
+
|
|
11
|
+
- Enabling ecosystem extensions such as UI state variants, animation presets, and design token integration
|
|
4
12
|
|
|
5
13
|
> **⚠️ Experimental**\
|
|
6
14
|
> This preset is currently in experimental stage as we are still exploring the best possible DX to write Tailwind upon Lynx. We welcome and encourage contributions from the community to help shape its future development. Your feedback, bug reports, and pull requests are invaluable in making this preset more robust and feature-complete.
|
|
7
15
|
|
|
8
16
|
## Basic Usage
|
|
9
17
|
|
|
10
|
-
```
|
|
18
|
+
```ts
|
|
11
19
|
// tailwind.config.ts
|
|
12
20
|
import preset from '@lynx-js/tailwind-preset';
|
|
13
21
|
|
|
@@ -17,7 +25,7 @@ export default {
|
|
|
17
25
|
};
|
|
18
26
|
```
|
|
19
27
|
|
|
20
|
-
```
|
|
28
|
+
```ts
|
|
21
29
|
// tailwind.config.ts
|
|
22
30
|
import { createLynxPreset } from '@lynx-js/tailwind-preset';
|
|
23
31
|
|
|
@@ -31,80 +39,7 @@ export default {
|
|
|
31
39
|
};
|
|
32
40
|
```
|
|
33
41
|
|
|
34
|
-
##
|
|
35
|
-
|
|
36
|
-
- `src/lynx.ts`: Main preset configuration that reverse-engineered [Tailwind's core plugins](https://github.com/tailwindlabs/tailwindcss/blob/v3/src/corePlugins.js).
|
|
37
|
-
- `src/plugins/lynx/`: Custom plugins as replacement when per-class customization are needed.
|
|
38
|
-
- `src/__tests__/`: Test files to ensure correct utility generation
|
|
39
|
-
|
|
40
|
-
## Contributing
|
|
41
|
-
|
|
42
|
-
### Getting Started
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
# Install dependencies
|
|
46
|
-
pnpm install
|
|
47
|
-
|
|
48
|
-
# Run tests
|
|
49
|
-
pnpm test
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
### Adding New Utilities
|
|
53
|
-
|
|
54
|
-
#### 1. Check if the CSS property is supported by Lynx
|
|
55
|
-
|
|
56
|
-
This can be verified in three ways:
|
|
57
|
-
|
|
58
|
-
1. [`@lynx-js/css-defines`](https://www.npmjs.com/package/@lynx-js/css-defines), this is the most accurate list of CSS properties supported by Lynx, directly generated from the source of Lynx internal definitions and released along with each Lynx releases.
|
|
59
|
-
2. `csstype.d.ts` in `@lynx-js/types`, this is used as the types of inline styles (e.g. `<view style>`) but this is currently maintained manually.
|
|
60
|
-
3. Lynx's runtime behaviors.
|
|
61
|
-
|
|
62
|
-
#### 2. Add/Remove it from the preset
|
|
63
|
-
|
|
64
|
-
##### 2.1 If enabling a Tailwind core plugin
|
|
65
|
-
|
|
66
|
-
- Add it to `DEFAULT_CORE_PLUGINS` array in `src/core.ts`
|
|
67
|
-
|
|
68
|
-
##### 2.2 If it requires custom handling
|
|
69
|
-
|
|
70
|
-
###### Lynx core plugins
|
|
71
|
-
|
|
72
|
-
For plugins that are considered **part of the Lynx core preset** — either implementing Tailwind core utilities or providing Lynx-specific functionality
|
|
73
|
-
|
|
74
|
-
- Create a new plugin in `src/plugins/lynx/`
|
|
75
|
-
- Use shared helpers from `src/helpers.ts` and `src/plugin-utils/` where applicable
|
|
76
|
-
- Export it from `src/plugins/lynx/index.ts`
|
|
77
|
-
- Register it in `src/plugins/lynx/plugin-registry.ts`\
|
|
78
|
-
_(This ensures a stable sorting order for the core set)_
|
|
79
|
-
- It will be automatically included in `src/core.ts` via the Lynx plugin registry
|
|
80
|
-
|
|
81
|
-
###### Non-core / Custom plugin categories
|
|
82
|
-
|
|
83
|
-
For plugins that are **not part of the Lynx core preset** — such as experimental features, app-specific utilities, or standalone plugin groups:
|
|
84
|
-
|
|
85
|
-
- These plugins require explicit registration and ordering
|
|
86
|
-
- Create a new category folder under `src/plugins/` (e.g. `src/plugins/experimental/`)
|
|
87
|
-
- Add the plugin in that folder, and optionally extract shared logic into `plugin-utils/`
|
|
88
|
-
- Export the plugin from `src/plugins/{category}/index.ts`
|
|
89
|
-
- Define a `plugin-registry.ts` in the same folder to control plugin order
|
|
90
|
-
- Import the registry in `src/core.ts` and include it in the appropriate position
|
|
91
|
-
|
|
92
|
-
#### 3. Adding Tests
|
|
93
|
-
|
|
94
|
-
We test by using Tailwind CLI to build `src/__tests__/` demo project with our preset, then extracting all properties used in the generated utilities and verify if all used properties are allowed according to `@lynx-js/types`.
|
|
95
|
-
|
|
96
|
-
To test new Tailwind utilities:
|
|
97
|
-
|
|
98
|
-
1. Modify `testClasses` in `src/__tests__/test-content.tsx`
|
|
99
|
-
2. Modify `supportedProperties` or `allowedUnsupportedProperties` in `config.test.ts`
|
|
100
|
-
3. Run tests with `pnpm test` to verify with Vitest.
|
|
101
|
-
|
|
102
|
-
To test new plugins:
|
|
103
|
-
|
|
104
|
-
1. Add new test file in `src/__tests__/plugins`. Import `runPlugin` test util function from `src/__tests__/utils/run-plugin.ts`. Mock theme values.
|
|
105
|
-
2. Run tests with `pnpm test` to verify with Vitest.
|
|
106
|
-
|
|
107
|
-
## Integration notes
|
|
42
|
+
## Integration Notes
|
|
108
43
|
|
|
109
44
|
### tailwind-merge & rsbuild-plugin-tailwindcss
|
|
110
45
|
|
|
@@ -128,3 +63,43 @@ export default {
|
|
|
128
63
|
],
|
|
129
64
|
};
|
|
130
65
|
```
|
|
66
|
+
|
|
67
|
+
## Ecosystem Extensions
|
|
68
|
+
|
|
69
|
+
Beyond core utility coverage, this preset supports ecosystem-level extensions to improve component styling DX and support common Tailwind ecosystem patterns adapted for Lynx.
|
|
70
|
+
|
|
71
|
+
### Enabling Lynx UI Plugins
|
|
72
|
+
|
|
73
|
+
UI plugins are not enabled by default. You can enable all plugins with:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
createLynxPreset({
|
|
77
|
+
lynxUIPlugins: true,
|
|
78
|
+
});
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Or enable individual plugins with their default options:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
createLynxPreset({
|
|
85
|
+
lynxUIPlugins: { uiVariants: true },
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Or configure each plugin individually — see each plugin's documentation for available options:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
createLynxPreset({
|
|
93
|
+
lynxUIPlugins: {
|
|
94
|
+
uiVariants: {
|
|
95
|
+
prefixes: {
|
|
96
|
+
ui: ['open', 'checked'],
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
#### Available Plugins
|
|
104
|
+
|
|
105
|
+
- [uiVariants](https://github.com/lynx-family/lynx-stack/tree/main/packages/third-party/tailwind-preset/docs/plugins/lynx-ui/uiVariants.md) — Class-based variants for expressing component state or structure using `ui-*` prefixes (e.g. `.ui-open:`, `.ui-side-left:`).
|
package/dist/core.d.ts
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
import { LYNX_PLUGIN_MAP, ORDERED_LYNX_PLUGIN_NAMES } from './plugins/lynx/plugin-registry.js';
|
|
2
2
|
import type { LynxPluginName } from './plugins/lynx/plugin-types.js';
|
|
3
|
+
import { LYNX_UI_PLUGIN_MAP, ORDERED_LYNX_UI_PLUGIN_NAMES } from './plugins/lynx-ui/plugin-registry.js';
|
|
4
|
+
import type { LynxUIPluginName, LynxUIPluginOptionsMap } from './plugins/lynx-ui/plugin-registry.js';
|
|
3
5
|
import type { CorePluginsConfig } from './types/tailwind-types.js';
|
|
4
6
|
export declare const DEFAULT_CORE_PLUGINS: CorePluginsConfig;
|
|
5
7
|
export type LynxPluginsOption = boolean | LynxPluginName[] | Partial<Record<LynxPluginName, boolean>>;
|
|
8
|
+
export type LynxUIPluginsOption = boolean | LynxUIPluginName[] | Partial<{
|
|
9
|
+
[K in LynxUIPluginName]: boolean | LynxUIPluginOptionsMap[K];
|
|
10
|
+
}>;
|
|
6
11
|
export declare function toEnabledSet(opt?: LynxPluginsOption): Set<LynxPluginName>;
|
|
12
|
+
export declare function toEnabledLynxUIPluginSet(opt?: LynxUIPluginsOption): Set<LynxUIPluginName>;
|
|
13
|
+
export declare function resolveUIPluginEntries(raw: LynxUIPluginsOption): {
|
|
14
|
+
[K in LynxUIPluginName]: [K, LynxUIPluginOptionsMap[K] | undefined];
|
|
15
|
+
}[LynxUIPluginName][];
|
|
7
16
|
export declare const getReplaceablePlugins: () => readonly LynxPluginName[];
|
|
8
17
|
export declare const isPluginReplaceable: (p: string) => p is LynxPluginName;
|
|
9
|
-
export type { LynxPluginName };
|
|
10
|
-
export { LYNX_PLUGIN_MAP, ORDERED_LYNX_PLUGIN_NAMES };
|
|
18
|
+
export type { LynxPluginName, LynxUIPluginName, LynxUIPluginOptionsMap };
|
|
19
|
+
export { LYNX_PLUGIN_MAP, ORDERED_LYNX_PLUGIN_NAMES, LYNX_UI_PLUGIN_MAP, ORDERED_LYNX_UI_PLUGIN_NAMES, };
|
|
11
20
|
/** svg-related plugins */
|
|
12
21
|
/** filter-related plugins, only gradyscale and blur are supported*/
|
|
13
22
|
/** backdrop-related plugins */
|
package/dist/helpers.d.ts
CHANGED
|
@@ -1,20 +1,13 @@
|
|
|
1
1
|
import { formatBoxShadowValue, parseBoxShadowValue } from 'tailwindcss/lib/util/parseBoxShadowValue.js';
|
|
2
2
|
import type { ShadowPart } from 'tailwindcss/lib/util/parseBoxShadowValue.js';
|
|
3
3
|
import type { ThemeKey, ValueTransformer } from 'tailwindcss/lib/util/transformThemeValue.js';
|
|
4
|
-
import type { Bound,
|
|
5
|
-
import type {
|
|
4
|
+
import type { Bound, CreatePluginFunction, PluginWithOptions, UtilityPluginOptions, UtilityVariations } from './types/plugin-types.js';
|
|
5
|
+
import type { Plugin, PluginCreator } from './types/tailwind-types.js';
|
|
6
|
+
declare const createPlugin: CreatePluginFunction;
|
|
6
7
|
/**
|
|
7
|
-
*
|
|
8
|
+
* Type guard
|
|
8
9
|
*/
|
|
9
|
-
declare function
|
|
10
|
-
handler: PluginCreator;
|
|
11
|
-
config?: Partial<Config> | undefined;
|
|
12
|
-
};
|
|
13
|
-
declare function createPluginWithName(name: string, fn: BoundedPluginCreator, cfg?: Partial<Config>): {
|
|
14
|
-
handler: PluginCreator;
|
|
15
|
-
config?: Partial<Config> | undefined;
|
|
16
|
-
};
|
|
17
|
-
export declare const plugin: PluginFn;
|
|
10
|
+
declare function isPluginWithOptions<T = unknown>(plugin: unknown): plugin is PluginWithOptions<T>;
|
|
18
11
|
/**
|
|
19
12
|
* Returns a shallow clone of the object where all function values
|
|
20
13
|
* are bound with `this` set to `undefined`,
|
|
@@ -31,8 +24,8 @@ declare function autoBind<T extends object>(obj: T): Bound<T>;
|
|
|
31
24
|
* For internal use in Lynx plugin system.
|
|
32
25
|
*/
|
|
33
26
|
declare function createUtilityPlugin(themeKey: string, utilityVariations?: UtilityVariations, options?: UtilityPluginOptions): PluginCreator;
|
|
34
|
-
export { createUtilityPlugin, createPlugin,
|
|
35
|
-
export type { Plugin };
|
|
27
|
+
export { createUtilityPlugin, createPlugin, autoBind, isPluginWithOptions };
|
|
28
|
+
export type { Plugin, PluginWithOptions };
|
|
36
29
|
export declare const transformThemeValue: (key: ThemeKey) => ValueTransformer;
|
|
37
30
|
export { parseBoxShadowValue, formatBoxShadowValue };
|
|
38
31
|
export type { ShadowPart };
|
package/dist/lynx.cjs
CHANGED
|
@@ -41,28 +41,27 @@ var createUtilityPlugin_js_default = /*#__PURE__*/ __webpack_require__.n(createU
|
|
|
41
41
|
require("tailwindcss/lib/util/parseBoxShadowValue.js");
|
|
42
42
|
const transformThemeValue_js_namespaceObject = require("tailwindcss/lib/util/transformThemeValue.js");
|
|
43
43
|
var transformThemeValue_js_default = /*#__PURE__*/ __webpack_require__.n(transformThemeValue_js_namespaceObject);
|
|
44
|
-
function
|
|
44
|
+
function createPluginImpl(fn, cfg) {
|
|
45
45
|
return {
|
|
46
46
|
handler: (api)=>fn(autoBind(api)),
|
|
47
47
|
config: cfg
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
createPluginImpl.withOptions = withOptions;
|
|
51
|
+
const createPlugin = createPluginImpl;
|
|
52
|
+
function withOptions(pluginFn, configFn = ()=>({})) {
|
|
53
|
+
const optionsFunction = function(options) {
|
|
54
|
+
return {
|
|
55
|
+
__options: options,
|
|
56
|
+
handler: (api)=>pluginFn(options)(autoBind(api)),
|
|
57
|
+
config: configFn(options)
|
|
58
|
+
};
|
|
54
59
|
};
|
|
60
|
+
optionsFunction.__isOptionsFunction = true;
|
|
61
|
+
optionsFunction.__pluginFunction = pluginFn;
|
|
62
|
+
optionsFunction.__configFunction = configFn;
|
|
63
|
+
return optionsFunction;
|
|
55
64
|
}
|
|
56
|
-
function pluginImpl(pluginFn, cfg) {
|
|
57
|
-
const wrapped = (api)=>pluginFn(autoBind(api));
|
|
58
|
-
return basePlugin(wrapped, cfg);
|
|
59
|
-
}
|
|
60
|
-
function withOptions(factory, cfgFactory) {
|
|
61
|
-
const optionsFn = (options)=>basePlugin((api)=>factory(options)(autoBind(api)), cfgFactory?.(options));
|
|
62
|
-
optionsFn.__isOptionsFunction = true;
|
|
63
|
-
return optionsFn;
|
|
64
|
-
}
|
|
65
|
-
pluginImpl.withOptions = withOptions;
|
|
66
65
|
function autoBind(obj) {
|
|
67
66
|
return Object.fromEntries(Object.entries(obj).map(([k, v])=>isFunction(v) ? [
|
|
68
67
|
k,
|
|
@@ -929,6 +928,69 @@ const LYNX_PLUGIN_ENTRIES = [
|
|
|
929
928
|
const plugin_registry_LYNX_PLUGIN_MAP = Object.fromEntries(LYNX_PLUGIN_ENTRIES);
|
|
930
929
|
const ORDERED_LYNX_PLUGIN_NAMES = LYNX_PLUGIN_ENTRIES.map(([n])=>n);
|
|
931
930
|
const plugin_registry_REPLACEABLE_LYNX_PLUGINS = ORDERED_LYNX_PLUGIN_NAMES.filter((n)=>'defaults' !== n);
|
|
931
|
+
const DEFAULT_PREFIXES = {
|
|
932
|
+
ui: [
|
|
933
|
+
'active',
|
|
934
|
+
'disabled',
|
|
935
|
+
'readonly',
|
|
936
|
+
'checked',
|
|
937
|
+
'selected',
|
|
938
|
+
'open',
|
|
939
|
+
'leaving',
|
|
940
|
+
'entering',
|
|
941
|
+
'animating',
|
|
942
|
+
'busy'
|
|
943
|
+
],
|
|
944
|
+
'ui-side': [
|
|
945
|
+
'left',
|
|
946
|
+
'right',
|
|
947
|
+
'top',
|
|
948
|
+
'bottom'
|
|
949
|
+
],
|
|
950
|
+
'ui-align': [
|
|
951
|
+
'start',
|
|
952
|
+
'end',
|
|
953
|
+
'center'
|
|
954
|
+
]
|
|
955
|
+
};
|
|
956
|
+
const uiVariants = createPlugin.withOptions((options)=>({ matchVariant })=>{
|
|
957
|
+
options = options ?? {};
|
|
958
|
+
const resolvedPrefixes = normalizePrefixes(options?.prefixes);
|
|
959
|
+
const entries = Object.entries(resolvedPrefixes);
|
|
960
|
+
for (const [prefix, states] of entries){
|
|
961
|
+
const stateEntries = Array.isArray(states) ? states.map((k)=>[
|
|
962
|
+
k,
|
|
963
|
+
k
|
|
964
|
+
]) : Object.entries(states);
|
|
965
|
+
const valueMap = Object.fromEntries(stateEntries);
|
|
966
|
+
matchVariant(prefix, (value, { modifier } = {})=>{
|
|
967
|
+
const mapped = valueMap[value];
|
|
968
|
+
if (!mapped || 'string' != typeof mapped) return '';
|
|
969
|
+
const selector = `&.${prefix}-${mapped}`;
|
|
970
|
+
return modifier && 'string' == typeof modifier ? `${selector}\\/${modifier}` : selector;
|
|
971
|
+
}, {
|
|
972
|
+
values: valueMap
|
|
973
|
+
});
|
|
974
|
+
}
|
|
975
|
+
});
|
|
976
|
+
function normalizePrefixes(input) {
|
|
977
|
+
if ('function' == typeof input) return input(DEFAULT_PREFIXES);
|
|
978
|
+
if (Array.isArray(input)) return Object.fromEntries(input.map((prefix)=>[
|
|
979
|
+
prefix,
|
|
980
|
+
DEFAULT_PREFIXES[prefix] ?? []
|
|
981
|
+
]));
|
|
982
|
+
return input ?? {
|
|
983
|
+
ui: DEFAULT_PREFIXES.ui
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
const LYNX_UI_PLUGIN_ENTRIES = [
|
|
987
|
+
[
|
|
988
|
+
'uiVariants',
|
|
989
|
+
uiVariants
|
|
990
|
+
]
|
|
991
|
+
];
|
|
992
|
+
const LYNX_UI_PLUGIN_MAP = Object.fromEntries(LYNX_UI_PLUGIN_ENTRIES);
|
|
993
|
+
const ORDERED_LYNX_UI_PLUGIN_NAMES = LYNX_UI_PLUGIN_ENTRIES.map(([n])=>n);
|
|
932
994
|
const DEFAULT_CORE_PLUGINS = [
|
|
933
995
|
'animation',
|
|
934
996
|
'aspectRatio',
|
|
@@ -997,6 +1059,39 @@ function toEnabledSet(opt = true) {
|
|
|
997
1059
|
else if (true === on) set.add(k);
|
|
998
1060
|
return set;
|
|
999
1061
|
}
|
|
1062
|
+
function toEnabledLynxUIPluginSet(opt = true) {
|
|
1063
|
+
if (true === opt) return new Set(ORDERED_LYNX_UI_PLUGIN_NAMES);
|
|
1064
|
+
if (false === opt) return new Set();
|
|
1065
|
+
if (Array.isArray(opt)) return new Set(opt);
|
|
1066
|
+
const set = new Set(ORDERED_LYNX_UI_PLUGIN_NAMES);
|
|
1067
|
+
for (const [k, on] of Object.entries(opt))if (false === on) set.delete(k);
|
|
1068
|
+
else if (true === on) set.add(k);
|
|
1069
|
+
return set;
|
|
1070
|
+
}
|
|
1071
|
+
function resolveUIPluginEntries(raw) {
|
|
1072
|
+
if (false === raw) return [];
|
|
1073
|
+
if (true === raw) return ORDERED_LYNX_UI_PLUGIN_NAMES.map((n)=>[
|
|
1074
|
+
n,
|
|
1075
|
+
{}
|
|
1076
|
+
]);
|
|
1077
|
+
if (Array.isArray(raw)) return ORDERED_LYNX_UI_PLUGIN_NAMES.filter((n)=>raw.includes(n)).map((n)=>[
|
|
1078
|
+
n,
|
|
1079
|
+
{}
|
|
1080
|
+
]);
|
|
1081
|
+
const out = [];
|
|
1082
|
+
for (const name of ORDERED_LYNX_UI_PLUGIN_NAMES){
|
|
1083
|
+
const val = raw[name];
|
|
1084
|
+
if (false !== val) if (true === val || void 0 === val) out.push([
|
|
1085
|
+
name,
|
|
1086
|
+
{}
|
|
1087
|
+
]);
|
|
1088
|
+
else out.push([
|
|
1089
|
+
name,
|
|
1090
|
+
val
|
|
1091
|
+
]);
|
|
1092
|
+
}
|
|
1093
|
+
return out;
|
|
1094
|
+
}
|
|
1000
1095
|
const lynxTheme = {
|
|
1001
1096
|
boxShadow: {
|
|
1002
1097
|
sm: '0 1px 2px 0 rgba(0, 0, 0, 0.05)',
|
|
@@ -1093,18 +1188,24 @@ const lynxTheme = {
|
|
|
1093
1188
|
}
|
|
1094
1189
|
}
|
|
1095
1190
|
};
|
|
1096
|
-
function createLynxPreset({ lynxPlugins = true, debug = false, theme } = {}) {
|
|
1097
|
-
const
|
|
1191
|
+
function createLynxPreset({ lynxPlugins = true, lynxUIPlugins = false, debug = false, theme } = {}) {
|
|
1192
|
+
const coreSetEnabled = toEnabledSet(lynxPlugins);
|
|
1193
|
+
const uiSetEnabled = toEnabledLynxUIPluginSet(lynxUIPlugins);
|
|
1098
1194
|
const defaultPluginName = 'defaults';
|
|
1099
1195
|
const plugins = [
|
|
1100
1196
|
plugin_registry_LYNX_PLUGIN_MAP[defaultPluginName]
|
|
1101
1197
|
];
|
|
1102
1198
|
for (const name of ORDERED_LYNX_PLUGIN_NAMES)if ('defaults' !== name) {
|
|
1103
|
-
if (
|
|
1199
|
+
if (coreSetEnabled.has(name)) {
|
|
1104
1200
|
plugins.push(plugin_registry_LYNX_PLUGIN_MAP[name]);
|
|
1105
|
-
if (debug) console.debug(`[Lynx] enabled plugin: ${name}`);
|
|
1201
|
+
if (debug) console.debug(`[Lynx] enabled core plugin: ${name}`);
|
|
1106
1202
|
}
|
|
1107
1203
|
}
|
|
1204
|
+
for (const [name, options] of resolveUIPluginEntries(lynxUIPlugins))if (uiSetEnabled.has(name)) {
|
|
1205
|
+
const fn = LYNX_UI_PLUGIN_MAP[name];
|
|
1206
|
+
plugins.push(fn(options));
|
|
1207
|
+
if (debug) console.debug(`[Lynx] enabled UI plugin: ${name}`);
|
|
1208
|
+
}
|
|
1108
1209
|
return {
|
|
1109
1210
|
plugins,
|
|
1110
1211
|
corePlugins: DEFAULT_CORE_PLUGINS,
|
package/dist/lynx.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { Config } from 'tailwindcss';
|
|
2
|
-
import type { LynxPluginName, LynxPluginsOption } from './core.js';
|
|
2
|
+
import type { LynxPluginName, LynxPluginsOption, LynxUIPluginsOption } from './core.js';
|
|
3
3
|
/**
|
|
4
4
|
* Should be used with Tailwind v3+ (JIT is enabled by default) and configured with `content`,
|
|
5
5
|
* otherwise the generated CSS bundle may include unused utilities.
|
|
6
6
|
*/
|
|
7
|
-
declare function createLynxPreset({ lynxPlugins, debug, theme, }?: {
|
|
7
|
+
declare function createLynxPreset({ lynxPlugins, lynxUIPlugins, debug, theme, }?: {
|
|
8
8
|
lynxPlugins?: LynxPluginsOption;
|
|
9
|
+
lynxUIPlugins?: LynxUIPluginsOption;
|
|
9
10
|
debug?: boolean;
|
|
10
11
|
theme?: Config['theme'];
|
|
11
12
|
}): Partial<Config>;
|
package/dist/lynx.js
CHANGED
|
@@ -1,28 +1,27 @@
|
|
|
1
1
|
import createUtilityPlugin from "tailwindcss/lib/util/createUtilityPlugin.js";
|
|
2
2
|
import "tailwindcss/lib/util/parseBoxShadowValue.js";
|
|
3
3
|
import transformThemeValue from "tailwindcss/lib/util/transformThemeValue.js";
|
|
4
|
-
function
|
|
4
|
+
function createPluginImpl(fn, cfg) {
|
|
5
5
|
return {
|
|
6
6
|
handler: (api)=>fn(autoBind(api)),
|
|
7
7
|
config: cfg
|
|
8
8
|
};
|
|
9
9
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
createPluginImpl.withOptions = withOptions;
|
|
11
|
+
const createPlugin = createPluginImpl;
|
|
12
|
+
function withOptions(pluginFn, configFn = ()=>({})) {
|
|
13
|
+
const optionsFunction = function(options) {
|
|
14
|
+
return {
|
|
15
|
+
__options: options,
|
|
16
|
+
handler: (api)=>pluginFn(options)(autoBind(api)),
|
|
17
|
+
config: configFn(options)
|
|
18
|
+
};
|
|
14
19
|
};
|
|
20
|
+
optionsFunction.__isOptionsFunction = true;
|
|
21
|
+
optionsFunction.__pluginFunction = pluginFn;
|
|
22
|
+
optionsFunction.__configFunction = configFn;
|
|
23
|
+
return optionsFunction;
|
|
15
24
|
}
|
|
16
|
-
function pluginImpl(pluginFn, cfg) {
|
|
17
|
-
const wrapped = (api)=>pluginFn(autoBind(api));
|
|
18
|
-
return basePlugin(wrapped, cfg);
|
|
19
|
-
}
|
|
20
|
-
function withOptions(factory, cfgFactory) {
|
|
21
|
-
const optionsFn = (options)=>basePlugin((api)=>factory(options)(autoBind(api)), cfgFactory?.(options));
|
|
22
|
-
optionsFn.__isOptionsFunction = true;
|
|
23
|
-
return optionsFn;
|
|
24
|
-
}
|
|
25
|
-
pluginImpl.withOptions = withOptions;
|
|
26
25
|
function autoBind(obj) {
|
|
27
26
|
return Object.fromEntries(Object.entries(obj).map(([k, v])=>isFunction(v) ? [
|
|
28
27
|
k,
|
|
@@ -889,6 +888,69 @@ const LYNX_PLUGIN_ENTRIES = [
|
|
|
889
888
|
const plugin_registry_LYNX_PLUGIN_MAP = Object.fromEntries(LYNX_PLUGIN_ENTRIES);
|
|
890
889
|
const ORDERED_LYNX_PLUGIN_NAMES = LYNX_PLUGIN_ENTRIES.map(([n])=>n);
|
|
891
890
|
const plugin_registry_REPLACEABLE_LYNX_PLUGINS = ORDERED_LYNX_PLUGIN_NAMES.filter((n)=>'defaults' !== n);
|
|
891
|
+
const DEFAULT_PREFIXES = {
|
|
892
|
+
ui: [
|
|
893
|
+
'active',
|
|
894
|
+
'disabled',
|
|
895
|
+
'readonly',
|
|
896
|
+
'checked',
|
|
897
|
+
'selected',
|
|
898
|
+
'open',
|
|
899
|
+
'leaving',
|
|
900
|
+
'entering',
|
|
901
|
+
'animating',
|
|
902
|
+
'busy'
|
|
903
|
+
],
|
|
904
|
+
'ui-side': [
|
|
905
|
+
'left',
|
|
906
|
+
'right',
|
|
907
|
+
'top',
|
|
908
|
+
'bottom'
|
|
909
|
+
],
|
|
910
|
+
'ui-align': [
|
|
911
|
+
'start',
|
|
912
|
+
'end',
|
|
913
|
+
'center'
|
|
914
|
+
]
|
|
915
|
+
};
|
|
916
|
+
const uiVariants = createPlugin.withOptions((options)=>({ matchVariant })=>{
|
|
917
|
+
options = options ?? {};
|
|
918
|
+
const resolvedPrefixes = normalizePrefixes(options?.prefixes);
|
|
919
|
+
const entries = Object.entries(resolvedPrefixes);
|
|
920
|
+
for (const [prefix, states] of entries){
|
|
921
|
+
const stateEntries = Array.isArray(states) ? states.map((k)=>[
|
|
922
|
+
k,
|
|
923
|
+
k
|
|
924
|
+
]) : Object.entries(states);
|
|
925
|
+
const valueMap = Object.fromEntries(stateEntries);
|
|
926
|
+
matchVariant(prefix, (value, { modifier } = {})=>{
|
|
927
|
+
const mapped = valueMap[value];
|
|
928
|
+
if (!mapped || 'string' != typeof mapped) return '';
|
|
929
|
+
const selector = `&.${prefix}-${mapped}`;
|
|
930
|
+
return modifier && 'string' == typeof modifier ? `${selector}\\/${modifier}` : selector;
|
|
931
|
+
}, {
|
|
932
|
+
values: valueMap
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
});
|
|
936
|
+
function normalizePrefixes(input) {
|
|
937
|
+
if ('function' == typeof input) return input(DEFAULT_PREFIXES);
|
|
938
|
+
if (Array.isArray(input)) return Object.fromEntries(input.map((prefix)=>[
|
|
939
|
+
prefix,
|
|
940
|
+
DEFAULT_PREFIXES[prefix] ?? []
|
|
941
|
+
]));
|
|
942
|
+
return input ?? {
|
|
943
|
+
ui: DEFAULT_PREFIXES.ui
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
const LYNX_UI_PLUGIN_ENTRIES = [
|
|
947
|
+
[
|
|
948
|
+
'uiVariants',
|
|
949
|
+
uiVariants
|
|
950
|
+
]
|
|
951
|
+
];
|
|
952
|
+
const LYNX_UI_PLUGIN_MAP = Object.fromEntries(LYNX_UI_PLUGIN_ENTRIES);
|
|
953
|
+
const ORDERED_LYNX_UI_PLUGIN_NAMES = LYNX_UI_PLUGIN_ENTRIES.map(([n])=>n);
|
|
892
954
|
const DEFAULT_CORE_PLUGINS = [
|
|
893
955
|
'animation',
|
|
894
956
|
'aspectRatio',
|
|
@@ -957,6 +1019,39 @@ function toEnabledSet(opt = true) {
|
|
|
957
1019
|
else if (true === on) set.add(k);
|
|
958
1020
|
return set;
|
|
959
1021
|
}
|
|
1022
|
+
function toEnabledLynxUIPluginSet(opt = true) {
|
|
1023
|
+
if (true === opt) return new Set(ORDERED_LYNX_UI_PLUGIN_NAMES);
|
|
1024
|
+
if (false === opt) return new Set();
|
|
1025
|
+
if (Array.isArray(opt)) return new Set(opt);
|
|
1026
|
+
const set = new Set(ORDERED_LYNX_UI_PLUGIN_NAMES);
|
|
1027
|
+
for (const [k, on] of Object.entries(opt))if (false === on) set.delete(k);
|
|
1028
|
+
else if (true === on) set.add(k);
|
|
1029
|
+
return set;
|
|
1030
|
+
}
|
|
1031
|
+
function resolveUIPluginEntries(raw) {
|
|
1032
|
+
if (false === raw) return [];
|
|
1033
|
+
if (true === raw) return ORDERED_LYNX_UI_PLUGIN_NAMES.map((n)=>[
|
|
1034
|
+
n,
|
|
1035
|
+
{}
|
|
1036
|
+
]);
|
|
1037
|
+
if (Array.isArray(raw)) return ORDERED_LYNX_UI_PLUGIN_NAMES.filter((n)=>raw.includes(n)).map((n)=>[
|
|
1038
|
+
n,
|
|
1039
|
+
{}
|
|
1040
|
+
]);
|
|
1041
|
+
const out = [];
|
|
1042
|
+
for (const name of ORDERED_LYNX_UI_PLUGIN_NAMES){
|
|
1043
|
+
const val = raw[name];
|
|
1044
|
+
if (false !== val) if (true === val || void 0 === val) out.push([
|
|
1045
|
+
name,
|
|
1046
|
+
{}
|
|
1047
|
+
]);
|
|
1048
|
+
else out.push([
|
|
1049
|
+
name,
|
|
1050
|
+
val
|
|
1051
|
+
]);
|
|
1052
|
+
}
|
|
1053
|
+
return out;
|
|
1054
|
+
}
|
|
960
1055
|
const lynxTheme = {
|
|
961
1056
|
boxShadow: {
|
|
962
1057
|
sm: '0 1px 2px 0 rgba(0, 0, 0, 0.05)',
|
|
@@ -1053,18 +1148,24 @@ const lynxTheme = {
|
|
|
1053
1148
|
}
|
|
1054
1149
|
}
|
|
1055
1150
|
};
|
|
1056
|
-
function createLynxPreset({ lynxPlugins = true, debug = false, theme } = {}) {
|
|
1057
|
-
const
|
|
1151
|
+
function createLynxPreset({ lynxPlugins = true, lynxUIPlugins = false, debug = false, theme } = {}) {
|
|
1152
|
+
const coreSetEnabled = toEnabledSet(lynxPlugins);
|
|
1153
|
+
const uiSetEnabled = toEnabledLynxUIPluginSet(lynxUIPlugins);
|
|
1058
1154
|
const defaultPluginName = 'defaults';
|
|
1059
1155
|
const plugins = [
|
|
1060
1156
|
plugin_registry_LYNX_PLUGIN_MAP[defaultPluginName]
|
|
1061
1157
|
];
|
|
1062
1158
|
for (const name of ORDERED_LYNX_PLUGIN_NAMES)if ('defaults' !== name) {
|
|
1063
|
-
if (
|
|
1159
|
+
if (coreSetEnabled.has(name)) {
|
|
1064
1160
|
plugins.push(plugin_registry_LYNX_PLUGIN_MAP[name]);
|
|
1065
|
-
if (debug) console.debug(`[Lynx] enabled plugin: ${name}`);
|
|
1161
|
+
if (debug) console.debug(`[Lynx] enabled core plugin: ${name}`);
|
|
1066
1162
|
}
|
|
1067
1163
|
}
|
|
1164
|
+
for (const [name, options] of resolveUIPluginEntries(lynxUIPlugins))if (uiSetEnabled.has(name)) {
|
|
1165
|
+
const fn = LYNX_UI_PLUGIN_MAP[name];
|
|
1166
|
+
plugins.push(fn(options));
|
|
1167
|
+
if (debug) console.debug(`[Lynx] enabled UI plugin: ${name}`);
|
|
1168
|
+
}
|
|
1068
1169
|
return {
|
|
1069
1170
|
plugins,
|
|
1070
1171
|
corePlugins: DEFAULT_CORE_PLUGINS,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { uiVariants } from './uiVariants.js';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { UIVariantsOptions } from './uiVariants.js';
|
|
2
|
+
import type { PluginWithOptions } from '../../helpers.js';
|
|
3
|
+
type Entry<K extends LynxUIPluginName> = readonly [
|
|
4
|
+
K,
|
|
5
|
+
PluginWithOptions<LynxUIPluginOptionsMap[K]>
|
|
6
|
+
];
|
|
7
|
+
interface LynxUIPluginOptionsMap {
|
|
8
|
+
uiVariants: UIVariantsOptions;
|
|
9
|
+
}
|
|
10
|
+
type LynxUIPluginName = keyof LynxUIPluginOptionsMap;
|
|
11
|
+
export declare const LYNX_UI_PLUGIN_ENTRIES: readonly [
|
|
12
|
+
Entry<'uiVariants'>
|
|
13
|
+
];
|
|
14
|
+
type EntryUnion = typeof LYNX_UI_PLUGIN_ENTRIES[number];
|
|
15
|
+
type PluginMap = {
|
|
16
|
+
[K in EntryUnion[0]]: Extract<EntryUnion, readonly [K, any]>[1];
|
|
17
|
+
};
|
|
18
|
+
export declare const LYNX_UI_PLUGIN_MAP: PluginMap;
|
|
19
|
+
export declare const ORDERED_LYNX_UI_PLUGIN_NAMES: readonly LynxUIPluginName[];
|
|
20
|
+
export type { LynxUIPluginName, LynxUIPluginOptionsMap };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { PluginWithOptions } from '../../helpers.js';
|
|
2
|
+
import type { KeyValuePairOrList } from '../../types/plugin-types.js';
|
|
3
|
+
declare const DEFAULT_PREFIXES: {
|
|
4
|
+
readonly ui: readonly ["active", "disabled", "readonly", "checked", "selected", "open", "leaving", "entering", "animating", "busy"];
|
|
5
|
+
readonly 'ui-side': readonly ["left", "right", "top", "bottom"];
|
|
6
|
+
readonly 'ui-align': readonly ["start", "end", "center"];
|
|
7
|
+
};
|
|
8
|
+
type DefaultPrefixMap = typeof DEFAULT_PREFIXES;
|
|
9
|
+
type PrefixConfig = string[] | Record<string, KeyValuePairOrList> | ((defaults: DefaultPrefixMap) => Record<string, KeyValuePairOrList>);
|
|
10
|
+
interface UIVariantsOptions {
|
|
11
|
+
/**
|
|
12
|
+
* Configures state-based variant prefixes.
|
|
13
|
+
*
|
|
14
|
+
* You can provide:
|
|
15
|
+
* - An array of prefixes to use their default states
|
|
16
|
+
* - Or an object mapping each prefix to an array or map of custom states.
|
|
17
|
+
* - An explicit object of prefix → values (array or map)
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* prefixes: ['ui'] // → `ui-checked:*`, `ui-open:*` using default states
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* prefixes: {
|
|
24
|
+
* ui: ['checked', 'open'],
|
|
25
|
+
* aria: { expanded: 'expanded', pressed: 'pressed' },
|
|
26
|
+
* }
|
|
27
|
+
*/
|
|
28
|
+
prefixes?: PrefixConfig;
|
|
29
|
+
}
|
|
30
|
+
declare const uiVariants: PluginWithOptions<UIVariantsOptions>;
|
|
31
|
+
export type { UIVariantsOptions };
|
|
32
|
+
export { uiVariants };
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { Config, PluginAPI, PluginCreator, ValueType } from './tailwind-types.js';
|
|
2
|
+
/** A flat structure of possible variant values, either list or key-value form. */
|
|
3
|
+
type KeyValuePairOrList = Record<string, string> | string[] | ReadonlyArray<string>;
|
|
2
4
|
/** Anything that is legal on the right-hand side of a CSS-in-JS object. */
|
|
3
5
|
type CSSStatic = string | number | string[] | Record<string, unknown> | null | undefined;
|
|
4
6
|
/**
|
|
@@ -27,24 +29,16 @@ type Bound<T> = {
|
|
|
27
29
|
[K in keyof T]: T[K] extends (...args: infer _A) => infer _R ? OmitThisParameter<T[K]> : T[K];
|
|
28
30
|
};
|
|
29
31
|
type BoundedPluginCreator = (api: Bound<PluginAPI>) => void;
|
|
30
|
-
interface
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
interface PluginWithConfig {
|
|
33
|
+
handler: PluginCreator;
|
|
34
|
+
config?: Partial<Config> | undefined;
|
|
35
|
+
}
|
|
36
|
+
interface PluginWithOptions<T> {
|
|
37
|
+
(options?: T): PluginWithConfig;
|
|
35
38
|
__isOptionsFunction: true;
|
|
36
39
|
}
|
|
37
|
-
interface
|
|
38
|
-
(pluginFn: BoundedPluginCreator, cfg?: Partial<Config>):
|
|
39
|
-
|
|
40
|
-
config?: Partial<Config> | undefined;
|
|
41
|
-
};
|
|
42
|
-
withOptions<T>(factory: (opts: T) => BoundedPluginCreator, cfgFactory?: (opts: T) => Partial<Config>): {
|
|
43
|
-
(opts: T): {
|
|
44
|
-
handler: PluginCreator;
|
|
45
|
-
config?: Partial<Config> | undefined;
|
|
46
|
-
};
|
|
47
|
-
__isOptionsFunction: true;
|
|
48
|
-
};
|
|
40
|
+
interface CreatePluginFunction {
|
|
41
|
+
(pluginFn: BoundedPluginCreator, cfg?: Partial<Config>): PluginWithConfig;
|
|
42
|
+
withOptions<T>(pluginFn: (options?: T) => BoundedPluginCreator, configFn?: (options?: T) => Partial<Config>): PluginWithOptions<T>;
|
|
49
43
|
}
|
|
50
|
-
export type { CSSStatic, PropertyEntry, UtilityEntry, UtilityGroup, UtilityVariations, UtilityPluginOptions, BoundedPluginCreator,
|
|
44
|
+
export type { CSSStatic, KeyValuePairOrList, PropertyEntry, UtilityEntry, UtilityGroup, UtilityVariations, UtilityPluginOptions, BoundedPluginCreator, Bound, CreatePluginFunction, PluginWithConfig, PluginWithOptions, };
|