@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 +48 -0
- package/dist/index.d.ts +53 -0
- package/dist/index.js +80 -0
- package/dist/index.js.map +1 -0
- package/package.json +65 -0
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.
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|