@loom-forge/grid 0.1.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/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # @loom-forge/grid
2
+
3
+ **loom** · o layout **`grid`** (dashboard) sobre `react-grid-layout`. É a prova de que o DSL
4
+ normaliza para o RGL: o nó declara `layout: "grid"` e cada filho um `placement: {x,y,w,h}`; o
5
+ **planner** (headless, do lado do core) vira o array de layout e o **renderer** monta o
6
+ `<ReactGridLayout>`. O core nem sabe que é RGL — ver
7
+ [react/DECISIONS ADR-106](../react/docs/DECISIONS.md).
8
+
9
+ ```bash
10
+ pnpm add @loom-forge/grid react-grid-layout
11
+ ```
12
+
13
+ ```ts
14
+ import { gridPlanner, gridRenderer } from "@loom-forge/grid";
15
+ const engine = createEngine({
16
+ planners: { ...defaultPlanners(), grid: gridPlanner() },
17
+ layoutRenderers: { ...defaultLayoutRenderers(), grid: gridRenderer() },
18
+ });
19
+ // def: { type:"Dashboard", layout:"grid", children:[{ id, type:"Widget", placement:{x,y,w,h} }] }
20
+ import "react-grid-layout/css/styles.css"; // na v2 as alças já vêm nesta folha
21
+ ```
22
+
23
+ Na **v1** do react-grid-layout falta a folha das alças, e ela vem do
24
+ `react-resizable` (`import "react-resizable/css/styles.css"`). Na **v2** o pacote a
25
+ embute, e o `react-resizable` deixou de ser dependência dele — importar de lá quebra
26
+ o build.
27
+
28
+ - `gridPlanner({ cols?, rowHeight? })` (headless) — `placement` → array de layout do RGL.
29
+ - `gridRenderer({ width?, margin?, className?, component? })` — plano → `<ReactGridLayout>`. `component`
30
+ permite `WidthProvider(Responsive)` pra auto-width.
31
+
32
+ ## O `placement` — o contrato que o layout lê, e que ele publica
33
+
34
+ O que um filho de `layout: "grid"` escreve em `placement` (`GridPlacement`):
35
+
36
+ | campo | o quê |
37
+ | ------------------------------ | -------------------------------------------------------------- |
38
+ | `x`, `y`, `w`, `h` | a célula: coluna, linha, largura e altura em unidades da grade |
39
+ | `minW`, `maxW`, `minH`, `maxH` | os limites do redimensionamento |
40
+ | `static` | fixo: nem arrasta nem redimensiona |
41
+ | `isDraggable`, `isResizable` | liga/desliga cada gesto por item |
42
+
43
+ Tudo opcional; o que não é escrito segue o default do RGL. O planner **publica essa forma como
44
+ JSON Schema em `placementSchema`** — é o que o inspetor da bancada lê para montar os campos de
45
+ posição de um filho (`x`/`y`/`w`/`h` viram controles numéricos, `static` um checkbox), sem
46
+ nenhuma lista escrita à mão do lado do editor: a fonte do campo é o contrato do layout, e não há
47
+ segunda descrição para divergir dele. Um layout que não publica `placementSchema` deixa o
48
+ inspetor mostrar o objeto cru — que é o preço de não ter contrato, e não um campo inventado.
@@ -0,0 +1,53 @@
1
+ import { LayoutPlanner } from '@loom-forge/core';
2
+ import { ComponentType } from 'react';
3
+ import { LayoutRenderer } from '@loom-forge/react';
4
+
5
+ interface GridPlacement {
6
+ x?: number;
7
+ y?: number;
8
+ w?: number;
9
+ h?: number;
10
+ minW?: number;
11
+ maxW?: number;
12
+ minH?: number;
13
+ maxH?: number;
14
+ static?: boolean;
15
+ isDraggable?: boolean;
16
+ isResizable?: boolean;
17
+ }
18
+ interface GridLayoutItem {
19
+ i: string;
20
+ x: number;
21
+ y: number;
22
+ w: number;
23
+ h: number;
24
+ minW?: number;
25
+ maxW?: number;
26
+ minH?: number;
27
+ maxH?: number;
28
+ static?: boolean;
29
+ isDraggable?: boolean;
30
+ isResizable?: boolean;
31
+ }
32
+ interface GridPlan {
33
+ cols: number;
34
+ rowHeight: number;
35
+ items: GridLayoutItem[];
36
+ }
37
+ interface GridPlannerOptions {
38
+ cols?: number;
39
+ rowHeight?: number;
40
+ }
41
+ declare function gridPlanner(opts?: GridPlannerOptions): LayoutPlanner;
42
+
43
+ interface GridRendererOptions {
44
+ /** largura em px (o RGL base exige). Default 1200. Pra auto-width responsivo, use `component`. */
45
+ width?: number;
46
+ margin?: [number, number];
47
+ className?: string;
48
+ /** componente RGL custom (ex.: `WidthProvider(Responsive)`). Default: react-grid-layout. */
49
+ component?: ComponentType<Record<string, unknown>>;
50
+ }
51
+ declare function gridRenderer(opts?: GridRendererOptions): LayoutRenderer;
52
+
53
+ export { type GridLayoutItem, type GridPlacement, type GridPlan, type GridPlannerOptions, type GridRendererOptions, gridPlanner, gridRenderer };
package/dist/index.js ADDED
@@ -0,0 +1,80 @@
1
+ import RGLImport from 'react-grid-layout';
2
+ import { jsx } from 'react/jsx-runtime';
3
+
4
+ // src/planner.ts
5
+ var PASS = [
6
+ "minW",
7
+ "maxW",
8
+ "minH",
9
+ "maxH",
10
+ "static",
11
+ "isDraggable",
12
+ "isResizable"
13
+ ];
14
+ var GRID_PLACEMENT = {
15
+ type: "object",
16
+ properties: {
17
+ x: { type: "number" },
18
+ y: { type: "number" },
19
+ w: { type: "number" },
20
+ h: { type: "number" },
21
+ minW: { type: "number" },
22
+ maxW: { type: "number" },
23
+ minH: { type: "number" },
24
+ maxH: { type: "number" },
25
+ static: { type: "boolean" },
26
+ isDraggable: { type: "boolean" },
27
+ isResizable: { type: "boolean" }
28
+ }
29
+ };
30
+ function gridPlanner(opts = {}) {
31
+ const cols = opts.cols ?? 12;
32
+ const rowHeight = opts.rowHeight ?? 30;
33
+ return {
34
+ name: "grid",
35
+ plan(children) {
36
+ const items = children.map((c, idx) => {
37
+ const p = c.placement ?? {};
38
+ const item = {
39
+ i: c.id,
40
+ x: p.x ?? 0,
41
+ y: p.y ?? idx,
42
+ // sem y → empilha
43
+ w: p.w ?? cols,
44
+ h: p.h ?? 1
45
+ };
46
+ const bag = item;
47
+ for (const k of PASS) if (p[k] !== void 0) bag[k] = p[k];
48
+ return item;
49
+ });
50
+ return { cols, rowHeight, items };
51
+ },
52
+ placementSchema: GRID_PLACEMENT
53
+ };
54
+ }
55
+ var DefaultRGL = RGLImport.default ?? RGLImport;
56
+ function gridRenderer(opts = {}) {
57
+ const Grid = opts.component ?? DefaultRGL;
58
+ return {
59
+ name: "grid",
60
+ render(plan, children) {
61
+ const p = plan;
62
+ return /* @__PURE__ */ jsx(
63
+ Grid,
64
+ {
65
+ className: opts.className,
66
+ layout: p.items,
67
+ cols: p.cols,
68
+ rowHeight: p.rowHeight,
69
+ width: opts.width ?? 1200,
70
+ margin: opts.margin,
71
+ children: p.items.map((it) => /* @__PURE__ */ jsx("div", { children: children.get(it.i) }, it.i))
72
+ }
73
+ );
74
+ }
75
+ };
76
+ }
77
+
78
+ export { gridPlanner, gridRenderer };
79
+ //# sourceMappingURL=index.js.map
80
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/planner.ts","../src/renderer.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { LayoutPlanner, PlannerChild } from \"@loom-forge/core\";\n\n/* ──────────────────────────────────────────────────────────────────────────────\n Planner `grid` — metade HEADLESS do adapter de dashboard. `placement = {x,y,w,h,…}`\n por nó → o array de layout do react-grid-layout (dados puros, sem RGL/react). O renderer\n (view) consome esse plano e normaliza pro <ReactGridLayout>. O DSL fica NO NOSSO formato;\n o RGL é só o meio de render.\n ────────────────────────────────────────────────────────────────────────────── */\n\nexport interface GridPlacement {\n x?: number;\n y?: number;\n w?: number;\n h?: number;\n minW?: number;\n maxW?: number;\n minH?: number;\n maxH?: number;\n static?: boolean;\n isDraggable?: boolean;\n isResizable?: boolean;\n}\n\nexport interface GridLayoutItem {\n i: string;\n x: number;\n y: number;\n w: number;\n h: number;\n minW?: number;\n maxW?: number;\n minH?: number;\n maxH?: number;\n static?: boolean;\n isDraggable?: boolean;\n isResizable?: boolean;\n}\n\nexport interface GridPlan {\n cols: number;\n rowHeight: number;\n items: GridLayoutItem[];\n}\n\nexport interface GridPlannerOptions {\n cols?: number;\n rowHeight?: number;\n}\n\nconst PASS = [\n \"minW\",\n \"maxW\",\n \"minH\",\n \"maxH\",\n \"static\",\n \"isDraggable\",\n \"isResizable\",\n] as const;\n\n/** a forma do `placement` que este layout lê — a de `GridPlacement`, como JSON Schema. */\nconst GRID_PLACEMENT = {\n type: \"object\",\n properties: {\n x: { type: \"number\" },\n y: { type: \"number\" },\n w: { type: \"number\" },\n h: { type: \"number\" },\n minW: { type: \"number\" },\n maxW: { type: \"number\" },\n minH: { type: \"number\" },\n maxH: { type: \"number\" },\n static: { type: \"boolean\" },\n isDraggable: { type: \"boolean\" },\n isResizable: { type: \"boolean\" },\n },\n} as const;\n\nexport function gridPlanner(opts: GridPlannerOptions = {}): LayoutPlanner {\n const cols = opts.cols ?? 12;\n const rowHeight = opts.rowHeight ?? 30;\n return {\n name: \"grid\",\n plan(children: PlannerChild[]): GridPlan {\n const items = children.map((c, idx): GridLayoutItem => {\n const p = (c.placement ?? {}) as GridPlacement;\n const item: GridLayoutItem = {\n i: c.id,\n x: p.x ?? 0,\n y: p.y ?? idx, // sem y → empilha\n w: p.w ?? cols,\n h: p.h ?? 1,\n };\n const bag = item as unknown as Record<string, unknown>;\n for (const k of PASS) if (p[k] !== undefined) bag[k] = p[k];\n return item;\n });\n return { cols, rowHeight, items };\n },\n placementSchema: GRID_PLACEMENT,\n };\n}\n","import type { ComponentType } from \"react\";\nimport RGLImport from \"react-grid-layout\";\nimport type { LayoutRenderer } from \"@loom-forge/react\";\nimport type { GridPlan } from \"./planner\";\n\n/* Metade de VIEW do adapter `grid` — transforma o plano (do planner headless) num\n <ReactGridLayout>. Interop CJS: o default export do react-grid-layout vem como `.default`\n em alguns bundlers. Pra responsivo, passe `component` (ex.: `WidthProvider(Responsive)`). */\n\nconst DefaultRGL = ((\n RGLImport as unknown as { default?: ComponentType<unknown> }\n).default ?? RGLImport) as ComponentType<Record<string, unknown>>;\n\nexport interface GridRendererOptions {\n /** largura em px (o RGL base exige). Default 1200. Pra auto-width responsivo, use `component`. */\n width?: number;\n margin?: [number, number];\n className?: string;\n /** componente RGL custom (ex.: `WidthProvider(Responsive)`). Default: react-grid-layout. */\n component?: ComponentType<Record<string, unknown>>;\n}\n\nexport function gridRenderer(opts: GridRendererOptions = {}): LayoutRenderer {\n const Grid = opts.component ?? DefaultRGL;\n return {\n name: \"grid\",\n render(plan, children) {\n const p = plan as GridPlan;\n return (\n <Grid\n className={opts.className}\n layout={p.items}\n cols={p.cols}\n rowHeight={p.rowHeight}\n width={opts.width ?? 1200}\n margin={opts.margin}\n >\n {p.items.map((it) => (\n <div key={it.i}>{children.get(it.i)}</div>\n ))}\n </Grid>\n );\n },\n };\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@loom-forge/grid",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "loom · o layout grid (dashboard) sobre react-grid-layout: o planner (headless) vira placement {x,y,w,h} em layout do RGL; o renderer monta o <ReactGridLayout>.",
6
+ "keywords": [
7
+ "grid",
8
+ "dashboard",
9
+ "layout",
10
+ "react-grid-layout"
11
+ ],
12
+ "license": "MIT",
13
+ "author": {
14
+ "name": "Anderson D. Rosa",
15
+ "url": "https://github.com/andersondrosa"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/andersondrosa/loom-forge.git",
20
+ "directory": "packages/grid"
21
+ },
22
+ "homepage": "https://github.com/andersondrosa/loom-forge/tree/main/packages/grid#readme",
23
+ "bugs": {
24
+ "url": "https://github.com/andersondrosa/loom-forge/issues"
25
+ },
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "sideEffects": false,
30
+ "exports": {
31
+ ".": {
32
+ "types": "./dist/index.d.ts",
33
+ "import": "./dist/index.js"
34
+ }
35
+ },
36
+ "files": [
37
+ "dist"
38
+ ],
39
+ "dependencies": {
40
+ "@loom-forge/core": "0.1.0",
41
+ "@loom-forge/react": "0.1.0"
42
+ },
43
+ "peerDependencies": {
44
+ "react": "^18 || ^19",
45
+ "react-grid-layout": "^1 || ^2"
46
+ },
47
+ "devDependencies": {
48
+ "@types/react": "^19.0.0",
49
+ "@types/react-dom": "^19.0.0",
50
+ "@types/react-grid-layout": "^2.1.0",
51
+ "react": "^19.0.0",
52
+ "react-dom": "^19.0.0",
53
+ "react-grid-layout": "^2.2.3",
54
+ "tsup": "^8.0.0",
55
+ "typescript": "^5.9.3"
56
+ },
57
+ "scripts": {
58
+ "build": "tsup",
59
+ "dev": "tsup --watch",
60
+ "test": "vitest run",
61
+ "test:watch": "vitest",
62
+ "typecheck": "tsc --noEmit",
63
+ "clean": "rm -rf dist"
64
+ }
65
+ }