@flow-os/style 0.0.67-dev.1772065615 → 0.0.68

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flow-os/style",
3
- "version": "0.0.67-dev.1772065615",
3
+ "version": "0.0.68",
4
4
  "license": "PolyForm-Shield-1.0.0",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  import { resolveS } from '../s';
7
+ import { parseBaseShorthand } from '../styleFlow/baseShorthand';
7
8
  import type { SValue } from '../../types';
8
9
  import type { StyleFlowTheme } from '../styleFlow';
9
10
 
@@ -12,6 +13,12 @@ export interface StyleProps {
12
13
  styleFlow?: StyleFlowTheme | string;
13
14
  }
14
15
 
16
+ function applyStyle(obj: Record<string, string>, el: HTMLElement): void {
17
+ for (const [k, v] of Object.entries(obj)) {
18
+ (el.style as unknown as Record<string, string>)[k] = v;
19
+ }
20
+ }
21
+
15
22
  export function applyStyleProps(el: HTMLElement, props: StyleProps): void {
16
23
  const { s, styleFlow } = props;
17
24
 
@@ -21,18 +28,22 @@ export function applyStyleProps(el: HTMLElement, props: StyleProps): void {
21
28
  const existing = el.className ? `${el.className} ` : '';
22
29
  el.className = `${existing}${resolved.className}`;
23
30
  }
24
- if (resolved.style) {
25
- for (const [k, v] of Object.entries(resolved.style)) {
26
- (el.style as unknown as Record<string, string>)[k] = v;
27
- }
28
- }
31
+ if (resolved.style) applyStyle(resolved.style, el);
29
32
  }
30
33
 
31
34
  if (styleFlow != null) {
32
35
  if (typeof styleFlow === 'string') {
33
36
  el.setAttribute('data-style-flow', styleFlow);
34
37
  } else if (typeof styleFlow === 'object') {
35
- el.setAttribute('data-style-flow', JSON.stringify(styleFlow));
38
+ const base = styleFlow.base;
39
+ if (typeof base === 'string') {
40
+ applyStyle(parseBaseShorthand(base), el);
41
+ }
42
+ const rest = { ...styleFlow };
43
+ delete rest.base;
44
+ if (Object.keys(rest).length > 0) {
45
+ el.setAttribute('data-style-flow', JSON.stringify(rest));
46
+ }
36
47
  }
37
48
  }
38
49
  }
@@ -1,23 +1,75 @@
1
1
  /**
2
2
  * `s` - shorthand for applying styles to elements.
3
- * Accepts: string (class names) or object (inline CSS).
3
+ * Accepts:
4
+ * - string: class names + inline breakpoints mob:(...) tab:(...) des:(...)
5
+ * - object: inline CSS or { base, mob, tab, des } for responsive classes
4
6
  */
5
7
 
6
- import type { SResult } from './types';
8
+ import type { SResult, SValue } from './types';
7
9
 
8
10
  export type { SResult, SValue } from './types';
9
11
 
12
+ const BP_RE = /(mob|tab|des):\(([^)]*)\)/g;
13
+ const BP_MAP: Record<string, string> = { mob: 'max-sm', tab: 'md', des: 'lg' };
14
+ const BP_KEYS = new Set(['base', 'mob', 'tab', 'des']);
15
+
10
16
  function cssProp(js: string): string {
11
17
  return js.replace(/([A-Z])/g, '-$1').toLowerCase();
12
18
  }
13
19
 
14
- export function resolveS(s: string | Record<string, string | number | undefined>): SResult {
20
+ function parseBreakpointString(str: string): string {
21
+ const classes: string[] = [];
22
+ let base = str;
23
+
24
+ let m: RegExpExecArray | null;
25
+ BP_RE.lastIndex = 0;
26
+ while ((m = BP_RE.exec(str)) !== null) {
27
+ const [, bp, content] = m;
28
+ const prefix = BP_MAP[bp];
29
+ const tokens = content.trim().split(/\s+/).filter(Boolean);
30
+ for (const t of tokens) classes.push(`${prefix}:${t}`);
31
+ }
32
+
33
+ base = base.replace(BP_RE, '').replace(/\s+/g, ' ').trim();
34
+ if (base) classes.unshift(...base.split(/\s+/).filter(Boolean));
35
+ return classes.join(' ');
36
+ }
37
+
38
+ export function resolveS(s: SValue): SResult {
15
39
  if (typeof s === 'string') {
16
- return { className: s.trim() || undefined };
40
+ const className = parseBreakpointString(s);
41
+ return { className: className || undefined };
17
42
  }
18
43
  if (s && typeof s === 'object') {
44
+ const obj = s as Record<string, string | number | undefined>;
45
+ const hasBp = BP_KEYS.some((k) => k in obj && obj[k] != null);
46
+
47
+ if (hasBp) {
48
+ const parts: string[] = [];
49
+ for (const bp of ['base', 'mob', 'tab', 'des']) {
50
+ const v = obj[bp];
51
+ if (v == null || v === '') continue;
52
+ const str = String(v).trim();
53
+ if (bp === 'base') parts.push(str);
54
+ else {
55
+ const prefix = BP_MAP[bp];
56
+ for (const t of str.split(/\s+/).filter(Boolean)) parts.push(`${prefix}:${t}`);
57
+ }
58
+ }
59
+ const className = parts.join(' ');
60
+ const style: Record<string, string> = {};
61
+ for (const [k, v] of Object.entries(obj)) {
62
+ if (BP_KEYS.has(k) || v == null || v === '') continue;
63
+ style[cssProp(k)] = String(v);
64
+ }
65
+ return {
66
+ ...(className ? { className } : {}),
67
+ ...(Object.keys(style).length ? { style } : {}),
68
+ };
69
+ }
70
+
19
71
  const style: Record<string, string> = {};
20
- for (const [k, v] of Object.entries(s)) {
72
+ for (const [k, v] of Object.entries(obj)) {
21
73
  if (v != null && v !== '') style[cssProp(k)] = String(v);
22
74
  }
23
75
  return Object.keys(style).length ? { style } : {};
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Parses shorthand CSS in base: '...' for styleFlow.
3
+ * Format: prop value [value...] separated by ;
4
+ * Examples: bg red; p 4; flex; c #fff
5
+ */
6
+
7
+ const SHORTHAND: Record<string, string> = {
8
+ bg: 'background',
9
+ background: 'background',
10
+ bgc: 'background-color',
11
+ c: 'color',
12
+ p: 'padding',
13
+ pt: 'padding-top',
14
+ pr: 'padding-right',
15
+ pb: 'padding-bottom',
16
+ pl: 'padding-left',
17
+ m: 'margin',
18
+ mt: 'margin-top',
19
+ mr: 'margin-right',
20
+ mb: 'margin-bottom',
21
+ ml: 'margin-left',
22
+ w: 'width',
23
+ h: 'height',
24
+ minw: 'min-width',
25
+ minh: 'min-height',
26
+ maxw: 'max-width',
27
+ maxh: 'max-height',
28
+ fw: 'font-weight',
29
+ fs: 'font-size',
30
+ lh: 'line-height',
31
+ gap: 'gap',
32
+ g: 'gap',
33
+ r: 'border-radius',
34
+ radius: 'border-radius',
35
+ b: 'border',
36
+ bt: 'border-top',
37
+ br: 'border-right',
38
+ bb: 'border-bottom',
39
+ bl: 'border-left',
40
+ o: 'opacity',
41
+ z: 'z-index',
42
+ };
43
+
44
+ const DISPLAY_TOKENS: Record<string, string> = {
45
+ flex: 'flex',
46
+ block: 'block',
47
+ inline: 'inline',
48
+ 'inline-block': 'inline-block',
49
+ grid: 'grid',
50
+ none: 'none',
51
+ hidden: 'none',
52
+ };
53
+
54
+ function toValue(v: string): string {
55
+ const n = parseFloat(v);
56
+ if (!Number.isNaN(n) && /^\d+(\.\d+)?$/.test(v.trim())) return `${n}px`;
57
+ return v;
58
+ }
59
+
60
+ export function parseBaseShorthand(str: string): Record<string, string> {
61
+ const style: Record<string, string> = {};
62
+ const parts = str.split(/[;\n]+/).map((s) => s.trim()).filter(Boolean);
63
+
64
+ for (const part of parts) {
65
+ const tokens = part.split(/\s+/).filter(Boolean);
66
+ if (tokens.length === 0) continue;
67
+
68
+ const first = tokens[0].toLowerCase();
69
+ if (DISPLAY_TOKENS[first]) {
70
+ style.display = DISPLAY_TOKENS[first];
71
+ continue;
72
+ }
73
+
74
+ const prop = SHORTHAND[first] ?? first.replace(/([A-Z])/g, '-$1').toLowerCase();
75
+ const values = tokens.slice(1);
76
+
77
+ if (values.length === 0) continue;
78
+ if (values.length === 1) style[prop] = toValue(values[0]);
79
+ else if (values.length === 2) {
80
+ style[prop] = `${toValue(values[0])} ${toValue(values[1])}`;
81
+ } else if (values.length === 4) {
82
+ style[prop] = values.map(toValue).join(' ');
83
+ } else {
84
+ style[prop] = values.map(toValue).join(' ');
85
+ }
86
+ }
87
+ return style;
88
+ }
package/src/jsx.d.ts CHANGED
@@ -1,9 +1,15 @@
1
1
  declare global {
2
2
  namespace JSX {
3
3
  interface IntrinsicAttributes {
4
- /** Shorthand: class names (string) or inline styles (object) */
4
+ /**
5
+ * Shorthand styles:
6
+ * - string: "flex flex-col mob:(bg-blue) tab:(p-4) des:(w-full)"
7
+ * - object: { display: 'flex' } or { base: 'flex', mob: 'bg-blue', tab: 'p-4' }
8
+ */
5
9
  s?: string | Record<string, string | number | undefined>;
6
- /** Design system / theme token reference */
10
+ /**
11
+ * Design system. base: 'bg red; p 4; flex' = shorthand CSS (inline styles).
12
+ */
7
13
  styleFlow?: Record<string, unknown> | string;
8
14
  }
9
15
  }
@@ -5,5 +5,7 @@ export type SValue = string | Record<string, string | number | undefined>;
5
5
  export type StyleFlowToken = string | number;
6
6
 
7
7
  export interface StyleFlowTheme {
8
- [key: string]: StyleFlowToken | StyleFlowTheme;
8
+ /** Shorthand CSS: "bg red; p 4; flex" → inline styles */
9
+ base?: string;
10
+ [key: string]: StyleFlowToken | StyleFlowTheme | undefined;
9
11
  }