ghosto 0.0.2
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/LICENSE +21 -0
- package/README.md +161 -0
- package/dist/index.d.mts +77 -0
- package/dist/index.mjs +1284 -0
- package/package.json +47 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 1qh
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# ogrid
|
|
2
|
+
|
|
3
|
+
**The dashboard grid that builds itself.**
|
|
4
|
+
|
|
5
|
+
Drop widgets in. They measure themselves, find their place, and snap to a grid. Drag to rearrange. Resize to fit. Copy the config. Paste into code. Ship it.
|
|
6
|
+
|
|
7
|
+
Your users can do the same thing — toggle edit mode, customize their layout, and it persists automatically. One prop.
|
|
8
|
+
|
|
9
|
+
No other grid library does this. They all make you guess dimensions, wire up persistence, build dev tools from scratch. ogrid does it all.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
bun add ogrid
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
import { Grid } from 'ogrid'
|
|
21
|
+
import 'ogrid/styles.css'
|
|
22
|
+
|
|
23
|
+
const config = {
|
|
24
|
+
layout: [
|
|
25
|
+
{ i: 'revenue', w: 12, fill: true },
|
|
26
|
+
{ i: 'users', w: 12 },
|
|
27
|
+
{ i: 'chart', w: 16, fill: true },
|
|
28
|
+
{ i: 'table', w: 8 },
|
|
29
|
+
],
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
<Grid config={config}>
|
|
33
|
+
<RevenueChart key="revenue" />
|
|
34
|
+
<UserCount key="users" />
|
|
35
|
+
<ActivityChart key="chart" />
|
|
36
|
+
<RecentTable key="table" />
|
|
37
|
+
</Grid>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
That’s it. Each child needs a unique `key` matching a layout item’s `i`. Items without explicit `x`/`y` are auto-placed. Items with `fill: true` stretch to available height. Everything else sizes to content.
|
|
41
|
+
|
|
42
|
+
## Edit mode
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
<Grid config={config} editable>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
One prop. Drag handles appear. Items become resizable. Users can rearrange everything.
|
|
49
|
+
|
|
50
|
+
## Persistence
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
<Grid config={config} editable id="dashboard" persist>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Two more props. User customizations save to localStorage automatically. Reload the page — layout restored. Reset clears it.
|
|
57
|
+
|
|
58
|
+
## The Panel
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
<Grid.Panel />
|
|
62
|
+
<Grid config={config} editable id="dashboard" persist>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
A toolbar that appears when `editable` is true. Sliders for columns, gap, and row height. Toggle cell boundaries. Copy the current layout as TypeScript. Reset to defaults. Accepts `children` (rendered at start) and `trailing` (rendered at end) for your own controls.
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
<Grid.Panel trailing={<ThemeToggle />}>
|
|
69
|
+
<EditSwitch />
|
|
70
|
+
</Grid.Panel>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Dev workflow
|
|
74
|
+
|
|
75
|
+
1. Add `editable` to your Grid
|
|
76
|
+
2. Open the Panel — drag widgets, tweak sliders, toggle rings to see boundaries
|
|
77
|
+
3. Click **Copy** — get the exact `GridConfig` as TypeScript
|
|
78
|
+
4. Paste into your code as the default config
|
|
79
|
+
5. Remove `editable` — ship a static, clean dashboard
|
|
80
|
+
|
|
81
|
+
## User customization workflow
|
|
82
|
+
|
|
83
|
+
1. User triggers edit mode (button, keyboard shortcut, easter egg — you decide)
|
|
84
|
+
2. Drag handles and resize handles appear
|
|
85
|
+
3. User rearranges their dashboard
|
|
86
|
+
4. Layout persists to localStorage via `id` + `persist`
|
|
87
|
+
5. User exits edit mode — clean view, their layout preserved
|
|
88
|
+
6. **Reset** in Panel clears saved layout, restores your defaults
|
|
89
|
+
|
|
90
|
+
## Props
|
|
91
|
+
|
|
92
|
+
### `<Grid>`
|
|
93
|
+
|
|
94
|
+
| Prop | Type | Default | Description |
|
|
95
|
+
| ---------------- | ------------------------------ | ------- | ---------------------------------------- |
|
|
96
|
+
| `config` | `GridConfig` | — | Layout configuration |
|
|
97
|
+
| `editable` | `boolean` | `false` | Enable drag, resize, and handles |
|
|
98
|
+
| `id` | `string` | — | Storage key for persistence |
|
|
99
|
+
| `persist` | `boolean` | `false` | Save user customizations to localStorage |
|
|
100
|
+
| `onConfigChange` | `(config: GridConfig) => void` | — | Called after drag/resize/slider changes |
|
|
101
|
+
|
|
102
|
+
### `<Grid.Panel>`
|
|
103
|
+
|
|
104
|
+
| Prop | Type | Description |
|
|
105
|
+
| ---------- | ----------- | ----------------------------------------- |
|
|
106
|
+
| `children` | `ReactNode` | Controls rendered at the start of the bar |
|
|
107
|
+
| `trailing` | `ReactNode` | Controls rendered at the end of the bar |
|
|
108
|
+
|
|
109
|
+
Auto-hides when `editable` is `false` and no `children`/`trailing` are provided.
|
|
110
|
+
|
|
111
|
+
### `GridConfig`
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
interface GridConfig {
|
|
115
|
+
cols?: number // default: 24
|
|
116
|
+
gap?: number // default: 16px
|
|
117
|
+
rowHeight?: number // default: 50px
|
|
118
|
+
layout?: LayoutItem[]
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
interface LayoutItem {
|
|
122
|
+
i: string // must match child's key
|
|
123
|
+
w?: number // width in columns (default: full width)
|
|
124
|
+
h?: number // height in rows
|
|
125
|
+
x?: number // column position (auto-placed if omitted)
|
|
126
|
+
y?: number // row position (auto-placed if omitted)
|
|
127
|
+
fill?: boolean // stretch to available height
|
|
128
|
+
minH?: number // minimum height (auto-computed from content)
|
|
129
|
+
minW?: number // minimum width
|
|
130
|
+
className?: string // CSS classes for the cell
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Hooks
|
|
135
|
+
|
|
136
|
+
### `useGridConfig()`
|
|
137
|
+
|
|
138
|
+
Returns the current `GridConfig` or `null` during measurement.
|
|
139
|
+
|
|
140
|
+
```tsx
|
|
141
|
+
import { useGridConfig } from 'ogrid'
|
|
142
|
+
|
|
143
|
+
const config = useGridConfig()
|
|
144
|
+
// { cols: 24, gap: 16, layout: [...] }
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## How it works
|
|
148
|
+
|
|
149
|
+
1. **Measurement phase** — Grid renders invisible, measures each child’s natural height via ResizeObserver
|
|
150
|
+
2. **Layout computation** — Column-based placement algorithm finds optimal positions
|
|
151
|
+
3. **Content constraints** — Resize can never shrink a cell below its content height
|
|
152
|
+
4. **Freeform mode** — After measurement, items can be freely dragged and resized
|
|
153
|
+
5. **Responsive** — Below 768px, switches to single-column compact mode
|
|
154
|
+
|
|
155
|
+
## Built on
|
|
156
|
+
|
|
157
|
+
- [react-grid-layout](https://github.com/1qh/react-grid-layout) — the battle-tested layout engine that handles drag, resize, collision detection, and smooth animations
|
|
158
|
+
|
|
159
|
+
## License
|
|
160
|
+
|
|
161
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
import * as _$zustand from "zustand";
|
|
3
|
+
import * as _$react_jsx_runtime0 from "react/jsx-runtime";
|
|
4
|
+
|
|
5
|
+
//#region src/mascot-canvas.d.ts
|
|
6
|
+
declare const MascotCanvas: () => _$react_jsx_runtime0.JSX.Element;
|
|
7
|
+
//#endregion
|
|
8
|
+
//#region src/mascot.d.ts
|
|
9
|
+
declare const MascotBubbleFollower: ({
|
|
10
|
+
children,
|
|
11
|
+
offsetY
|
|
12
|
+
}: {
|
|
13
|
+
children: ReactNode;
|
|
14
|
+
offsetY?: number;
|
|
15
|
+
}) => _$react_jsx_runtime0.JSX.Element;
|
|
16
|
+
declare const Mascot: ({
|
|
17
|
+
bubble,
|
|
18
|
+
size
|
|
19
|
+
}: {
|
|
20
|
+
bubble?: ReactNode;
|
|
21
|
+
size?: number;
|
|
22
|
+
}) => _$react_jsx_runtime0.JSX.Element;
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/use-mascot-pose.d.ts
|
|
25
|
+
interface MascotPose {
|
|
26
|
+
bodyScreenPx: {
|
|
27
|
+
x: number;
|
|
28
|
+
y: number;
|
|
29
|
+
};
|
|
30
|
+
homeWorld: {
|
|
31
|
+
x: number;
|
|
32
|
+
y: number;
|
|
33
|
+
z: number;
|
|
34
|
+
};
|
|
35
|
+
setBodyScreenPx: (x: number, y: number) => void;
|
|
36
|
+
setHomeWorld: (x: number, y: number, z: number) => void;
|
|
37
|
+
}
|
|
38
|
+
declare const useMascotPose: _$zustand.UseBoundStore<_$zustand.StoreApi<MascotPose>>;
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/constants.d.ts
|
|
41
|
+
declare const CHANNEL_DECAY_S: {
|
|
42
|
+
readonly affection: 30;
|
|
43
|
+
readonly affinity: number;
|
|
44
|
+
readonly annoyance: 3;
|
|
45
|
+
readonly attention: 4;
|
|
46
|
+
readonly boredom: 600;
|
|
47
|
+
readonly cheer: 0.6;
|
|
48
|
+
readonly curiosity: 0.8;
|
|
49
|
+
readonly dizzy: 0.5;
|
|
50
|
+
readonly energy: 1800;
|
|
51
|
+
readonly excitement: 0.5;
|
|
52
|
+
readonly fatigue: 3600;
|
|
53
|
+
readonly heat: 0.6;
|
|
54
|
+
readonly hunger: 1800;
|
|
55
|
+
readonly joy: 0.6;
|
|
56
|
+
readonly loneliness: 40;
|
|
57
|
+
readonly magnetism: 1.5;
|
|
58
|
+
readonly worry: 4;
|
|
59
|
+
};
|
|
60
|
+
type ChannelName = keyof typeof CHANNEL_DECAY_S;
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region src/use-mascot-channels.d.ts
|
|
63
|
+
type ChannelName$1 = keyof typeof CHANNEL_DECAY_S;
|
|
64
|
+
type Channels = Record<ChannelName$1, number>;
|
|
65
|
+
interface MascotState {
|
|
66
|
+
bump: (name: ChannelName$1, delta: number, cap?: number) => void;
|
|
67
|
+
channels: Channels;
|
|
68
|
+
decay: (dt: number) => void;
|
|
69
|
+
set: (name: ChannelName$1, value: number) => void;
|
|
70
|
+
setSmoothK: (k: number) => void;
|
|
71
|
+
smoothed: () => Channels;
|
|
72
|
+
snapshot: () => Channels;
|
|
73
|
+
tick: (dt: number) => void;
|
|
74
|
+
}
|
|
75
|
+
declare const useMascotChannels: _$zustand.UseBoundStore<_$zustand.StoreApi<MascotState>>;
|
|
76
|
+
//#endregion
|
|
77
|
+
export { type ChannelName, Mascot, MascotBubbleFollower, MascotCanvas, useMascotChannels, useMascotPose };
|