playhtml 2.0.17 → 2.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/CHANGELOG.md +7 -4
- package/README.md +84 -95
- package/dist/main.d.ts +45 -4
- package/dist/playhtml.es.js +2170 -1508
- package/dist/playhtml.umd.js +32 -6
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
The format is based on Keep a Changelog and this project adheres to Semantic Versioning.
|
|
4
4
|
|
|
5
|
-
## 2.0
|
|
5
|
+
## 2.1.0 - 2024-01-27
|
|
6
6
|
|
|
7
|
-
**
|
|
7
|
+
- **NEW FEATURE** Added eventing support for imperative logic like showing confetti whenever someone clicks a button which don't depend on a reacting to a data value changing. See the README under "eventing" for more details on how to set this up.
|
|
8
|
+
- **BREAKING CHANGE** Changed the hash function used to generate element ids to be a stable length for long-term scalability. This will cause all elements without an `id` to be re-created to lose any persistent historical data. This was done to avoid duplicates and to swap to using a standard-length hash function (SHA-1). We still recommend you setting a unique `id` for each element to avoid any potential duplicates in the future, and using `selectorId` will not be affected by this change.
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
## 2.0.16 - 2024-01-04
|
|
11
|
+
|
|
12
|
+
- **BREAKING CHANGE** deprecated using non-object values as `defaultData` for elements. If you were using a single value before, instead, use an object with a `value` key. e.g. `defaultData: { value: "my value" }`. This allows for easier extension of the data in the future.
|
|
13
|
+
- **BREAKING CHANGE** deprecated `playhtml.init()` automatically being called to avoid side-effects upon import. This has been replaced with a new `init` file that you can directly import if you'd like to auto-initialize without any settings. See the README for more details.
|
|
11
14
|
- exported `setupPlayElements` to call to look for any new elements to initialize
|
|
12
15
|
|
|
13
16
|
## 2.0.7 - 2023-10-02
|
package/README.md
CHANGED
|
@@ -2,15 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
_interactive, collaborative html elements with a single data attribute_
|
|
4
4
|
|
|
5
|
-
playhtml is a fast, small (~300KB), and expressive library for magically creating collaborative interactive HTML elements that persist their state across sessions.
|
|
5
|
+
playhtml is a fast, small (~300KB), library-agnostic, and expressive library for magically creating collaborative interactive HTML elements that persist their state across sessions.
|
|
6
|
+
|
|
7
|
+
The simplest example is creating a shared, movable piece of HTML "furniture":
|
|
6
8
|
|
|
7
9
|
```html
|
|
8
10
|
<div id="couch" can-move style="font-size: 80px">🛋</div>
|
|
9
11
|
```
|
|
10
12
|
|
|
13
|
+
At a glance, playhtml supports:
|
|
14
|
+
|
|
15
|
+
- reactive data scoped at a per-element level
|
|
16
|
+
- sync and data persistence behavior customization (locally persisted, real-time synced, or globally persisted)
|
|
17
|
+
- custom events for imperative logic
|
|
18
|
+
- a range of magical plug-and-play and full customization
|
|
19
|
+
- _(coming soon)_ a web component library for "plug-and-play" collaborative elements
|
|
20
|
+
- _(coming soon)_ sharing state across domains
|
|
21
|
+
- _(coming soon)_ permissioning for behaviors
|
|
22
|
+
- _(coming soon)_ triggering behavior on a schedule (think cron)
|
|
23
|
+
- _(coming soon)_ custom data sources for long-term persistence
|
|
24
|
+
|
|
11
25
|
https://github.com/spencerc99/playhtml/assets/14796580/00e84e15-2c1c-4b4b-8e15-2af22f39db7a
|
|
12
26
|
|
|
13
|
-
|
|
27
|
+
playhtml is still in beta and active development. Join our [Discord community](https://discord.gg/h7sABTv8) to get help and show off what you've built!
|
|
14
28
|
|
|
15
29
|
## Usage
|
|
16
30
|
|
|
@@ -21,7 +35,7 @@ Head to the proper section depending on your technology preference:
|
|
|
21
35
|
|
|
22
36
|
### vanilla html / simple browser usage
|
|
23
37
|
|
|
24
|
-
To use this library, you can import the library
|
|
38
|
+
To use this library, you can import the library from a CDN (in this case we will use [unpkg.com](https://unpkg.com)). Please make sure to do this after all the HTML elements on the page and ensure that the HTML elements you are "magic-ifying" have an `id` set.
|
|
25
39
|
|
|
26
40
|
```html
|
|
27
41
|
<body>
|
|
@@ -34,19 +48,21 @@ To use this library, you can import the library and the associated styles from a
|
|
|
34
48
|
id="openSign"
|
|
35
49
|
<!-- INVALID EXAMPLE <img src="https://media2.giphy.com/media/lL7A3Li0YtFHq/giphy.gif?cid=ecf05e47ah89o71gzz7ke7inrgb1ai1xcbrjnqdf7o890118&ep=v1_stickers_search&rid=giphy.gif" can-move /> -->
|
|
36
50
|
<!-- import the script -->
|
|
51
|
+
|
|
52
|
+
<!-- Option 1 (simplest, no customization) -->
|
|
53
|
+
<script
|
|
54
|
+
type="module"
|
|
55
|
+
src="https://unpkg.com/playhtml@latest/dist/init.es.js"
|
|
56
|
+
></script>
|
|
57
|
+
|
|
58
|
+
<!-- Option 2 (customize options to use your own partykit provider or specify the room) -->
|
|
37
59
|
<script type="module">
|
|
38
60
|
import "https://unpkg.com/playhtml@latest";
|
|
39
|
-
playhtml.init(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
// host: "mypartykit.user.partykit.dev"
|
|
44
|
-
// })
|
|
61
|
+
playhtml.init({
|
|
62
|
+
room: "my-room",
|
|
63
|
+
host: "mypartykit.user.partykit.dev",
|
|
64
|
+
});
|
|
45
65
|
</script>
|
|
46
|
-
<link
|
|
47
|
-
rel="stylesheet"
|
|
48
|
-
href="https://unpkg.com/playhtml@latest/dist/style.css"
|
|
49
|
-
/>
|
|
50
66
|
</body>
|
|
51
67
|
```
|
|
52
68
|
|
|
@@ -61,74 +77,52 @@ If you have dynamic elements that are hydrated after the initial load, you can c
|
|
|
61
77
|
</script>
|
|
62
78
|
```
|
|
63
79
|
|
|
64
|
-
|
|
80
|
+
#### eventing
|
|
65
81
|
|
|
66
|
-
|
|
67
|
-
`@playhtml/react` provides components out of the box corresponding to each of the capabilities. It manages all the state syncing for you, so you can reactively render your component based on whatever data is coming in.
|
|
82
|
+
You can set up imperative logic that doesn't depend on a data value changing (like triggering confetti when someone clicks in an area) by registering events with playhtml. You can either pass in a list of events when you call `playhtml.init` or you can call `playhtml.registerPlayEventListener` to register an event at any time.
|
|
68
83
|
|
|
69
|
-
|
|
84
|
+
https://github.com/spencerc99/playhtml/assets/14796580/bd8ecfaf-73ab-4aa2-9312-8917809f52a2
|
|
70
85
|
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
86
|
+
```html
|
|
87
|
+
<div
|
|
88
|
+
style="width: 400px; height: 400px; border: 1px red solid"
|
|
89
|
+
id="confettiZone"
|
|
90
|
+
>
|
|
91
|
+
<h1>CONFETTI ZONE</h1>
|
|
92
|
+
</div>
|
|
93
|
+
<!-- Import confetti library -->
|
|
94
|
+
<script src="https://cdn.jsdelivr.net/npm/canvas-confetti@1.9.2/dist/confetti.browser.min.js"></script>
|
|
95
|
+
<script>
|
|
96
|
+
confettiZone.addEventListener("click", onClickConfettiZone);
|
|
97
|
+
function onClickConfettiZone(e) {
|
|
98
|
+
playhtml.dispatchPlayEvent({ type: "confetti" });
|
|
99
|
+
}
|
|
100
|
+
</script>
|
|
101
|
+
<script type="module">
|
|
102
|
+
import "https://unpkg.com/playhtml@latest";
|
|
103
|
+
playhtml.init({
|
|
104
|
+
events: {
|
|
105
|
+
confetti: {
|
|
106
|
+
type: "confetti",
|
|
107
|
+
onEvent: (data) => {
|
|
108
|
+
window.confetti({
|
|
109
|
+
particleCount: 100,
|
|
110
|
+
spread: 70,
|
|
111
|
+
origin: { y: 0.6 },
|
|
112
|
+
});
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
</script>
|
|
101
118
|
```
|
|
102
119
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
**next.js**
|
|
106
|
-
Handling Next is more difficult due to the browser-first nature of `playhtml` and how you have to handle the same code running server-side with Next.
|
|
107
|
-
|
|
108
|
-
To initialize `playhtml`, you'll need to dynamically import `playhtml` and call init in a `useEffect` in the top-level component.
|
|
109
|
-
|
|
110
|
-
```tsx
|
|
111
|
-
async function initPlayhtml() {
|
|
112
|
-
const playhtml = (await import("@playhtml/react")).playhtml;
|
|
120
|
+
### react-based frameworks
|
|
113
121
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
// ... my component
|
|
117
|
-
useEffect(() => {
|
|
118
|
-
void initPlayhtml();
|
|
119
|
-
});
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
Then, in your components, you'll need to turn off `ssr` for dynamically importing the `playhtml` elements.
|
|
122
|
+
**react**
|
|
123
|
+
`@playhtml/react` provides components out of the box corresponding to each of the capabilities. It manages all the state syncing for you, so you can reactively render your component based on whatever data is coming in.
|
|
123
124
|
|
|
124
|
-
|
|
125
|
-
import type { CanPlayElement as CanPlayElementType } from "@playhtml/react";
|
|
126
|
-
import dynamic from "next/dynamic";
|
|
127
|
-
const CanPlayElement = dynamic(
|
|
128
|
-
() => import("@playhtml/react").then((c) => c.CanPlayElement),
|
|
129
|
-
{ ssr: false }
|
|
130
|
-
) as typeof CanPlayElementType;
|
|
131
|
-
```
|
|
125
|
+
Refer to the [@playhtml/react README](https://github.com/spencerc99/playhtml/tree/main/packages/react) for the getting started guide.
|
|
132
126
|
|
|
133
127
|
---
|
|
134
128
|
|
|
@@ -138,11 +132,9 @@ If you're trying this out and having trouble, please message me ([email](mailto:
|
|
|
138
132
|
|
|
139
133
|
## Examples
|
|
140
134
|
|
|
141
|
-
|
|
135
|
+
To get started, you can find examples under the `example` folder or React examples under `packages/react/examples`.
|
|
142
136
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
A full list can be found in `types.ts` under `TagType`.
|
|
137
|
+
For full-fledged examples, check out our [community gallery](https://coda.io/@spencer/playhtml) for more inspiration on what you can do! And [submit your own](https://coda.io/form/playhtml-example_dnUR7xNE7wz) once you've made one.
|
|
146
138
|
|
|
147
139
|
## Custom Capabilities
|
|
148
140
|
|
|
@@ -159,24 +151,21 @@ https://github.com/spencerc99/playhtml/assets/14796580/fae669b1-b3e2-404e-bd7a-3
|
|
|
159
151
|
<!-- IMPORTANT: this data must be set _before_ importing the playhtml library. -->
|
|
160
152
|
<script>
|
|
161
153
|
let candle = document.getElementById("customCandle");
|
|
162
|
-
candle.defaultData = true;
|
|
154
|
+
candle.defaultData = { on: true };
|
|
163
155
|
candle.onClick = (_e, { data, setData }) => {
|
|
164
|
-
setData(!data);
|
|
156
|
+
setData({ on: !data.on });
|
|
165
157
|
};
|
|
166
|
-
candle.updateElement = ({ data }) => {
|
|
167
|
-
|
|
158
|
+
candle.updateElement = ({ element, data }) => {
|
|
159
|
+
element.src = data.on ? "/candle-gif.gif" : "/candle-off.png";
|
|
168
160
|
};
|
|
169
161
|
candle.resetShortcut = "shiftKey";
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
setData(!data);
|
|
173
|
-
};
|
|
174
|
-
// The above statement could also be done using the `additionalSetup`
|
|
162
|
+
|
|
163
|
+
// If you wanted to trigger this on hover, you could use `onMount`
|
|
175
164
|
// which can initialize several different event listeners and custom logic.
|
|
176
|
-
// customCandle.
|
|
177
|
-
// customCandle.addEventListener("
|
|
165
|
+
// customCandle.onMount = ({ getData, setData }) => {
|
|
166
|
+
// customCandle.addEventListener("mouseover", (_e) => {
|
|
178
167
|
// const data = getData();
|
|
179
|
-
// setData(!data);
|
|
168
|
+
// setData({on: !data.on}});
|
|
180
169
|
// });
|
|
181
170
|
// };
|
|
182
171
|
</script>
|
|
@@ -186,15 +175,11 @@ https://github.com/spencerc99/playhtml/assets/14796580/fae669b1-b3e2-404e-bd7a-3
|
|
|
186
175
|
import "https://unpkg.com/playhtml@latest";
|
|
187
176
|
playhtml.init();
|
|
188
177
|
</script>
|
|
189
|
-
<link
|
|
190
|
-
rel="stylesheet"
|
|
191
|
-
href="https://unpkg.com/playhtml@latest/dist/style.css"
|
|
192
|
-
/>
|
|
193
178
|
```
|
|
194
179
|
|
|
195
180
|
See all supported properties in the `ElementInitializer` [object in `types.ts`](https://github.com/spencerc99/playhtml/blob/main/src/types.ts#L13).
|
|
196
181
|
|
|
197
|
-
The only required properties are `defaultData`, `updateElement` and some kind of setup to trigger those functions (in this case, `onClick`, but you can add custom event listeners and logic using the `
|
|
182
|
+
The only required properties are `defaultData`, `updateElement` and some kind of setup to trigger those functions (in this case, `onClick`, but you can add custom event listeners and logic using the `onMount` property). See more examples based on the definitions for the included capabilities in [`elements.ts`](https://github.com/spencerc99/playhtml/blob/main/src/elements.ts#L72).
|
|
198
183
|
|
|
199
184
|
If you make something fun, please show me! This is designed as an open library for anyone to add on new interactions and capabilities, so we [welcome contributions](https://github.com/spencerc99/playhtml/blob/main/CONTRIBUTING.md) for new built-in capabilities.
|
|
200
185
|
|
|
@@ -240,6 +225,10 @@ Creates an element that can be resized using a `scale` `transform`. Clicking the
|
|
|
240
225
|
|
|
241
226
|
Creates a rotatable element using a `rotate` `transform` on the element. Dragging the element to the left or right will rotate it counter-clockwise and clockwise respectively.
|
|
242
227
|
|
|
228
|
+
## Help & Community
|
|
229
|
+
|
|
230
|
+
Join our [Discord community](https://discord.gg/h7sABTv8) to get help and show off what you've built!
|
|
231
|
+
|
|
243
232
|
## Data Policy
|
|
244
233
|
|
|
245
234
|
Currently all data is stored by a [Partykit](https://partykit.dev) instance under my account and is not encrypted. This means that anyone with the room name can access the data. In the future, I'd like to enable providing your own custom persistence options via object storage providers (or any generic service that can accept a POST endpoint with a dump of the data).
|
package/dist/main.d.ts
CHANGED
|
@@ -5,11 +5,16 @@ import { ElementData } from '@playhtml/common';
|
|
|
5
5
|
import { ElementEventHandlerData } from '@playhtml/common';
|
|
6
6
|
import { ElementInitializer } from '@playhtml/common';
|
|
7
7
|
import { ElementSetupData } from '@playhtml/common';
|
|
8
|
+
import { EventMessage } from '@playhtml/common';
|
|
8
9
|
import { ModifierKey } from '@playhtml/common';
|
|
10
|
+
import { PlayEvent } from '@playhtml/common';
|
|
11
|
+
import { RegisteredPlayEvent } from '@playhtml/common';
|
|
9
12
|
import { TagType } from '@playhtml/common';
|
|
10
13
|
import * as Y from 'yjs';
|
|
11
14
|
import YPartyKitProvider from 'y-partykit/provider';
|
|
12
15
|
|
|
16
|
+
declare function dispatchPlayEvent(message: EventMessage): void;
|
|
17
|
+
|
|
13
18
|
declare class ElementHandler<T = any, U = any, V = any> {
|
|
14
19
|
defaultData: T;
|
|
15
20
|
localData: U;
|
|
@@ -47,7 +52,7 @@ declare class ElementHandler<T = any, U = any, V = any> {
|
|
|
47
52
|
* Public-use setter for data that makes the change to all clients.
|
|
48
53
|
*/
|
|
49
54
|
setData(data: T): void;
|
|
50
|
-
|
|
55
|
+
setMyAwareness(data: V): void;
|
|
51
56
|
setDataDebounced(data: T): void;
|
|
52
57
|
/**
|
|
53
58
|
* Resets the element to its default state.
|
|
@@ -55,13 +60,34 @@ declare class ElementHandler<T = any, U = any, V = any> {
|
|
|
55
60
|
reset(): void;
|
|
56
61
|
}
|
|
57
62
|
|
|
58
|
-
export declare interface InitOptions {
|
|
63
|
+
export declare interface InitOptions<T = any> {
|
|
64
|
+
/**
|
|
65
|
+
* The room to connect users to (this should be a string that matches the other users
|
|
66
|
+
* that you want a given user to connect with).
|
|
67
|
+
*
|
|
68
|
+
* All rooms are automatically prefixed with their host (`window.location.hostname`) to prevent
|
|
69
|
+
* conflicting with other people's sites.
|
|
70
|
+
* Defaults to `window.location.pathname + window.location.search. You can customize this by
|
|
71
|
+
* passing in your own room dynamically
|
|
72
|
+
*/
|
|
59
73
|
room?: string;
|
|
74
|
+
/**
|
|
75
|
+
* Provide your own partykit host if you'd like to run your own server and customize the logic.
|
|
76
|
+
*/
|
|
60
77
|
host?: string;
|
|
78
|
+
/**
|
|
79
|
+
* Optionally provide your own map of capabilities
|
|
80
|
+
*/
|
|
61
81
|
extraCapabilities?: Record<string, ElementInitializer>;
|
|
82
|
+
/**
|
|
83
|
+
* A mapping of event types to PlayEvents. Allows specifying of imperative logic to trigger when a
|
|
84
|
+
* client triggers some event. Automatically listens to native DOM events to trigger these.
|
|
85
|
+
*
|
|
86
|
+
*/
|
|
87
|
+
events?: Record<string, PlayEvent<T>>;
|
|
62
88
|
}
|
|
63
89
|
|
|
64
|
-
declare function initPlayHTML({ room: inputRoom, host, extraCapabilities, }?: InitOptions): YPartyKitProvider | undefined;
|
|
90
|
+
declare function initPlayHTML({ room: inputRoom, host, extraCapabilities, events, }?: InitOptions): YPartyKitProvider | undefined;
|
|
65
91
|
|
|
66
92
|
export declare const playhtml: PlayHTMLComponents;
|
|
67
93
|
|
|
@@ -73,10 +99,25 @@ declare interface PlayHTMLComponents {
|
|
|
73
99
|
setupPlayElementForTag: typeof setupPlayElementForTag;
|
|
74
100
|
globalData: Y.Map<any> | undefined;
|
|
75
101
|
elementHandlers: Map<string, Map<string, ElementHandler>> | undefined;
|
|
102
|
+
eventHandlers: Map<string, Array<RegisteredPlayEvent>> | undefined;
|
|
103
|
+
dispatchPlayEvent: typeof dispatchPlayEvent;
|
|
104
|
+
registerPlayEventListener: typeof registerPlayEventListener;
|
|
105
|
+
removePlayEventListener: typeof removePlayEventListener;
|
|
76
106
|
}
|
|
77
107
|
|
|
108
|
+
/**
|
|
109
|
+
* Registers the given event listener.
|
|
110
|
+
* Returns a unique ID corresponding to the listener.
|
|
111
|
+
*/
|
|
112
|
+
declare function registerPlayEventListener(type: string, event: Omit<PlayEvent, "type">): string;
|
|
113
|
+
|
|
78
114
|
declare function removePlayElement(element: Element | null): void;
|
|
79
115
|
|
|
116
|
+
/**
|
|
117
|
+
* Removes the event listener with the given type and id.
|
|
118
|
+
*/
|
|
119
|
+
declare function removePlayEventListener(type: string, id: string): void;
|
|
120
|
+
|
|
80
121
|
/**
|
|
81
122
|
* Sets up any playhtml elements that are currently on the page.
|
|
82
123
|
*
|
|
@@ -92,6 +133,6 @@ declare function setupPlayElement(element: Element, { ignoreIfAlreadySetup }?: {
|
|
|
92
133
|
/**
|
|
93
134
|
* Sets up a playhtml element to handle the given tag's capabilities.
|
|
94
135
|
*/
|
|
95
|
-
declare function setupPlayElementForTag<T extends TagType | string>(element: HTMLElement, tag: T): void
|
|
136
|
+
declare function setupPlayElementForTag<T extends TagType | string>(element: HTMLElement, tag: T): Promise<void>;
|
|
96
137
|
|
|
97
138
|
export { }
|