@orieljs/live 0.1.0-alpha.4 → 0.1.0-alpha.5

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 CHANGED
@@ -33,7 +33,51 @@ Route.get('/counter', Counter)
33
33
 
34
34
  Embed a component in any view with `<Live component={Counter} />`. In the browser, components are driven by `@orieljs/live-client`.
35
35
 
36
- Known limitations:
36
+ ## Pagination
37
+
38
+ ```tsx
39
+ import { Component, Pagination, WithPagination } from '@orieljs/framework'
40
+ import { Post } from '../Models/Post'
41
+
42
+ export default class Posts extends WithPagination(Component) {
43
+ search = ''
44
+
45
+ updatedSearch() {
46
+ this.resetPage()
47
+ }
48
+
49
+ async render() {
50
+ const posts = await Post.whereLike('title', `%${this.search}%`).latest().paginate(10)
51
+ return (
52
+ <div>
53
+ <input o:model-live="search" aria-label="Search" />
54
+ {posts.items().map((post) => <p>{post.title}</p>)}
55
+ <Pagination paginator={posts} class="flex gap-2" />
56
+ </div>
57
+ )
58
+ }
59
+ }
60
+ ```
61
+
62
+ `paginate()` in the component (in `render()`, in `mount()`, in actions and the update hooks, or in a `@Computed`
63
+ getter any of them reads) shows the component's `page`, and its links are built on the page's URL. The actions
64
+ `nextPage`, `previousPage`, `gotoPage(n)`, `setPage(n)` and `resetPage` move it; `<Pagination>` renders them as
65
+ buttons with the kits' `btn` classes. The page starts at 1 and isn't kept in the address bar.
66
+
67
+ ## Middleware on updates
68
+
69
+ Each update re-runs the `auth`, `verified`, `guest` and `can` middleware of the route that rendered the page
70
+ (Livewire's persistent middleware), with that route's parameters bound again, so `can:update,post` sees the post. A
71
+ signed-out tab gets a 401 and a user who lost the ability a 403, before the component's actions run. On a page behind
72
+ one of these middleware, a model deleted since the page rendered gets a 404 too; a page with none binds nothing on
73
+ updates. The list is `live.persistent_middleware` in `config/live.ts`.
74
+
75
+ ## Flash data in updates
76
+
77
+ A status an action flashes (`session().flash('status', 'Saved.')`) shows in that update's response only, unless the
78
+ action redirects or navigates: then it is there on the next page. Component state is unaffected.
79
+
80
+ ## Known limitations
37
81
 
38
82
  - `o:navigate` does not run scripts in the new page.
39
83
  - A DOM event whose name contains `-` can't be written as `o:<event>` in TSX, because modifiers are `-` segments (for example `o:model-live`).
package/dist/index.d.ts CHANGED
@@ -189,9 +189,11 @@ declare class LiveComponentManager implements LiveManager {
189
189
  update(request: LiveUpdateRequest): Promise<LiveUpdateResponse>;
190
190
  updateComponent(snapshotJson: string, updates: Record<string, unknown>, calls: Call[]): Promise<ComponentResult>;
191
191
  /**
192
- * The route action of POST /oriel/live/update. Each component's update runs behind the auth middleware of the route
193
- * that rendered its page (Livewire's persistent middleware): a signed-out tab gets a 401, never the action. When a
194
- * middleware answers instead (a redirect), that is the response.
192
+ * The route action of POST /oriel/live/update. Each component's update runs behind the persistent middleware of the
193
+ * route that rendered its page (Livewire's): `auth`, `verified`, `guest` and `can`, with the page route's parameters
194
+ * bound again. A signed-out tab gets a 401, a revoked ability a 403 and a deleted model a 404, before the component
195
+ * hydrates. When a middleware answers instead (a redirect), that is the response. A key first flashed during the
196
+ * update shows in this response only, unless the update redirects.
195
197
  */
196
198
  handle(request: HttpRequest): Promise<LiveUpdateResponse | Response>;
197
199
  /** A full-page component (Route.get('/counter', Counter)): mounted with the route parameters, inside the layout. */
@@ -206,8 +208,14 @@ declare class LiveComponentManager implements LiveManager {
206
208
  private applyUpdate;
207
209
  /** Runs one call from the browser: a magic action, an event, or a public method. Returns its return value. */
208
210
  private call;
209
- /** Renders the component (rendering/rendered hooks, nested children tracked) and checks for a single root. */
211
+ /**
212
+ * Renders the component (rendering/rendered hooks, nested children tracked) and checks for a single root. A
213
+ * WithPagination component's page and page URL are what paginate() reads while it renders, as in its mount() and in
214
+ * its update's hooks and actions.
215
+ */
210
216
  private render;
217
+ /** The absolute URL of the page a memo path belongs to, on the current request's origin ('/' + path without one). */
218
+ private pageUrl;
211
219
  /** Dispatches, redirects: the effects every response carries. */
212
220
  private effects;
213
221
  /** dehydrate() hook, then the state as snapshot data, the error bag, and the checksum over both. */
@@ -217,8 +225,9 @@ declare class LiveComponentManager implements LiveManager {
217
225
  /** The URI the client runtime posts updates to. */
218
226
  declare const UPDATE_URI = "/oriel/live/update";
219
227
  /**
220
- * Binds the LiveManager, merges the `live` config, registers POST /oriel/live/update (named `live.update`, in the `web`
221
- * group: sessions and CSRF apply), the make:live command, and the components under app/Live.
228
+ * Binds the LiveManager and the PaginationState that WithPagination components answer, merges the `live` config,
229
+ * registers POST /oriel/live/update (named `live.update`, in the `web` group: sessions and CSRF apply), the make:live
230
+ * command, and the components under app/Live.
222
231
  */
223
232
  declare class LiveServiceProvider extends ServiceProvider {
224
233
  register(): void;
@@ -277,10 +286,57 @@ interface PersistentMiddleware {
277
286
  readonly parameters: readonly string[];
278
287
  }
279
288
  /**
280
- * Livewire's persistent middleware: the route that rendered the page (the component's memo path and method) is
281
- * matched again, and the middleware among `persistent` (aliases) that it ran are returned, to run before an update.
282
- * A path no route matches has none.
289
+ * Livewire's persistent middleware: the middleware among `persistent` (aliases) that the page route (the component's
290
+ * memo path and method) ran, in the order an update runs them. A path no route matches has none.
283
291
  */
284
292
  declare function persistentMiddleware(router: Router, persistent: readonly string[], method: string, path: string): PersistentMiddleware[];
285
293
 
286
- export { Component, type ComponentResult, Computed, CorruptComponentPayloadError, ErrorBag, InvalidLiveRequestError, Live, LiveComponentManager, LiveComponentNotFoundError, LiveError, type LiveProps, LiveServiceProvider, Locked, MethodNotCallableError, On, PERSISTENT_MIDDLEWARE, PendingEvent, type PersistentMiddleware, PropertyNotUpdatableError, TestRequestScope, Testable, UPDATE_URI, componentName, live, persistentMiddleware };
294
+ type ComponentConstructor = abstract new (...args: any[]) => Component;
295
+ /** What WithPagination() adds to a component: its page and the actions that move it. */
296
+ interface Paginated {
297
+ /** The current page. The browser may set it; Live reads it as a page number. */
298
+ page: number;
299
+ nextPage(): Promise<void>;
300
+ previousPage(): Promise<void>;
301
+ gotoPage(page: number | string): Promise<void>;
302
+ resetPage(): Promise<void>;
303
+ setPage(page: number | string): Promise<void>;
304
+ }
305
+ /**
306
+ * Livewire's WithPagination: `class Posts extends WithPagination(Component)`. The public `page` (1 to start; there
307
+ * is no address-bar sync) is what `paginate()` shows in the component (in `render()`, `mount()`, `hydrate()`, the
308
+ * update hooks and actions, or in a `@Computed` getter any of them reads), and its page links are built on the
309
+ * page's URL. The actions move it: `o:click="nextPage"`, `previousPage`, `gotoPage(3)`, `setPage(3)`,
310
+ * `resetPage` (call it from `updatedSearch()` so a new search starts on page 1). `updatingPage(page)` and
311
+ * `updatedPage(page)` run around every change. One paginator per component: other page names read the request.
312
+ */
313
+ declare function WithPagination<TBase extends ComponentConstructor>(Base: TBase): TBase & (abstract new (...args: any[]) => Paginated);
314
+
315
+ /**
316
+ * What <Pagination> reads from a paginator. Structural, since Live can't import Database: paginate()'s
317
+ * LengthAwarePaginator (with lastPage(), so page buttons) and simplePaginate()'s Paginator (Previous and Next only).
318
+ */
319
+ interface PaginationSource {
320
+ currentPage(): number;
321
+ lastPage?(): number;
322
+ hasMorePages(): boolean;
323
+ onFirstPage(): boolean;
324
+ /** Page buttons on each side of the current one (the paginator's onEachSide(), 3 by default). */
325
+ readonly onEachSideCount?: number;
326
+ /** Not cursorPaginate(): its pages are cursors, which a component's page number can't follow. */
327
+ readonly nextCursor?: never;
328
+ }
329
+ interface PaginationProps {
330
+ paginator: PaginationSource;
331
+ /** Classes for the <nav>, such as its layout ("flex gap-2"); in your file, so Tailwind generates them. */
332
+ class?: string;
333
+ }
334
+ /**
335
+ * Livewire's pagination links for a WithPagination component: `<Pagination paginator={posts} />` in render(). A
336
+ * <nav aria-label="Pagination"> with « Previous, the page buttons and Next » as Live actions (previousPage,
337
+ * gotoPage(n), nextPage), the current page marked aria-current="page", the ends disabled, the kits' theme classes
338
+ * (btn, btn-secondary). Nothing when everything fits on one page. The address bar is left alone.
339
+ */
340
+ declare function Pagination({ paginator, class: className }: PaginationProps): Htmlable;
341
+
342
+ export { Component, type ComponentResult, Computed, CorruptComponentPayloadError, ErrorBag, InvalidLiveRequestError, Live, LiveComponentManager, LiveComponentNotFoundError, LiveError, type LiveProps, LiveServiceProvider, Locked, MethodNotCallableError, On, PERSISTENT_MIDDLEWARE, type Paginated, Pagination, type PaginationProps, type PaginationSource, PendingEvent, type PersistentMiddleware, PropertyNotUpdatableError, TestRequestScope, Testable, UPDATE_URI, WithPagination, componentName, live, persistentMiddleware };