playhtml 2.0.18 → 2.1.1

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 CHANGED
@@ -2,9 +2,14 @@
2
2
 
3
3
  The format is based on Keep a Changelog and this project adheres to Semantic Versioning.
4
4
 
5
- ## [Unreleased]
5
+ ## 2.1.1 - 2024-01-30
6
6
 
7
- - **BREAKING CHANGE**Changed the hash function used to generate element ids. 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.
7
+ - added an `init.js` file export which can be imported to auto-initialize with default settings. Designed to be simplest way to get started with playhtml.
8
+
9
+ ## 2.1.0 - 2024-01-27
10
+
11
+ - **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.
12
+ - **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
13
 
9
14
  ## 2.0.16 - 2024-01-04
10
15
 
package/README.md CHANGED
@@ -2,17 +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. For example, you can create a movable piece of HTML "furniture" by adding the `can-move` attribute:
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
 
11
- https://github.com/spencerc99/playhtml/assets/14796580/00e84e15-2c1c-4b4b-8e15-2af22f39db7a
13
+ At a glance, playhtml supports:
12
14
 
13
- If you enjoy this, please consider [sponsoring the project or sending a small donation](https://github.com/sponsors/spencerc99). This helps ensure that the library is maintained and improved over time and funds the hosting costs for the syncing and persistence services.
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
14
24
 
15
- Join our [Discord community](https://discord.gg/h7sABTv8) to get help and show off what you've built!
25
+ https://github.com/spencerc99/playhtml/assets/14796580/00e84e15-2c1c-4b4b-8e15-2af22f39db7a
26
+
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!
16
28
 
17
29
  ## Usage
18
30
 
@@ -23,7 +35,7 @@ Head to the proper section depending on your technology preference:
23
35
 
24
36
  ### vanilla html / simple browser usage
25
37
 
26
- To use this library, you can import the library and the associated styles 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.
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.
27
39
 
28
40
  ```html
29
41
  <body>
@@ -43,12 +55,12 @@ To use this library, you can import the library and the associated styles from a
43
55
  src="https://unpkg.com/playhtml@latest/dist/init.es.js"
44
56
  ></script>
45
57
 
46
- <!-- Option 2 (customize options to use your own partykit provider or specify the room) -->
58
+ <!-- Option 2 (customize options to specify the room everyone connects to (a unique ID) or use your own partykit provider) -->
47
59
  <script type="module">
48
60
  import "https://unpkg.com/playhtml@latest";
49
61
  playhtml.init({
50
62
  room: "my-room",
51
- host: "mypartykit.user.partykit.dev",
63
+ host: `${myPartykitUser}.partykit.dev`,
52
64
  });
53
65
  </script>
54
66
  </body>
@@ -65,74 +77,52 @@ If you have dynamic elements that are hydrated after the initial load, you can c
65
77
  </script>
66
78
  ```
67
79
 
68
- ### react / next.js / other frameworks
80
+ #### eventing
69
81
 
70
- **react**
71
- `@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.
72
83
 
73
- Install by using your preferred package manager
84
+ https://github.com/spencerc99/playhtml/assets/14796580/bd8ecfaf-73ab-4aa2-9312-8917809f52a2
74
85
 
75
- ```bash
76
- npm install @playhtml/react # or
77
- # yarn add @playhtml/react
78
- ```
79
-
80
- The callback handlers are slightly different to match the reactive patterns of React. For example, instead of `updateElement`, which is a vanilla javascript recreation of a reactive state management system, you can simply use your own state hooks or call `setData` to modify the shared state.
81
-
82
- You must import `playhtml` from the package and call `playhtml.init` somewhere before your components are rendered. This can be done anywhere in the code (unless you are using a framework with Server-Side Rendering, SSR, like with Next.JS, see below for how to handle this).
83
-
84
- ```tsx
85
- // candle
86
- playhtml.init();
87
- export function Candle() {
88
- return (
89
- <CanPlayElement
90
- defaultData={{ on: false }}
91
- onClick={(_e, { data, setData }) => {
92
- setData({ on: !data.on });
93
- }}
94
- >
95
- {({ data }) => (
96
- <img
97
- src={data.on ? "/candle-gif.gif" : "/candle-off.png"}
98
- selector-id=".candle"
99
- className="candle"
100
- />
101
- )}
102
- </CanPlayElement>
103
- );
104
- }
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>
105
118
  ```
106
119
 
107
- Refer to `packages/react/example.tsx` for a full list of examples.
108
-
109
- **next.js**
110
- 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.
111
-
112
- To initialize `playhtml`, you'll need to dynamically import `playhtml` and call init in a `useEffect` in the top-level component.
113
-
114
- ```tsx
115
- async function initPlayhtml() {
116
- const playhtml = (await import("@playhtml/react")).playhtml;
117
-
118
- playhtml.init();
119
- }
120
- // ... my component
121
- useEffect(() => {
122
- void initPlayhtml();
123
- });
124
- ```
120
+ ### react-based frameworks
125
121
 
126
- 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.
127
124
 
128
- ```tsx
129
- import type { CanPlayElement as CanPlayElementType } from "@playhtml/react";
130
- import dynamic from "next/dynamic";
131
- const CanPlayElement = dynamic(
132
- () => import("@playhtml/react").then((c) => c.CanPlayElement),
133
- { ssr: false }
134
- ) as typeof CanPlayElementType;
135
- ```
125
+ Refer to the [@playhtml/react README](https://github.com/spencerc99/playhtml/tree/main/packages/react) for the getting started guide.
136
126
 
137
127
  ---
138
128
 
@@ -142,11 +132,9 @@ If you're trying this out and having trouble, please message me ([email](mailto:
142
132
 
143
133
  ## Examples
144
134
 
145
- Check out the [full gallery of community examples](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
-
147
- ## Capabilities
135
+ To get started, you can find examples under the `example` folder or React examples under `packages/react/examples`.
148
136
 
149
- 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.
150
138
 
151
139
  ## Custom Capabilities
152
140
 
@@ -172,9 +160,9 @@ https://github.com/spencerc99/playhtml/assets/14796580/fae669b1-b3e2-404e-bd7a-3
172
160
  };
173
161
  candle.resetShortcut = "shiftKey";
174
162
 
175
- // If you wanted to trigger this on hover, you could use `additionalSetup`
163
+ // If you wanted to trigger this on hover, you could use `onMount`
176
164
  // which can initialize several different event listeners and custom logic.
177
- // customCandle.additionalSetup = ({ getData, setData }) => {
165
+ // customCandle.onMount = ({ getData, setData }) => {
178
166
  // customCandle.addEventListener("mouseover", (_e) => {
179
167
  // const data = getData();
180
168
  // setData({on: !data.on}});
@@ -191,7 +179,7 @@ https://github.com/spencerc99/playhtml/assets/14796580/fae669b1-b3e2-404e-bd7a-3
191
179
 
192
180
  See all supported properties in the `ElementInitializer` [object in `types.ts`](https://github.com/spencerc99/playhtml/blob/main/src/types.ts#L13).
193
181
 
194
- 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 `additionalSetup` 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).
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).
195
183
 
196
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.
197
185
 
@@ -0,0 +1,2 @@
1
+ import { playhtml as i } from "./playhtml.es.js";
2
+ i.init({});
package/dist/main.d.ts CHANGED
@@ -3,13 +3,18 @@
3
3
  import { ElementAwarenessEventHandlerData } from '@playhtml/common';
4
4
  import { ElementData } from '@playhtml/common';
5
5
  import { ElementEventHandlerData } from '@playhtml/common';
6
- import { ElementInitializer } from '../../common/src/index';
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';
9
- import { TagType } from '../../common/src/index';
10
+ import { PlayEvent } from '@playhtml/common';
11
+ import { RegisteredPlayEvent } from '@playhtml/common';
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;
@@ -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
  *