alif-ui 2.0.0-alpha.18 → 2.0.0-alpha.19

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/README.md CHANGED
@@ -134,6 +134,24 @@ If your project requires a brand:
134
134
 
135
135
  And here's where magic happens - all the components automatically adapt to that brand!
136
136
 
137
+ # AI assisted development
138
+
139
+ This repo also ships **agent skills** for AI assisted development — see `skills/`.
140
+
141
+ `alif-ui` teaches AI coding assistants how to build UI with this library — setup, every component's props, our patterns and the mistakes to avoid — and adapts to the project it is installed in.
142
+
143
+ The skill ships inside the package, so once `alif-ui` is installed one command puts it where your assistant looks:
144
+
145
+ ```bash
146
+ npx alif-ui skills install
147
+ ```
148
+
149
+ It installs into every assistant skill directory the project already has — any `.<assistant>/skills` folder, so Claude Code, Cursor, Windsurf and the shared `.agents/skills` are all found without us keeping a list. A project with none of them gets `.claude/skills/`; `--dir <path>` and `--global` override that. Re-run it after upgrading `alif-ui`, and commit the installed folder so your team gets it.
150
+
151
+ Written in the open [Agent Skills](https://agentskills.io) format, so it works in Claude Code, Cursor, Copilot and other compatible assistants.
152
+
153
+ A second skill, `alif-ui-contributor`, documents this repository for contributors and needs no installation. See **Guides → AI Assisted Development** in Storybook for details.
154
+
137
155
  # Documentation
138
156
 
139
157
  Visit [https://ui.alif.tj](https://ui.alif.tj) to view the full documentation.
@@ -5,62 +5,63 @@ import { useOutsideClick as r } from "../../hooks/useOutsideClick/useOutsideClic
5
5
  import { useFloatingPosition as i } from "./useFloatingPosition/useFloatingPosition.js";
6
6
  import { PopoverTrigger as a } from "./PopoverTrigger/PopoverTrigger.js";
7
7
  import { PopoverDropdown as o } from "./PopoverDropdown/PopoverDropdown.js";
8
- import { useRef as s, useState as c } from "react";
9
- import { jsx as l } from "react/jsx-runtime";
8
+ import { useId as s, useRef as c, useState as l } from "react";
9
+ import { jsx as u } from "react/jsx-runtime";
10
10
  //#region src/lib/components/Popover/Popover.tsx
11
- var u = ({ children: a, offset: o = 4, position: u = "bottom", isOpen: d, onChange: f, onClose: p, withArrow: m = !1, width: h = "max-content", zIndex: g = e("popover"), isFixed: _ = !1, transitionProps: v, triggerType: y = "click", disabled: b = !1 }) => {
12
- let [x, S] = c(!1), [C, w] = c(null), [T, E] = c(null), D = s(null), O = s(null), k = n(), { placement: A, arrowPlacement: j } = i(T, C, u, o, m, _), M = () => {
13
- if (d !== void 0) {
14
- f?.(!d);
11
+ var d = ({ children: a, offset: o = 4, position: d = "bottom", isOpen: f, onChange: p, onClose: m, withArrow: h = !1, width: g = "max-content", zIndex: _ = e("popover"), isFixed: v = !1, transitionProps: y, triggerType: b = "click", disabled: x = !1 }) => {
12
+ let [S, C] = l(!1), [w, T] = l(null), [E, D] = l(null), O = c(null), k = c(null), A = n(), j = `${A?.popoverPath ?? ""}/${s()}`, { placement: M, arrowPlacement: N } = i(E, w, d, o, h, v), P = () => {
13
+ if (f !== void 0) {
14
+ p?.(!f);
15
15
  return;
16
16
  }
17
- S((e) => !e);
18
- }, N = () => {
19
- if (d !== void 0) {
20
- f?.(!0);
17
+ C((e) => !e);
18
+ }, F = () => {
19
+ if (f !== void 0) {
20
+ p?.(!0);
21
21
  return;
22
22
  }
23
- S(!0);
24
- }, P = () => {
25
- !d && !x || (S(!1), f?.(!1), p?.());
23
+ C(!0);
24
+ }, I = () => {
25
+ !f && !S || (C(!1), p?.(!1), m?.());
26
26
  };
27
- return r([{ current: C }, { current: T }], P), /* @__PURE__ */ l(t, {
27
+ return r([{ current: w }, { current: E }], I, (e) => !!e.closest("[data-popover-dropdown]")?.getAttribute("data-popover-dropdown")?.startsWith(`${j}/`)), /* @__PURE__ */ u(t, {
28
28
  value: {
29
- closePopover: P,
29
+ closePopover: I,
30
30
  closeAllPopovers: () => {
31
- P(), k?.closeAllPopovers?.();
31
+ I(), A?.closeAllPopovers?.();
32
32
  },
33
- togglePopover: M,
34
- openPopover: N,
33
+ togglePopover: P,
34
+ openPopover: F,
35
35
  delayedOpen: () => {
36
- O.current &&= (window.clearTimeout(O.current), null), !D.current && (D.current = window.setTimeout(() => {
37
- N(), D.current = null;
36
+ k.current &&= (window.clearTimeout(k.current), null), !O.current && (O.current = window.setTimeout(() => {
37
+ F(), O.current = null;
38
38
  }, 200));
39
39
  },
40
40
  delayedClose: () => {
41
- D.current &&= (window.clearTimeout(D.current), null), !O.current && (O.current = window.setTimeout(() => {
42
- P(), O.current = null;
41
+ O.current &&= (window.clearTimeout(O.current), null), !k.current && (k.current = window.setTimeout(() => {
42
+ I(), k.current = null;
43
43
  }, 200));
44
44
  },
45
- triggerRefState: T,
46
- setTriggerRefState: E,
47
- isOpen: x,
48
- isOpenControlled: d,
49
- setPopoverRefState: w,
50
- popoverRefState: C,
51
- placement: A,
52
- arrowPlacement: j,
53
- width: h,
54
- withArrow: m,
55
- triggerType: y,
56
- disabled: b,
57
- zIndex: g,
58
- transitionProps: v,
59
- isFixed: _
45
+ popoverPath: j,
46
+ triggerRefState: E,
47
+ setTriggerRefState: D,
48
+ isOpen: S,
49
+ isOpenControlled: f,
50
+ setPopoverRefState: T,
51
+ popoverRefState: w,
52
+ placement: M,
53
+ arrowPlacement: N,
54
+ width: g,
55
+ withArrow: h,
56
+ triggerType: b,
57
+ disabled: x,
58
+ zIndex: _,
59
+ transitionProps: y,
60
+ isFixed: v
60
61
  },
61
62
  children: a
62
63
  });
63
64
  };
64
- u.Trigger = a, u.Dropdown = o;
65
+ d.Trigger = a, d.Dropdown = o;
65
66
  //#endregion
66
- export { u as Popover };
67
+ export { d as Popover };
@@ -6,13 +6,13 @@ import { PopoverArrow as r } from "./PopoverArrow.js";
6
6
  import { jsx as i, jsxs as a } from "react/jsx-runtime";
7
7
  //#region src/lib/components/Popover/PopoverDropdown/PopoverDropdown.tsx
8
8
  var o = ({ children: o, className: s, style: c, onClick: l, ...u }) => {
9
- let { isOpen: d, isOpenControlled: f, placement: p, setPopoverRefState: m, zIndex: h, width: g, withArrow: _, arrowPlacement: v, triggerRefState: y, transitionProps: b, isFixed: x, delayedClose: S, delayedOpen: C, triggerType: w } = n(), T = w === "hover" || w === "click-hover", E = () => {
10
- T && C();
11
- }, D = (e) => {
12
- if (!T) return;
9
+ let { isOpen: d, isOpenControlled: f, placement: p, setPopoverRefState: m, zIndex: h, width: g, withArrow: _, arrowPlacement: v, triggerRefState: y, transitionProps: b, isFixed: x, delayedClose: S, delayedOpen: C, triggerType: w, popoverPath: T } = n(), E = w === "hover" || w === "click-hover", D = () => {
10
+ E && C();
11
+ }, O = (e) => {
12
+ if (!E) return;
13
13
  let t = e.relatedTarget;
14
14
  t instanceof Node && y?.contains(t) || S();
15
- }, O = (e) => {
15
+ }, k = (e) => {
16
16
  e.stopPropagation(), l?.(e);
17
17
  };
18
18
  return /* @__PURE__ */ i(e, {
@@ -25,7 +25,7 @@ var o = ({ children: o, className: s, style: c, onClick: l, ...u }) => {
25
25
  mounted: f || d,
26
26
  ...b,
27
27
  children: (e) => /* @__PURE__ */ a("div", {
28
- "data-popover-dropdown": !0,
28
+ "data-popover-dropdown": T,
29
29
  style: {
30
30
  ...e,
31
31
  ...c,
@@ -33,9 +33,9 @@ var o = ({ children: o, className: s, style: c, onClick: l, ...u }) => {
33
33
  },
34
34
  className: s,
35
35
  ref: m,
36
- onClick: O,
37
- onMouseEnter: E,
38
- onMouseLeave: D,
36
+ onClick: k,
37
+ onMouseEnter: D,
38
+ onMouseLeave: O,
39
39
  ...u,
40
40
  children: [_ && /* @__PURE__ */ i(r, { ...v }), o]
41
41
  })
@@ -35,8 +35,7 @@ var e = {
35
35
  `${t}-end`,
36
36
  t
37
37
  ].filter((e, t, n) => n.indexOf(e) === t);
38
- }, i = async (i, a, o, s, c, l) => {
39
- await document.fonts.ready;
38
+ }, i = (i, a, o, s, c, l) => {
40
39
  let u = i.getBoundingClientRect(), d = a.getBoundingClientRect(), f = {
41
40
  top: 0,
42
41
  left: 0
@@ -1,6 +1,6 @@
1
1
  import { getPopoverPosition as e } from "./useFloating.util.js";
2
2
  import { getScrollableAncestors as t } from "./getScrollableAncestors.js";
3
- import { useEffect as n, useRef as r, useState as i } from "react";
3
+ import { useLayoutEffect as n, useRef as r, useState as i } from "react";
4
4
  //#region src/lib/components/Popover/useFloatingPosition/useFloatingPosition.ts
5
5
  var a = (a, o, s = "bottom", c, l = !1, u) => {
6
6
  let [d, f] = i({
@@ -14,21 +14,17 @@ var a = (a, o, s = "bottom", c, l = !1, u) => {
14
14
  return n(() => {
15
15
  if (!a || !o) return;
16
16
  let n = () => {
17
- e(a, o, s, c, l, u).then(({ positionProps: e, arrowPosition: t }) => {
18
- f(e), m(t);
19
- });
17
+ let { positionProps: t, arrowPosition: n } = e(a, o, s, c, l, u);
18
+ f(t), m(n);
20
19
  };
21
- if (u) {
22
- n();
23
- return;
24
- }
25
- let r = t(a);
26
- r.forEach((e) => e.addEventListener("scroll", n)), window.addEventListener("resize", n), h.current = new ResizeObserver(n), h.current.observe(a), h.current.observe(o);
27
- let i;
28
- return document.fonts.ready.then(() => {
29
- i = requestAnimationFrame(n);
30
- }), () => {
31
- r.forEach((e) => e.removeEventListener("scroll", n)), window.removeEventListener("resize", n), h.current?.disconnect(), cancelAnimationFrame(i);
20
+ n();
21
+ let r;
22
+ if (document.fonts.ready.then(() => {
23
+ r = requestAnimationFrame(n);
24
+ }), u) return () => cancelAnimationFrame(r);
25
+ let i = t(a);
26
+ return i.forEach((e) => e.addEventListener("scroll", n)), window.addEventListener("resize", n), h.current = new ResizeObserver(n), h.current.observe(a), h.current.observe(o), () => {
27
+ i.forEach((e) => e.removeEventListener("scroll", n)), window.removeEventListener("resize", n), h.current?.disconnect(), cancelAnimationFrame(r);
32
28
  };
33
29
  }, [
34
30
  a,
@@ -1,22 +1,20 @@
1
1
  import { useEffect as e, useMemo as t } from "react";
2
2
  //#region src/lib/hooks/useOutsideClick/useOutsideClick.ts
3
- var n = (n, r) => {
4
- let i = t(() => Array.isArray(n) ? n : [n], [n]);
3
+ var n = (n, r, i) => {
4
+ let a = t(() => Array.isArray(n) ? n : [n], [n]);
5
5
  e(() => {
6
6
  let e = (e) => {
7
7
  let t = e.target;
8
- if (i.some((e) => e.current?.contains(t))) return;
9
- let n = t;
10
- for (; n;) {
11
- if (n.hasAttribute && n.hasAttribute("data-popover-dropdown")) return;
12
- n = n.parentElement;
13
- }
14
- r(e);
8
+ a.some((e) => e.current?.contains(t)) || t instanceof Element && i?.(t) || r(e);
15
9
  };
16
10
  return document.addEventListener("mousedown", e), () => {
17
11
  document.removeEventListener("mousedown", e);
18
12
  };
19
- }, [i, r]);
13
+ }, [
14
+ a,
15
+ r,
16
+ i
17
+ ]);
20
18
  };
21
19
  //#endregion
22
20
  export { n as useOutsideClick };
@@ -10,6 +10,7 @@ interface PopoverContext {
10
10
  delayedClose: () => void;
11
11
  triggerType?: TriggerType;
12
12
  disabled?: boolean;
13
+ popoverPath: string;
13
14
  triggerRefState: HTMLDivElement | null;
14
15
  setTriggerRefState: (ref: HTMLDivElement | null) => void;
15
16
  isOpen: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"Popover.context.d.ts","sourceRoot":"","sources":["../../../../../src/lib/components/Popover/Popover.context.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAEjE,UAAU,cAAc;IACvB,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,eAAe,EAAE,cAAc,GAAG,IAAI,CAAC;IACvC,kBAAkB,EAAE,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,KAAK,IAAI,CAAC;IACzD,MAAM,EAAE,OAAO,CAAC;IAChB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,kBAAkB,EAAE,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,KAAK,IAAI,CAAC;IACzD,eAAe,EAAE,cAAc,GAAG,IAAI,CAAC;IACvC,SAAS,EAAE,aAAa,CAAC;IACzB,cAAc,EAAE,aAAa,CAAC;IAC9B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,IAAI,CAAC,eAAe,EAAE,SAAS,GAAG,UAAU,CAAC,CAAC;IAChE,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,eAAO,MACN,sBAAsB;;;+CACtB,iBAAiB,wBACjB,yBAAyB,6BAGzB,CAAC"}
1
+ {"version":3,"file":"Popover.context.d.ts","sourceRoot":"","sources":["../../../../../src/lib/components/Popover/Popover.context.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAEjE,UAAU,cAAc;IACvB,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,cAAc,GAAG,IAAI,CAAC;IACvC,kBAAkB,EAAE,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,KAAK,IAAI,CAAC;IACzD,MAAM,EAAE,OAAO,CAAC;IAChB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,kBAAkB,EAAE,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,KAAK,IAAI,CAAC;IACzD,eAAe,EAAE,cAAc,GAAG,IAAI,CAAC;IACvC,SAAS,EAAE,aAAa,CAAC;IACzB,cAAc,EAAE,aAAa,CAAC;IAC9B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,IAAI,CAAC,eAAe,EAAE,SAAS,GAAG,UAAU,CAAC,CAAC;IAChE,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,eAAO,MACN,sBAAsB;;;+CACtB,iBAAiB,wBACjB,yBAAyB,6BAGzB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"Popover.d.ts","sourceRoot":"","sources":["../../../../../src/lib/components/Popover/Popover.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAU/C,eAAO,MAAM,OAAO;8JAcjB,YAAY;;;CAgHd,CAAC"}
1
+ {"version":3,"file":"Popover.d.ts","sourceRoot":"","sources":["../../../../../src/lib/components/Popover/Popover.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAU/C,eAAO,MAAM,OAAO;8JAcjB,YAAY;;;CA0Hd,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"PopoverDropdown.d.ts","sourceRoot":"","sources":["../../../../../../src/lib/components/Popover/PopoverDropdown/PopoverDropdown.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAI/D,eAAO,MAAM,eAAe,GAAI,uDAM7B,oBAAoB,4CAuEtB,CAAC"}
1
+ {"version":3,"file":"PopoverDropdown.d.ts","sourceRoot":"","sources":["../../../../../../src/lib/components/Popover/PopoverDropdown/PopoverDropdown.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAI/D,eAAO,MAAM,eAAe,GAAI,uDAM7B,oBAAoB,4CAwEtB,CAAC"}
@@ -9,8 +9,8 @@ export declare const ARROW: {
9
9
  readonly START_MARGIN: number;
10
10
  readonly END_MARGIN: number;
11
11
  };
12
- export declare const getPopoverPosition: (trigger: HTMLDivElement, popover: HTMLDivElement, position: Position, offset: number, withArrow: boolean, isFixed: boolean) => Promise<{
12
+ export declare const getPopoverPosition: (trigger: HTMLDivElement, popover: HTMLDivElement, position: Position, offset: number, withArrow: boolean, isFixed: boolean) => {
13
13
  positionProps: PositionProps;
14
14
  arrowPosition: ArrowPositionProps;
15
- }>;
15
+ };
16
16
  //# sourceMappingURL=useFloating.util.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useFloating.util.d.ts","sourceRoot":"","sources":["../../../../../../src/lib/components/Popover/useFloatingPosition/useFloating.util.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG5C,eAAO,MAAM,KAAK;;;;;;;CAgBjB,CAAC;AA+BF,eAAO,MAAM,kBAAkB,GAC9B,SAAS,cAAc,EACvB,SAAS,cAAc,EACvB,UAAU,QAAQ,EAClB,QAAQ,MAAM,EACd,WAAW,OAAO,EAClB,SAAS,OAAO,KACd,OAAO,CAAC;IACV,aAAa,EAAE,aAAa,CAAC;IAC7B,aAAa,EAAE,kBAAkB,CAAC;CAClC,CA2IA,CAAC"}
1
+ {"version":3,"file":"useFloating.util.d.ts","sourceRoot":"","sources":["../../../../../../src/lib/components/Popover/useFloatingPosition/useFloating.util.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG5C,eAAO,MAAM,KAAK;;;;;;;CAgBjB,CAAC;AA+BF,eAAO,MAAM,kBAAkB,GAC9B,SAAS,cAAc,EACvB,SAAS,cAAc,EACvB,UAAU,QAAQ,EAClB,QAAQ,MAAM,EACd,WAAW,OAAO,EAClB,SAAS,OAAO,KACd;IACF,aAAa,EAAE,aAAa,CAAC;IAC7B,aAAa,EAAE,kBAAkB,CAAC;CA0IlC,CAAC"}
@@ -1,3 +1,3 @@
1
1
  import { RefObject } from 'react';
2
- export declare const useOutsideClick: (refs: RefObject<HTMLElement> | RefObject<HTMLElement>[], callback: (e?: Event) => void) => void;
2
+ export declare const useOutsideClick: (refs: RefObject<HTMLElement> | RefObject<HTMLElement>[], callback: (e?: Event) => void, shouldIgnore?: (target: Element) => boolean) => void;
3
3
  //# sourceMappingURL=useOutsideClick.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useOutsideClick.d.ts","sourceRoot":"","sources":["../../../../../src/lib/hooks/useOutsideClick/useOutsideClick.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAsB,MAAM,OAAO,CAAC;AAEtD,eAAO,MAAM,eAAe,GAC3B,MAAM,SAAS,CAAC,WAAW,CAAC,GAAG,SAAS,CAAC,WAAW,CAAC,EAAE,EACvD,UAAU,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,IAAI,SAkC7B,CAAC"}
1
+ {"version":3,"file":"useOutsideClick.d.ts","sourceRoot":"","sources":["../../../../../src/lib/hooks/useOutsideClick/useOutsideClick.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAsB,MAAM,OAAO,CAAC;AAEtD,eAAO,MAAM,eAAe,GAC3B,MAAM,SAAS,CAAC,WAAW,CAAC,GAAG,SAAS,CAAC,WAAW,CAAC,EAAE,EACvD,UAAU,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,IAAI,EAC7B,eAAe,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,SAwB3C,CAAC"}
package/package.json CHANGED
@@ -21,11 +21,16 @@
21
21
  },
22
22
  "license": "ISC",
23
23
  "private": false,
24
- "version": "2.0.0-alpha.18",
24
+ "version": "2.0.0-alpha.19",
25
25
  "type": "module",
26
26
  "files": [
27
- "dist"
27
+ "dist",
28
+ "skills",
29
+ "scripts/installSkills.js"
28
30
  ],
31
+ "bin": {
32
+ "alif-ui": "./scripts/installSkills.js"
33
+ },
29
34
  "main": "./dist/main.js",
30
35
  "module": "./dist/main.js",
31
36
  "style": "./dist/alif-ui.css",
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import { fileURLToPath } from 'url';
6
+
7
+ const SKILL_NAME = 'alif-ui';
8
+
9
+ const DEFAULT_DIR = '.claude/skills';
10
+
11
+ const here = path.dirname(fileURLToPath(import.meta.url));
12
+ const skillSource = path.join(here, '..', 'skills', SKILL_NAME);
13
+ const packageJsonPath = path.join(here, '..', 'package.json');
14
+
15
+ const usage = `
16
+ alif-ui — Alif Bank design system
17
+
18
+ Usage
19
+ npx alif-ui skills install [--dir <path>] [--global]
20
+
21
+ Options
22
+ --dir <path> install into this directory instead of detecting one
23
+ (e.g. --dir .agents/skills for a shared agent directory)
24
+ --global install into ~/.claude/skills (available in every project)
25
+
26
+ With no options the skill is installed into every assistant skill directory the
27
+ project already has — any '.<assistant>/skills' folder, such as .claude/skills,
28
+ .cursor/skills or the shared .agents/skills. A project with none of them gets
29
+ ${DEFAULT_DIR}.
30
+
31
+ Installs the ${SKILL_NAME} skill, which teaches AI coding assistants
32
+ (Claude Code, Cursor, Copilot, ...) how to build UI with alif-ui.
33
+ `;
34
+
35
+ const getVersion = () => {
36
+ try {
37
+ return JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')).version;
38
+ } catch {
39
+ return null;
40
+ }
41
+ };
42
+
43
+ const getTargets = (args) => {
44
+ const dirIndex = args.indexOf('--dir');
45
+
46
+ if (dirIndex !== -1) {
47
+ const dir = args[dirIndex + 1];
48
+
49
+ if (!dir) {
50
+ throw new Error('--dir requires a path');
51
+ }
52
+
53
+ return [path.resolve(dir)];
54
+ }
55
+
56
+ if (args.includes('--global')) {
57
+ const home = process.env.HOME || process.env.USERPROFILE;
58
+
59
+ if (!home) {
60
+ throw new Error('cannot resolve the home directory for --global');
61
+ }
62
+
63
+ return [path.join(home, '.claude', 'skills')];
64
+ }
65
+
66
+ return findAgentDirs();
67
+ };
68
+
69
+ const findAgentDirs = () => {
70
+ const root = process.cwd();
71
+
72
+ const found = fs
73
+ .readdirSync(root, { withFileTypes: true })
74
+ .filter((entry) => entry.isDirectory() && entry.name.startsWith('.'))
75
+ .map((entry) => path.join(root, entry.name, 'skills'))
76
+ .filter((dir) => fs.existsSync(dir));
77
+
78
+ if (found.length > 0) {
79
+ return found;
80
+ }
81
+
82
+ console.log(
83
+ `no assistant skill directory found — using ${DEFAULT_DIR}\n` +
84
+ `if your assistant reads somewhere else, re-run with --dir <path>\n`,
85
+ );
86
+
87
+ return [path.resolve(DEFAULT_DIR)];
88
+ };
89
+
90
+ const installSkill = (args) => {
91
+ if (!fs.existsSync(skillSource)) {
92
+ throw new Error(
93
+ `skill not found at ${skillSource}. Reinstall alif-ui, or run this from a project that depends on it.`,
94
+ );
95
+ }
96
+
97
+ const version = getVersion();
98
+
99
+ for (const target of getTargets(args)) {
100
+ const destination = path.join(target, SKILL_NAME);
101
+ const isUpdate = fs.existsSync(destination);
102
+
103
+ fs.mkdirSync(target, { recursive: true });
104
+ fs.cpSync(skillSource, destination, { recursive: true });
105
+
106
+ const action = isUpdate ? 'updated' : 'installed';
107
+ const relative = path.relative(process.cwd(), destination) || destination;
108
+
109
+ console.log(
110
+ `${action} ${SKILL_NAME}${version ? ` (alif-ui ${version})` : ''} → ${relative}`,
111
+ );
112
+ }
113
+
114
+ console.log(
115
+ '\nRestart your assistant to pick it up, and commit the folder so your team gets it too.',
116
+ );
117
+ };
118
+
119
+ const [command, subcommand, ...rest] = process.argv.slice(2);
120
+
121
+ try {
122
+ if (command === 'skills' && (subcommand === 'install' || !subcommand)) {
123
+ installSkill(rest);
124
+ } else if (command === 'skills') {
125
+ throw new Error(`unknown subcommand: skills ${subcommand}`);
126
+ } else {
127
+ console.log(usage.trim());
128
+ process.exit(command === undefined ? 0 : 1);
129
+ }
130
+ } catch (error) {
131
+ console.error(`alif-ui: ${error.message}`);
132
+ process.exit(1);
133
+ }
@@ -0,0 +1,110 @@
1
+ ---
2
+ name: alif-ui
3
+ description: How to build application UI with the alif-ui React component library (Alif Bank design system) — setup, the component catalogue and its exact props, composition patterns and the mistakes to avoid. Use whenever writing, reviewing or debugging React/Next.js code that imports from the alif-ui package — screens, forms, tables, modals, layouts, theming, localization or icons. For developing the library itself, use the alif-ui-contributor skill instead.
4
+ ---
5
+
6
+ # Building UI with alif-ui
7
+
8
+ > This skill is for **application code that imports `alif-ui`**. If you are working inside the library repository itself (`src/lib/**`), use the `alif-ui-contributor` skill instead.
9
+
10
+ `alif-ui` is Alif Bank's React component library implementing the Alif design system. When a project depends on it, **compose screens out of these components** — do not hand-roll buttons, inputs, modals or spinners, and do not add another UI library (MUI, Ant, shadcn, Chakra) alongside it.
11
+
12
+ ## Step 0 — read this project's setup first
13
+
14
+ Every app wires the library differently. Before writing UI, establish these facts once per session and follow them instead of the generic examples below:
15
+
16
+ | What to learn | Where to look |
17
+ | ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
18
+ | Installed version | `alif-ui` entry in `package.json`, or `node_modules/alif-ui/package.json` |
19
+ | The exact public API of that version | `node_modules/alif-ui/dist/main.d.ts` — the ground truth if anything here disagrees |
20
+ | Whether setup exists, and where | grep for `AlifProvider` and `alif-ui/styles.css` |
21
+ | Brand in use | the `brand` prop passed to `AlifProvider`, and which brand CSS is imported next to `alif-ui/styles.css` |
22
+ | Theme and locale defaults | `initialMode` / `initialLocale` on `AlifProvider` |
23
+ | Whether the built-in i18n is used | grep for `localization.init` and `useTranslation`; note where the locale JSONs live |
24
+ | Form library | `formik` / `react-hook-form` in `package.json` — picks `Controller` vs `register()` vs plain state |
25
+ | Framework | `next` in `package.json`, and app router (`app/layout.tsx`) vs pages router (`pages/_app.tsx`) — decides where `'use client'` is needed |
26
+ | House conventions | grep existing `from 'alif-ui'` imports: local wrapper components, how errors and loading states are already expressed, whether the app pins sizes or relies on defaults |
27
+
28
+ Two rules follow from this:
29
+
30
+ - **Match the app, not the sample.** If the codebase already wraps `Input` in its own `FormField`, use that wrapper; if screens use `size="s"` everywhere, keep it.
31
+ - **When this skill and `main.d.ts` disagree, `main.d.ts` wins** — this skill documents the 2.x API, and an app may sit on a different version. Say so rather than silently writing against the wrong API.
32
+
33
+ If the setup below is missing entirely, add it — that is the one case where you introduce it yourself.
34
+
35
+ ## Setup (must exist once per app)
36
+
37
+ ```tsx
38
+ import { AlifProvider } from 'alif-ui';
39
+ import 'alif-ui/styles.css'; // library tokens + reset — always first
40
+ import 'styles/alif/alif.css'; // ONE brand css from the design team
41
+
42
+ <AlifProvider brand="alif" initialMode="light" initialLocale="ru">
43
+ <App />
44
+ </AlifProvider>;
45
+ ```
46
+
47
+ - `brand`: any string (default `'universal'`). Only `universal` ships inside the package; every other brand is a CSS file the design team hands to the project. The value must equal the brand name in that file's `:root[data-brand='<name>']` selector — otherwise the app silently stays on `universal`.
48
+ - `initialMode`: `'light' | 'dark' | 'system'` (default `'system'`). Persisted in `localStorage`, applied as `<html data-theme>`.
49
+ - `initialLocale`: `'ru' | 'en' | 'tj'` (default: browser locale). Applied as `<html data-locale>`.
50
+ - **Next.js**: `AlifProvider` uses hooks and `localStorage`, so put it in a `'use client'` wrapper component and render that from `app/layout.tsx` (or `pages/_app.tsx`).
51
+ - Components rendered outside `AlifProvider` will not resolve tokens. Wrap the whole tree, including test render helpers.
52
+
53
+ ## Import rules
54
+
55
+ ```tsx
56
+ import { Button, Input, Modal, SolidSystemSettings, useMode } from 'alif-ui';
57
+ ```
58
+
59
+ - Everything — components, hooks, providers, icons, types — is a **named export of the package root**. There are no submodule paths and no default exports.
60
+ - Never import from `alif-ui/dist/...`, `alif-ui/src/...` or a component folder; only `'alif-ui'` and `'alif-ui/styles.css'`.
61
+ - Prop types are exported too (`ButtonProps`, `SelectOption<T>`, `ModalProps`, …) — use them instead of re-declaring shapes.
62
+
63
+ ## Non-negotiable rules
64
+
65
+ 1. **Never restyle with hardcoded values.** No hex colors, no `px` font sizes, no invented spacing. Use the component's own props first; when you must override, set the component's CSS custom properties or use design tokens (`var(--brand-*)`, `var(--mode-*)`). See `references/theming.md`.
66
+ 2. **CSS Module class names are hashed** — you cannot target the library's internal classes. Style through `className` / `style` on the component (both are forwarded and merged last) or through its CSS variables.
67
+ 3. **Boolean/handler naming is `isOpen` + `onClose` / `onChange`** — not `open`, not `onOpenChange`. Overlays (`Modal`, `Drawer`, `Backdrop`) require both.
68
+ 4. **Compound components are used through dot notation** (`Modal.Header`, `Tabs.Panel`, `Table.Td`, `Sidebar.Item`). The sub-parts are not separate imports, and skipping them (e.g. raw `<thead>` inside `Table`) breaks styling.
69
+ 5. **Controlled components need `value` + `onChange`.** `Select`, `Tabs`, `Rating`, `InputOtp`, `FileUploader`, `Slider`, `Pagination` and the date pickers have **required** value/handler props; `Accordion`, `Popover`, `Tab.Group`, `Checkbox.Group`, `Radio.Group` accept `defaultValue` for uncontrolled use.
70
+ 6. **Size scales differ per component** — `Button` is `xs|s|m|l`, `Input`/`Field`/`Textarea` are `s|m|l`, `List` is `m|l`, `Avatar` is `xxs…xxl`, `Checkbox`/`Radio`/`Switch` are `s|m`. Check `references/components.md` before passing a size.
71
+ 7. **Text uses `Typography`** with `variant` + matching `size` (`heading` → `h1…h6`, `body` → `xs|s|m|l`, `description` → `s|m|l`, `display` → `s|m|l`) and `element` for the rendered tag. Don't style bare `<h1>`/`<p>`.
72
+ 8. **Inputs are full width by default** (`fullWidth = true` on `Field`/`Input`/`Search`); `Button` is not. Use the `fullWidth` prop rather than wrapper divs with `width: 100%`.
73
+ 9. **Icons come from the package** (`SolidSystemSettings`, `OutlineNavigationChevronDown`, `TjFlag`) and inherit `currentColor` — pass them into `leftIcon` / `rightIcon` / `icon` / `leftSection` slots instead of `<img>` or inline SVG.
74
+ 10. **User-visible strings are the app's responsibility.** Components ship no copy of their own; feed them through props, and use `useTranslation()` / `localization.t()` if the app uses the built-in i18n.
75
+
76
+ ## Component catalogue (all exports)
77
+
78
+ Details, exact props and examples: `references/components.md`.
79
+
80
+ | Area | Components |
81
+ | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
82
+ | Actions | `Button`, `UnstyledButton` |
83
+ | Text & media | `Typography`, `Avatar` (`.Group`), `Badge`, `Indicator`, `Divider`, `Paper`, `Skeleton`, `Loader`, `ProgressBar` |
84
+ | Inputs | `Input`, `Field`, `Textarea`, `Search`, `InputOtp`, `Checkbox` (`.Group`), `Radio` (`.Group`), `Switch`, `Rating`, `Slider`, `RangeSlider`, `FileUploader`, `FileView`, `SelectorInput`, `Hinter` |
85
+ | Selection | `Select`, `SelectMultiple`, `List` (`.Group`, `.Item`), `Tab` (`.Group`), `SegmentedControl` |
86
+ | Dates | `InputDatePicker`, `InputTimePicker`, `InputMonthPicker`, `InputYearPicker`, `InputMonthYearPicker` (+ bare `DatePicker`, `TimePicker`, `MonthPicker`, `YearPicker`, `MonthYearPicker`) |
87
+ | Navigation | `Tabs` (`.Menu`, `.Tab`, `.Panel`), `Breadcrumbs`, `Pagination`, `Navbar`, `Sidebar` (`.Header`, `.Items`, `.Item`, `.Collapse`, `.Group`, `.Element`, `.Profile`, `.Footer`), `AppLayout` |
88
+ | Overlays | `Modal` (`.Header`, `.Content`, `.Actions`), `Drawer` (`.Header`, `.Content`, `.Actions`), `Popover` (`.Trigger`, `.Dropdown`), `Menu` (`.Item`, `.Sub`), `Tooltip`, `Backdrop`, `Portal` |
89
+ | Feedback | `Snackbar` + `snackbar` API, `Alert` |
90
+ | Disclosure | `Accordion` (`.Item`, `.Trigger`, `.Content`), `Collapse`, `Timeline` (`.Item`, `.Content`, `.OppositeContent`) |
91
+ | Data | `Table` (`.Thead`, `.Tbody`, `.Tfoot`, `.Tr`, `.Th`, `.Td`, `.CollapseRow`) |
92
+ | Motion | `Transition` |
93
+ | Hooks & providers | `AlifProvider`, `useMode`, `useLocale`, `useTranslation`, `localization`, `useMediaQuery`, `useOutsideClick`, `useInfiniteScroll`, `useFloatingPosition` |
94
+
95
+ ## How to work
96
+
97
+ 1. Establish the project's setup (Step 0) if this session hasn't yet.
98
+ 2. Find the component in the table above, then read its entry in `references/components.md` — props there are copied from the library's types, so trust them over guesses.
99
+ 3. If the task is a whole flow (form, CRUD table, wizard, layout), start from `references/patterns.md`.
100
+ 4. Before finishing, check `references/pitfalls.md` — it lists the mistakes that actually break alif-ui code.
101
+ 5. Theming, dark mode, brands, localization: `references/theming.md`.
102
+
103
+ If a component genuinely does not exist for the design at hand, build it from `Paper` + `Typography` + tokens rather than raw styled divs, and say so explicitly in your answer.
104
+
105
+ ## References
106
+
107
+ - `references/components.md` — every exported component, its props, defaults and a minimal example.
108
+ - `references/patterns.md` — forms (Formik / React Hook Form), overlays, tables with pagination, async selects, snackbars, layout shells, date pickers.
109
+ - `references/theming.md` — tokens, brands, light/dark, overriding component styles, typography, i18n hooks.
110
+ - `references/pitfalls.md` — wrong ↔ right pairs; read before shipping.