@kalink-ui/seedly 0.29.0 → 0.30.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 CHANGED
@@ -1,5 +1,55 @@
1
1
  # @kalink-ui/seedly
2
2
 
3
+ ## 0.30.0
4
+
5
+ ### Minor Changes
6
+
7
+ - e257be4: feat(styles): add dynamic fluid scale helpers
8
+
9
+ New
10
+ - `getInterpolationFor(value, options)`: per-value exponential mapping from a mobile range to a desktop range.
11
+ - `toFluidClamp([min, max], options)`: build a viewport-based CSS `clamp()` with `interpolateFrom`/`interpolateTo` (defaults `23.5` and `80`).
12
+ - `toFluidClampFor(value, options)`: convenience to compute the mapped max and format a `clamp()` in one step.
13
+
14
+ Updates
15
+ - Storybook theme now uses the new helpers and no longer depends on an interpolation variable.
16
+
17
+ Usage
18
+
19
+ ```ts
20
+ import {
21
+ getInterpolationFor,
22
+ toFluidClampFor,
23
+ } from '@kalink-ui/seedly/styles';
24
+
25
+ const mapped = getInterpolationFor(12, {
26
+ lowMin: 12,
27
+ lowMax: 64,
28
+ highMin: 12,
29
+ highMax: 200,
30
+ exponent: 2,
31
+ rounding: 'none',
32
+ });
33
+
34
+ const clamp = toFluidClampFor(12, {
35
+ lowMin: 12,
36
+ lowMax: 64,
37
+ highMin: 12,
38
+ highMax: 200,
39
+ exponent: 2,
40
+ rounding: 'none',
41
+ // optional overrides
42
+ interpolateFrom: 23.5,
43
+ interpolateTo: 80,
44
+ });
45
+ ```
46
+
47
+ ## 0.29.1
48
+
49
+ ### Patch Changes
50
+
51
+ - 174eb5b: Correctly apply auto layout when defined
52
+
3
53
  ## 0.29.0
4
54
 
5
55
  ### Minor Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kalink-ui/seedly",
3
- "version": "0.29.0",
3
+ "version": "0.30.0",
4
4
  "description": "A set of components for building UIs with React and TypeScript",
5
5
  "sideEffects": false,
6
6
  "license": "MIT",
@@ -43,8 +43,8 @@
43
43
  "vite": "^6.3.5",
44
44
  "vite-tsconfig-paths": "^5.1.4",
45
45
  "vitest": "^3.2.3",
46
- "@kalink-ui/eslint-config": "0.10.0",
47
- "@kalink-ui/typescript-config": "0.4.0"
46
+ "@kalink-ui/typescript-config": "0.4.0",
47
+ "@kalink-ui/eslint-config": "0.10.0"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "@vanilla-extract/css": "^1.17.1",
@@ -172,7 +172,7 @@ const gridColumnsStyles = Object.fromEntries(
172
172
  { '@layer': Record<string, { gridTemplateColumns: string }> }
173
173
  >;
174
174
 
175
- export const gridFitStyles = {
175
+ export const autoLayoutStyles = {
176
176
  fill: {
177
177
  '@layer': {
178
178
  [components]: {
@@ -226,7 +226,7 @@ export const gridRecipe = recipe({
226
226
  /**
227
227
  * Whether to use auto-fill (default) or auto-fit
228
228
  */
229
- fit: gridFitStyles,
229
+ autoLayout: autoLayoutStyles,
230
230
 
231
231
  /**
232
232
  * Grid item alignment along inline axis
@@ -274,8 +274,8 @@ export const columnsAt = createResponsiveVariants({
274
274
  media: defaultMedia,
275
275
  });
276
276
 
277
- export const fitAt = createResponsiveVariants({
278
- styles: gridFitStyles,
277
+ export const autoLayoutAt = createResponsiveVariants({
278
+ styles: autoLayoutStyles,
279
279
  media: defaultMedia,
280
280
  });
281
281
 
@@ -4,7 +4,7 @@ import {
4
4
  alignContentAt,
5
5
  alignItemsAt,
6
6
  columnsAt,
7
- fitAt,
7
+ autoLayoutAt,
8
8
  gridRecipe,
9
9
  justifyContentAt,
10
10
  justifyItemsAt,
@@ -20,7 +20,7 @@ export const gridResponsive = responsiveRecipe({
20
20
  columnSpacing: columnSpacingAt,
21
21
  rowSpacing: rowSpacingAt,
22
22
  columns: columnsAt,
23
- fit: fitAt,
23
+ autoLayout: autoLayoutAt,
24
24
  justifyItems: justifyItemsAt,
25
25
  alignItems: alignItemsAt,
26
26
  justifyContent: justifyContentAt,
@@ -30,18 +30,16 @@ type GridProps<TUse extends ElementType> = PolymorphicComponentProps<TUse> &
30
30
  *
31
31
  * https://every-layout.dev/layouts/grid/
32
32
  */
33
- export function Grid<TUse extends ElementType>({
34
- minSize,
35
- className,
36
- ...props
37
- }: GridProps<TUse>) {
33
+ export function Grid<TUse extends ElementType>(props: GridProps<TUse>) {
38
34
  const {
39
35
  use: Comp = 'div',
36
+ minSize,
37
+ className,
40
38
  spacing,
41
39
  columnSpacing,
42
40
  rowSpacing,
43
41
  columns = { xs: 4, md: 8, lg: 12 },
44
- fit,
42
+ autoLayout,
45
43
  justifyItems,
46
44
  alignItems,
47
45
  justifyContent,
@@ -56,8 +54,8 @@ export function Grid<TUse extends ElementType>({
56
54
  spacing,
57
55
  columnSpacing,
58
56
  rowSpacing,
59
- columns,
60
- fit,
57
+ columns: autoLayout ? undefined : columns,
58
+ autoLayout,
61
59
  justifyItems,
62
60
  alignItems,
63
61
  justifyContent,
@@ -34,3 +34,13 @@ export {
34
34
  } from './responsive';
35
35
 
36
36
  export { breakpoints, screen, type BreakpointKey } from './breakpoints';
37
+
38
+ export {
39
+ getInterpolationFor,
40
+ toFluidClamp,
41
+ toFluidClampFor,
42
+ type Interval,
43
+ type ExponentialScaleOptions,
44
+ type DynamicInterpolationOptions,
45
+ type FluidClampOptions,
46
+ } from './scale';
@@ -0,0 +1,149 @@
1
+ export type Interval = [number, number];
2
+
3
+ /**
4
+ * Common easing/rounding options used by the interpolation helpers.
5
+ * Note: `minStep` is ignored in single-value mapping but kept for backward
6
+ * compatibility of the public types.
7
+ */
8
+ export interface ExponentialScaleOptions {
9
+ exponent?: number; // 1 = linear, >1 = ease-in, <1 = ease-out
10
+ minStep?: number; // kept for compatibility; not used by per-value mapping
11
+ rounding?: 'none' | 'round' | 'floor' | 'ceil';
12
+ }
13
+
14
+ export interface DynamicInterpolationOptions extends ExponentialScaleOptions {
15
+ lowMin: number;
16
+ lowMax: number;
17
+ highMin: number;
18
+ highMax: number;
19
+ clampInput?: boolean; // clamp input inside [lowMin, lowMax]
20
+ ensureGteInput?: boolean; // ensure output >= input
21
+ }
22
+
23
+ export interface FluidClampOptions {
24
+ unit?: 'rem' | 'px';
25
+ baseFontSize?: number; // px per rem when unit is 'rem'
26
+ interpolateFrom?: number; // defaults to 23.5 (in `unit`)
27
+ interpolateTo?: number; // defaults to 80 (in `unit`)
28
+ }
29
+
30
+ /**
31
+ * Format a `[min, max]` pair into a CSS clamp() with viewport-based interpolation:
32
+ * clamp(min, calc(min + (max - min) * ((100vw - from) / (to - from))), max)
33
+ *
34
+ * - `from`/`to` default to 23.5 and 80 (in the selected `unit`).
35
+ * - `unit` may be 'rem' (default) or 'px'. When 'rem', values are converted from px
36
+ * using `baseFontSize` (default 16).
37
+ */
38
+ export function toFluidClamp(
39
+ [min, max]: Interval,
40
+ options: FluidClampOptions = {},
41
+ ): string {
42
+ const unit = options.unit ?? 'rem';
43
+ const base = options.baseFontSize ?? 16;
44
+ const from = options.interpolateFrom ?? 23.5;
45
+ const to = options.interpolateTo ?? 80;
46
+
47
+ const toUnit = (px: number): number => {
48
+ if (unit === 'px') {
49
+ return px;
50
+ }
51
+
52
+ return px / base;
53
+ };
54
+
55
+ const minU = toUnit(min);
56
+ const maxU = toUnit(max);
57
+ const diffU = maxU - minU;
58
+
59
+ const interp = `((100vw - ${from}${unit}) / (${to} - ${from}))`;
60
+
61
+ return `clamp(${minU}${unit}, calc(${minU}${unit} + (${diffU} * ${interp})), ${maxU}${unit})`;
62
+ }
63
+
64
+ // Batch scale helpers were removed in favor of per-value mapping.
65
+
66
+ /**
67
+ * Map a single `value` from [lowMin, lowMax] to [highMin, highMax] using
68
+ * exponential easing. Optionally rounds, clamps input, and ensures the output
69
+ * is not below the input (enabled by default).
70
+ */
71
+ export function getInterpolationFor(
72
+ value: number,
73
+ {
74
+ lowMin,
75
+ lowMax,
76
+ highMin,
77
+ highMax,
78
+ exponent = 2,
79
+ rounding = 'round',
80
+ clampInput = true,
81
+ ensureGteInput = true,
82
+ }: DynamicInterpolationOptions,
83
+ ): number {
84
+ if (lowMax === lowMin) {
85
+ throw new Error('getInterpolationFor: lowMin and lowMax must differ.');
86
+ }
87
+
88
+ const clamp = (v: number, min: number, max: number): number => {
89
+ if (v < min) {
90
+ return min;
91
+ }
92
+
93
+ if (v > max) {
94
+ return max;
95
+ }
96
+
97
+ return v;
98
+ };
99
+
100
+ const round = (v: number): number => {
101
+ if (rounding === 'none') {
102
+ return v;
103
+ }
104
+
105
+ if (rounding === 'floor') {
106
+ return Math.floor(v);
107
+ }
108
+
109
+ if (rounding === 'ceil') {
110
+ return Math.ceil(v);
111
+ }
112
+
113
+ return Math.round(v);
114
+ };
115
+
116
+ const input = clampInput ? clamp(value, lowMin, lowMax) : value;
117
+
118
+ const t = (input - lowMin) / (lowMax - lowMin);
119
+ const eased = Math.pow(t, exponent);
120
+ const mapped = highMin + eased * (highMax - highMin);
121
+
122
+ let result = round(mapped);
123
+
124
+ if (ensureGteInput && result < input) {
125
+ result = input;
126
+ }
127
+
128
+ return result;
129
+ }
130
+
131
+ export interface FluidClampForOptions
132
+ extends DynamicInterpolationOptions,
133
+ FluidClampOptions {}
134
+
135
+ /**
136
+ * Convenience to produce a CSS clamp() for a single `value` by first computing
137
+ * its mapped high value via `getInterpolationFor`, then formatting with
138
+ * `toFluidClamp` using viewport interpolation.
139
+ */
140
+ export function toFluidClampFor(
141
+ value: number,
142
+ { unit = 'rem', baseFontSize = 16, ...opts }: FluidClampForOptions,
143
+ ): string {
144
+ return toFluidClamp([value, getInterpolationFor(value, opts)], {
145
+ unit,
146
+ baseFontSize,
147
+ ...opts,
148
+ });
149
+ }