@almadar/std 16.186.0 → 16.188.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/behaviors/lolo/ui/core/.hand-authored.json +4 -0
- package/behaviors/lolo/ui/core/atoms/std-board.lolo +138 -4
- package/behaviors/lolo/ui/core/atoms/std-browse.lolo +148 -8
- package/behaviors/lolo/ui/core/atoms/std-calendar.lolo +141 -3
- package/behaviors/lolo/ui/core/atoms/std-event-log.lolo +255 -2
- package/behaviors/lolo/ui/core/atoms/std-gallery.lolo +148 -1
- package/behaviors/lolo/ui/core/atoms/std-list.lolo +193 -1
- package/behaviors/lolo/ui/core/atoms/std-record-detail.lolo +329 -0
- package/behaviors/lolo/ui/core/atoms/std-selection.lolo +182 -1
- package/behaviors/lolo/ui/core/atoms/std-tree.lolo +113 -3
- package/behaviors/lolo/ui/game/atoms/std-area-fill.lolo +93 -0
- package/behaviors/lolo/ui/game/atoms/std-loop-lock.lolo +79 -0
- package/behaviors/lolo/ui/game/atoms/std-pad-lock.lolo +93 -0
- package/behaviors/lolo/ui/game/atoms/std-patrol-hazard.lolo +14 -7
- package/behaviors/lolo/ui/game/atoms/std-platformer-body.lolo +21 -4
- package/behaviors/lolo/ui/game/atoms/std-velocity-gate.lolo +101 -0
- package/behaviors/registry/infra/atoms/std-approval-gate.orb +16 -8
- package/behaviors/registry/infra/atoms/std-cache-aside.orb +62 -31
- package/behaviors/registry/infra/atoms/std-circuit-breaker.orb +32 -16
- package/behaviors/registry/infra/atoms/std-data-erasure.orb +16 -8
- package/behaviors/registry/infra/atoms/std-mod-queue.orb +16 -8
- package/behaviors/registry/infra/atoms/std-rate-limiter.orb +28 -14
- package/behaviors/registry/infra/organisms/std-status-lifecycle-control.orb +2 -1
- package/behaviors/registry/ml/organisms/std-escalating-decision.orb +10 -5
- package/behaviors/registry/ml/organisms/std-knowledge-tracing.orb +4 -2
- package/behaviors/registry/ui/core/atoms/std-billable-hour.orb +2 -1
- package/behaviors/registry/ui/core/atoms/std-board.orb +355 -2
- package/behaviors/registry/ui/core/atoms/std-browse.orb +304 -31
- package/behaviors/registry/ui/core/atoms/std-calendar.orb +344 -0
- package/behaviors/registry/ui/core/atoms/std-dashboard-grid-embedded.orb +2 -1
- package/behaviors/registry/ui/core/atoms/std-esign-flow.orb +4 -2
- package/behaviors/registry/ui/core/atoms/std-esign-request.orb +6 -3
- package/behaviors/registry/ui/core/atoms/std-event-log.orb +773 -0
- package/behaviors/registry/ui/core/atoms/std-export.orb +4 -2
- package/behaviors/registry/ui/core/atoms/std-gallery.orb +390 -0
- package/behaviors/registry/ui/core/atoms/std-import.orb +4 -2
- package/behaviors/registry/ui/core/atoms/std-list.orb +451 -0
- package/behaviors/registry/ui/core/atoms/std-record-detail.orb +1141 -0
- package/behaviors/registry/ui/core/atoms/std-selection.orb +436 -0
- package/behaviors/registry/ui/core/atoms/std-tree.orb +292 -1
- package/behaviors/registry/ui/game/atoms/std-area-fill.orb +955 -0
- package/behaviors/registry/ui/game/atoms/std-fx-trail.orb +1 -1
- package/behaviors/registry/ui/game/atoms/std-gate-lock.orb +1 -1
- package/behaviors/registry/ui/game/atoms/std-lives.orb +1 -1
- package/behaviors/registry/ui/game/atoms/std-loop-lock.orb +785 -0
- package/behaviors/registry/ui/game/atoms/std-pad-lock.orb +903 -0
- package/behaviors/registry/ui/game/atoms/std-patrol-hazard.orb +190 -44
- package/behaviors/registry/ui/game/atoms/std-platformer-body.orb +868 -637
- package/behaviors/registry/ui/game/atoms/std-velocity-gate.orb +1040 -0
- package/behaviors/registry/ui/game/organisms/std-arcade-board-3d.orb +8 -4
- package/behaviors/registry/ui/game/organisms/std-crew-board-2d.orb +6 -3
- package/behaviors/registry/ui/game/organisms/std-dungeon-board-2d.orb +10 -5
- package/behaviors/registry/ui/game/organisms/std-market-board-3d.orb +8 -4
- package/behaviors/registry/ui/game/organisms/std-objective-board-3d.orb +8 -4
- package/behaviors/registry/ui/game/organisms/std-platformer-board-2d.orb +7 -4
- package/behaviors/registry/ui/game/organisms/std-survival-board-3d.orb +8 -4
- package/behaviors/registry/ui/game/organisms/std-tactics-board-2d.orb +12 -6
- package/behaviors/registry/ui/game/organisms/std-tactics-board-3d.orb +8 -4
- package/behaviors/registry/ui/game/organisms/std-worldmap-board-2d.orb +6 -3
- package/behaviors/registry/ui/learning/atoms/std-learn-slope-probe.orb +1 -1
- package/behaviors/registry/ui/marketing/atoms/std-marketing-article.orb +2 -1
- package/behaviors/registry/ui/marketing/atoms/std-marketing-footer.orb +2 -1
- package/behaviors/registry/ui/marketing/atoms/std-marketing-split.orb +2 -1
- package/behaviors/registry/ui/marketing/atoms/std-marketing-stats.orb +2 -1
- package/behaviors/registry/ui/marketing/atoms/std-marketing-steps.orb +2 -1
- package/behaviors/registry/ui/marketing/atoms/std-marketing-team.orb +2 -1
- package/dist/behaviors/exports-reader.js +7822 -3996
- package/dist/behaviors/functions/index.d.ts +598 -270
- package/dist/behaviors/functions/index.js +7805 -3989
- package/dist/behaviors/index.d.ts +1 -1
- package/dist/behaviors/index.js +7823 -3997
- package/dist/behaviors/query.js +7822 -3996
- package/dist/behaviors-embeddings.hash.json +3 -3
- package/dist/behaviors-embeddings.json +535 -519
- package/dist/behaviors-registry.json +2877 -2268
- package/dist/index.d.ts +1 -1
- package/dist/index.js +7823 -3997
- package/dist/knob-embeddings.hash.json +3 -3
- package/dist/knob-embeddings.json +1 -1
- package/dist/registry/factory-signatures.json +1 -1
- package/package.json +1 -1
- package/behaviors/lolo/ui/core/organisms/ui-ui-slot-renderer.lolo +0 -71
- package/behaviors/registry/ui/core/organisms/ui-ui-slot-renderer.orb +0 -234
|
@@ -2,6 +2,7 @@ app std-event-log "2.1.0"
|
|
|
2
2
|
"std-event-log — generic chronological event feed (Stripe activity log / GitHub PR timeline / Linear event feed style). Renders a vertical `timeline` with type-coded dots, a single horizontal chip-bar of kind filters (All + per-kind), reverse-chronological sort, and an inline form for logging events that weren't auto-captured. `config.kindOptions` maps each kind → status/icon/label so consuming molecules/organisms get type-coded visuals without re-implementing topology. Topology = loading / viewing / backfilling / error."
|
|
3
3
|
type ObjectSpec = { id : string, label : string, value : string }
|
|
4
4
|
type KindOption = { label : string!, value : string! }
|
|
5
|
+
type FilterSpec = { field : string!, label : string, options : [string] }
|
|
5
6
|
orbital EventLogOrbital {
|
|
6
7
|
type LogEntry = { id : string!, title : string!, description : string, date : string, status : "complete" | "active" | "pending" | "error", kind : "created" | "updated" | "approved" | "rejected" }
|
|
7
8
|
|
|
@@ -89,7 +90,37 @@ orbital EventLogOrbital {
|
|
|
89
90
|
direction: horizontal
|
|
90
91
|
gap: sm
|
|
91
92
|
type: stack
|
|
92
|
-
}, {
|
|
93
|
+
}, (if @config.bodySearch {
|
|
94
|
+
children: [{
|
|
95
|
+
className: "w-full max-w-md"
|
|
96
|
+
clearable: true
|
|
97
|
+
event: REFETCH_QUERY
|
|
98
|
+
placeholder: @config.searchPlaceholder
|
|
99
|
+
type: search-input
|
|
100
|
+
}]
|
|
101
|
+
direction: horizontal
|
|
102
|
+
gap: sm
|
|
103
|
+
type: stack
|
|
104
|
+
} {
|
|
105
|
+
children: []
|
|
106
|
+
gap: none
|
|
107
|
+
type: stack
|
|
108
|
+
}), (if [">", (array/len @config.filters), 0] {
|
|
109
|
+
children: [{
|
|
110
|
+
entity: EventLogView
|
|
111
|
+
event: REFETCH_FILTER
|
|
112
|
+
filters: @config.filters
|
|
113
|
+
look: @config.filterBarLook
|
|
114
|
+
type: filter-group
|
|
115
|
+
}]
|
|
116
|
+
direction: horizontal
|
|
117
|
+
gap: sm
|
|
118
|
+
type: stack
|
|
119
|
+
} {
|
|
120
|
+
children: []
|
|
121
|
+
gap: none
|
|
122
|
+
type: stack
|
|
123
|
+
}), {
|
|
93
124
|
activeTab: @entity.filterKind
|
|
94
125
|
tabChangeEvent: APPLY_FILTER
|
|
95
126
|
tabs: @entity.filterChips
|
|
@@ -155,7 +186,150 @@ orbital EventLogOrbital {
|
|
|
155
186
|
direction: horizontal
|
|
156
187
|
gap: sm
|
|
157
188
|
type: stack
|
|
189
|
+
}, (if @config.bodySearch {
|
|
190
|
+
children: [{
|
|
191
|
+
className: "w-full max-w-md"
|
|
192
|
+
clearable: true
|
|
193
|
+
event: REFETCH_QUERY
|
|
194
|
+
placeholder: @config.searchPlaceholder
|
|
195
|
+
type: search-input
|
|
196
|
+
}]
|
|
197
|
+
direction: horizontal
|
|
198
|
+
gap: sm
|
|
199
|
+
type: stack
|
|
200
|
+
} {
|
|
201
|
+
children: []
|
|
202
|
+
gap: none
|
|
203
|
+
type: stack
|
|
204
|
+
}), (if [">", (array/len @config.filters), 0] {
|
|
205
|
+
children: [{
|
|
206
|
+
entity: EventLogView
|
|
207
|
+
event: REFETCH_FILTER
|
|
208
|
+
filters: @config.filters
|
|
209
|
+
look: @config.filterBarLook
|
|
210
|
+
type: filter-group
|
|
211
|
+
}]
|
|
212
|
+
direction: horizontal
|
|
213
|
+
gap: sm
|
|
214
|
+
type: stack
|
|
215
|
+
} {
|
|
216
|
+
children: []
|
|
217
|
+
gap: none
|
|
218
|
+
type: stack
|
|
219
|
+
}), {
|
|
220
|
+
activeTab: @entity.filterKind
|
|
221
|
+
tabChangeEvent: APPLY_FILTER
|
|
222
|
+
tabs: @entity.filterChips
|
|
223
|
+
type: tabs
|
|
158
224
|
}, {
|
|
225
|
+
entity: @entity.entries
|
|
226
|
+
fields: (title description date status)
|
|
227
|
+
look: @config.timelineLook
|
|
228
|
+
type: timeline
|
|
229
|
+
}]
|
|
230
|
+
direction: vertical
|
|
231
|
+
gap: md
|
|
232
|
+
type: stack
|
|
233
|
+
})
|
|
234
|
+
;; REFETCH_* stays in `viewing` and DOES NOT replace the timeline with a
|
|
235
|
+
;; skeleton — same reasoning as std-browse's REFETCH_* arms: a skeleton
|
|
236
|
+
;; on refetch unmounts the timeline and flickers every keystroke. The
|
|
237
|
+
;; fetch fires in the background; the EventLogLoaded arm below re-renders
|
|
238
|
+
;; in place once it lands. Each refetch also honours the current kind
|
|
239
|
+
;; tab (@entity.filterKind) so search/generic-filter compound with it
|
|
240
|
+
;; instead of resetting it.
|
|
241
|
+
REFETCH_QUERY -> viewing
|
|
242
|
+
(fetch EventLogView {
|
|
243
|
+
emit: { failure: EventLogLoadFailed, success: EventLogLoaded }
|
|
244
|
+
filter: (and (or ["=", @entity.filterKind, ""] ["=", (object/get @entity kind), @entity.filterKind]) (or ["=", ?searchTerm, ""] (str/includes (object/get @entity title) ?searchTerm)))
|
|
245
|
+
})
|
|
246
|
+
REFETCH_FILTER -> viewing
|
|
247
|
+
(fetch EventLogView {
|
|
248
|
+
emit: { failure: EventLogLoadFailed, success: EventLogLoaded }
|
|
249
|
+
filter: (and (or ["=", @entity.filterKind, ""] ["=", (object/get @entity kind), @entity.filterKind]) (or ["=", ?value, ""] ["=", (object/get @entity ?field), ?value]))
|
|
250
|
+
})
|
|
251
|
+
;; No filter carried over on REFETCH_PAGE, mirroring std-browse exactly —
|
|
252
|
+
;; the reference page arm does not preserve query/filter either.
|
|
253
|
+
REFETCH_PAGE -> viewing
|
|
254
|
+
(fetch EventLogView {
|
|
255
|
+
emit: { failure: EventLogLoadFailed, success: EventLogLoaded }
|
|
256
|
+
limit: @config.pageSize
|
|
257
|
+
offset: ["*", ["-", ?page, 1], @config.pageSize]
|
|
258
|
+
})
|
|
259
|
+
EventLogLoaded -> viewing
|
|
260
|
+
(set @entity.filterChips (array/prepend (array/map @config.kindOptions (fn k {
|
|
261
|
+
icon: (object/get @k icon circle)
|
|
262
|
+
id: (object/get @k key)
|
|
263
|
+
label: (object/get @k label)
|
|
264
|
+
})) {
|
|
265
|
+
icon: list
|
|
266
|
+
id: ""
|
|
267
|
+
label: All
|
|
268
|
+
}))
|
|
269
|
+
(set @entity.allEntries (array/sort (array/map ?data (fn row {
|
|
270
|
+
date: (object/get @row createdAt (object/get @row date ""))
|
|
271
|
+
description: (object/get @row description "")
|
|
272
|
+
id: (object/get @row id "")
|
|
273
|
+
kind: (object/get @row kind "")
|
|
274
|
+
status: (object/get (or (array/find @config.kindOptions (fn k ["=", (object/get @k key), (object/get @row kind "")])) {}) status pending)
|
|
275
|
+
title: (object/get @row title "")
|
|
276
|
+
})) date desc))
|
|
277
|
+
(set @entity.entries (array/sort (array/map ?data (fn row {
|
|
278
|
+
date: (object/get @row createdAt (object/get @row date ""))
|
|
279
|
+
description: (object/get @row description "")
|
|
280
|
+
id: (object/get @row id "")
|
|
281
|
+
kind: (object/get @row kind "")
|
|
282
|
+
status: (object/get (or (array/find @config.kindOptions (fn k ["=", (object/get @k key), (object/get @row kind "")])) {}) status pending)
|
|
283
|
+
title: (object/get @row title "")
|
|
284
|
+
})) date desc))
|
|
285
|
+
(render-ui main {
|
|
286
|
+
children: [{
|
|
287
|
+
align: center
|
|
288
|
+
children: [{ name: history, type: icon }, {
|
|
289
|
+
content: @config.title
|
|
290
|
+
type: typography
|
|
291
|
+
variant: h3
|
|
292
|
+
}, {
|
|
293
|
+
action: OPEN_BACKFILL
|
|
294
|
+
icon: plus
|
|
295
|
+
label: "Log event"
|
|
296
|
+
type: button
|
|
297
|
+
variant: primary
|
|
298
|
+
}]
|
|
299
|
+
direction: horizontal
|
|
300
|
+
gap: sm
|
|
301
|
+
type: stack
|
|
302
|
+
}, (if @config.bodySearch {
|
|
303
|
+
children: [{
|
|
304
|
+
className: "w-full max-w-md"
|
|
305
|
+
clearable: true
|
|
306
|
+
event: REFETCH_QUERY
|
|
307
|
+
placeholder: @config.searchPlaceholder
|
|
308
|
+
type: search-input
|
|
309
|
+
}]
|
|
310
|
+
direction: horizontal
|
|
311
|
+
gap: sm
|
|
312
|
+
type: stack
|
|
313
|
+
} {
|
|
314
|
+
children: []
|
|
315
|
+
gap: none
|
|
316
|
+
type: stack
|
|
317
|
+
}), (if [">", (array/len @config.filters), 0] {
|
|
318
|
+
children: [{
|
|
319
|
+
entity: EventLogView
|
|
320
|
+
event: REFETCH_FILTER
|
|
321
|
+
filters: @config.filters
|
|
322
|
+
look: @config.filterBarLook
|
|
323
|
+
type: filter-group
|
|
324
|
+
}]
|
|
325
|
+
direction: horizontal
|
|
326
|
+
gap: sm
|
|
327
|
+
type: stack
|
|
328
|
+
} {
|
|
329
|
+
children: []
|
|
330
|
+
gap: none
|
|
331
|
+
type: stack
|
|
332
|
+
}), {
|
|
159
333
|
activeTab: @entity.filterKind
|
|
160
334
|
tabChangeEvent: APPLY_FILTER
|
|
161
335
|
tabs: @entity.filterChips
|
|
@@ -237,7 +411,37 @@ orbital EventLogOrbital {
|
|
|
237
411
|
direction: horizontal
|
|
238
412
|
gap: sm
|
|
239
413
|
type: stack
|
|
240
|
-
}, {
|
|
414
|
+
}, (if @config.bodySearch {
|
|
415
|
+
children: [{
|
|
416
|
+
className: "w-full max-w-md"
|
|
417
|
+
clearable: true
|
|
418
|
+
event: REFETCH_QUERY
|
|
419
|
+
placeholder: @config.searchPlaceholder
|
|
420
|
+
type: search-input
|
|
421
|
+
}]
|
|
422
|
+
direction: horizontal
|
|
423
|
+
gap: sm
|
|
424
|
+
type: stack
|
|
425
|
+
} {
|
|
426
|
+
children: []
|
|
427
|
+
gap: none
|
|
428
|
+
type: stack
|
|
429
|
+
}), (if [">", (array/len @config.filters), 0] {
|
|
430
|
+
children: [{
|
|
431
|
+
entity: EventLogView
|
|
432
|
+
event: REFETCH_FILTER
|
|
433
|
+
filters: @config.filters
|
|
434
|
+
look: @config.filterBarLook
|
|
435
|
+
type: filter-group
|
|
436
|
+
}]
|
|
437
|
+
direction: horizontal
|
|
438
|
+
gap: sm
|
|
439
|
+
type: stack
|
|
440
|
+
} {
|
|
441
|
+
children: []
|
|
442
|
+
gap: none
|
|
443
|
+
type: stack
|
|
444
|
+
}), {
|
|
241
445
|
activeTab: @entity.filterKind
|
|
242
446
|
tabChangeEvent: APPLY_FILTER
|
|
243
447
|
tabs: @entity.filterChips
|
|
@@ -354,6 +558,25 @@ orbital EventLogOrbital {
|
|
|
354
558
|
@description "Indicates an attempt to save an event failed."
|
|
355
559
|
@synonyms "error, failure, save failed"
|
|
356
560
|
@tier "internal"
|
|
561
|
+
REFETCH_QUERY {
|
|
562
|
+
searchTerm : string!
|
|
563
|
+
}
|
|
564
|
+
@description "Triggers an activity feed refresh based on the current search term."
|
|
565
|
+
@synonyms "refresh, reload, update, re-query, search"
|
|
566
|
+
@tier "domain"
|
|
567
|
+
REFETCH_FILTER {
|
|
568
|
+
field : string!
|
|
569
|
+
value : string!
|
|
570
|
+
}
|
|
571
|
+
@description "Triggers an activity feed refresh based on a field/value filter, in addition to the built-in kind tabs."
|
|
572
|
+
@synonyms "refresh, filter, update, reload"
|
|
573
|
+
@tier "domain"
|
|
574
|
+
REFETCH_PAGE {
|
|
575
|
+
page : number!
|
|
576
|
+
}
|
|
577
|
+
@description "Triggers an activity feed page refresh."
|
|
578
|
+
@synonyms "reload, refresh, update, paginate"
|
|
579
|
+
@tier "domain"
|
|
357
580
|
}
|
|
358
581
|
|
|
359
582
|
config {
|
|
@@ -397,6 +620,36 @@ orbital EventLogOrbital {
|
|
|
397
620
|
@label "Timeline look"
|
|
398
621
|
@description "Layer 2 visual treatment for the timeline."
|
|
399
622
|
@tier "presentation"
|
|
623
|
+
bodySearch : boolean = false
|
|
624
|
+
@label "Show the log's own search box?"
|
|
625
|
+
@description "Embeds a search box above the activity feed. Set true only when the page has no other search affordance (e.g. no page-level std-search), so the surface shows one search box instead of two. False is the default — no consumer of this atom has ever had a search box."
|
|
626
|
+
@synonyms "inline search, log search, built-in search, show search"
|
|
627
|
+
@tier "presentation"
|
|
628
|
+
searchPlaceholder : string = "Search activity…"
|
|
629
|
+
@label "Search box placeholder text"
|
|
630
|
+
@description "Hint text inside the log's search box."
|
|
631
|
+
@synonyms "search hint, placeholder, search box text"
|
|
632
|
+
@tier "presentation"
|
|
633
|
+
filters : [FilterSpec] = []
|
|
634
|
+
@label "Which filters should appear above the feed?"
|
|
635
|
+
@description "Dropdown filter facets by field, rendered as an embedded filter bar above the timeline; each selection fires REFETCH_FILTER on this trait, ANDed with the current kind tab. Empty (the default) = no filter bar — the right setting whenever the page already composes std-filter beside this atom and routes XFilter.FILTER -> REFETCH_FILTER, or relies solely on the built-in kind-filter chips (driven by kindOptions, which this knob does not duplicate or replace). Use this knob only for an extra facet beyond kind (e.g. actor, priority)."
|
|
636
|
+
@synonyms "filter bar, faceted filters, field filters, dropdowns"
|
|
637
|
+
@tier "presentation"
|
|
638
|
+
filterBarLook : "toolbar" | "chips" | "pills" | "popover-trigger" | "inline-column-header" = toolbar
|
|
639
|
+
@label "Filter bar look"
|
|
640
|
+
@description "Visual treatment for the embedded filter bar, mirroring std-filter's own enum. Only applies when `filters` is non-empty."
|
|
641
|
+
@synonyms "filter bar style, facet style, chips, pills"
|
|
642
|
+
@tier "presentation"
|
|
643
|
+
pageSize : number = 20
|
|
644
|
+
@label "How many entries per page when paging?"
|
|
645
|
+
@description "Records fetched from the server per REFETCH_PAGE request. The initial load and every search/kind/filter refetch stay unpaginated (load everything that matches) so the default unlimited activity feed is unchanged; only an explicit REFETCH_PAGE from a composed std-pagination control applies this limit."
|
|
646
|
+
@synonyms "fetch limit, records per page, page limit"
|
|
647
|
+
@tier "presentation"
|
|
648
|
+
displayPageSize : number = 20
|
|
649
|
+
@label "How many entries per page should a pager show?"
|
|
650
|
+
@description "Page size a consumer-composed std-pagination control should use to compute total pages (e.g. from totalCount). This atom's own timeline pattern has no built-in pager UI, so this knob is informational for the composed control, not consumed by the timeline itself."
|
|
651
|
+
@synonyms "visible rows, items per page, display limit"
|
|
652
|
+
@tier "presentation"
|
|
400
653
|
}
|
|
401
654
|
}
|
|
402
655
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
app std-gallery "1.0.0"
|
|
2
2
|
"std-gallery — image gallery primitive. Browses GalleryItems in a media-gallery grid; opens a lightbox modal for full-size viewing."
|
|
3
|
+
type FilterSpec = { field : string!, label : string, options : [string] }
|
|
3
4
|
orbital GalleryItemOrbital {
|
|
4
5
|
uses Spinner from "std/behaviors/ui-spinner"
|
|
5
6
|
uses Typography from "std/behaviors/ui-typography"
|
|
@@ -103,7 +104,37 @@ orbital GalleryItemOrbital {
|
|
|
103
104
|
direction: horizontal
|
|
104
105
|
gap: sm
|
|
105
106
|
type: stack
|
|
106
|
-
}, @trait.TitleDivider, @
|
|
107
|
+
}, @trait.TitleDivider, (if @config.bodySearch {
|
|
108
|
+
children: [{
|
|
109
|
+
className: "w-full max-w-md"
|
|
110
|
+
clearable: true
|
|
111
|
+
event: REFETCH_QUERY
|
|
112
|
+
placeholder: @config.searchPlaceholder
|
|
113
|
+
type: search-input
|
|
114
|
+
}]
|
|
115
|
+
direction: horizontal
|
|
116
|
+
gap: sm
|
|
117
|
+
type: stack
|
|
118
|
+
} {
|
|
119
|
+
children: []
|
|
120
|
+
gap: none
|
|
121
|
+
type: stack
|
|
122
|
+
}), (if [">", (array/len @config.filters), 0] {
|
|
123
|
+
children: [{
|
|
124
|
+
entity: GalleryItem
|
|
125
|
+
event: REFETCH_FILTER
|
|
126
|
+
filters: @config.filters
|
|
127
|
+
look: @config.filterBarLook
|
|
128
|
+
type: filter-group
|
|
129
|
+
}]
|
|
130
|
+
direction: horizontal
|
|
131
|
+
gap: sm
|
|
132
|
+
type: stack
|
|
133
|
+
} {
|
|
134
|
+
children: []
|
|
135
|
+
gap: none
|
|
136
|
+
type: stack
|
|
137
|
+
}), @trait.MediaGalleryGrid]
|
|
107
138
|
direction: vertical
|
|
108
139
|
gap: md
|
|
109
140
|
type: stack
|
|
@@ -121,6 +152,78 @@ orbital GalleryItemOrbital {
|
|
|
121
152
|
(fetch GalleryItem {
|
|
122
153
|
emit: { failure: GalleryItemLoadFailed, success: GalleryItemLoaded }
|
|
123
154
|
})
|
|
155
|
+
;; REFETCH_* stays in `browsing` and DOES NOT replace the gallery grid
|
|
156
|
+
;; with a spinner — search-as-you-type would otherwise unmount the
|
|
157
|
+
;; media-gallery every keystroke and flicker. The fetch fires in the
|
|
158
|
+
;; background; when GalleryItemLoaded arrives below, React reconciles
|
|
159
|
+
;; the grid in place instead of unmounting it.
|
|
160
|
+
REFETCH_QUERY -> browsing
|
|
161
|
+
(fetch GalleryItem {
|
|
162
|
+
emit: { failure: GalleryItemLoadFailed, success: GalleryItemLoaded }
|
|
163
|
+
filter: (or ["=", ?searchTerm, ""] (str/includes (object/get @entity name) ?searchTerm))
|
|
164
|
+
limit: @config.pageSize
|
|
165
|
+
offset: 0
|
|
166
|
+
})
|
|
167
|
+
REFETCH_FILTER -> browsing
|
|
168
|
+
(fetch GalleryItem {
|
|
169
|
+
emit: { failure: GalleryItemLoadFailed, success: GalleryItemLoaded }
|
|
170
|
+
filter: (or ["=", ?value, ""] ["=", (object/get @entity ?field), ?value])
|
|
171
|
+
limit: @config.pageSize
|
|
172
|
+
offset: 0
|
|
173
|
+
})
|
|
174
|
+
REFETCH_PAGE -> browsing
|
|
175
|
+
(fetch GalleryItem {
|
|
176
|
+
emit: { failure: GalleryItemLoadFailed, success: GalleryItemLoaded }
|
|
177
|
+
limit: @config.pageSize
|
|
178
|
+
offset: ["*", ["-", ?page, 1], @config.pageSize]
|
|
179
|
+
})
|
|
180
|
+
GalleryItemLoaded -> browsing
|
|
181
|
+
(render-ui main {
|
|
182
|
+
children: [{
|
|
183
|
+
align: center
|
|
184
|
+
children: [@trait.GalleryIcon, {
|
|
185
|
+
content: @config.title
|
|
186
|
+
type: typography
|
|
187
|
+
variant: h3
|
|
188
|
+
}]
|
|
189
|
+
direction: horizontal
|
|
190
|
+
gap: sm
|
|
191
|
+
type: stack
|
|
192
|
+
}, @trait.TitleDivider, (if @config.bodySearch {
|
|
193
|
+
children: [{
|
|
194
|
+
className: "w-full max-w-md"
|
|
195
|
+
clearable: true
|
|
196
|
+
event: REFETCH_QUERY
|
|
197
|
+
placeholder: @config.searchPlaceholder
|
|
198
|
+
type: search-input
|
|
199
|
+
}]
|
|
200
|
+
direction: horizontal
|
|
201
|
+
gap: sm
|
|
202
|
+
type: stack
|
|
203
|
+
} {
|
|
204
|
+
children: []
|
|
205
|
+
gap: none
|
|
206
|
+
type: stack
|
|
207
|
+
}), (if [">", (array/len @config.filters), 0] {
|
|
208
|
+
children: [{
|
|
209
|
+
entity: GalleryItem
|
|
210
|
+
event: REFETCH_FILTER
|
|
211
|
+
filters: @config.filters
|
|
212
|
+
look: @config.filterBarLook
|
|
213
|
+
type: filter-group
|
|
214
|
+
}]
|
|
215
|
+
direction: horizontal
|
|
216
|
+
gap: sm
|
|
217
|
+
type: stack
|
|
218
|
+
} {
|
|
219
|
+
children: []
|
|
220
|
+
gap: none
|
|
221
|
+
type: stack
|
|
222
|
+
}), @trait.MediaGalleryGrid]
|
|
223
|
+
direction: vertical
|
|
224
|
+
gap: md
|
|
225
|
+
type: stack
|
|
226
|
+
})
|
|
124
227
|
VIEW -> viewing
|
|
125
228
|
(set @entity.id ?id)
|
|
126
229
|
(render-ui modal {
|
|
@@ -195,6 +298,25 @@ orbital GalleryItemOrbital {
|
|
|
195
298
|
}
|
|
196
299
|
|
|
197
300
|
listens {
|
|
301
|
+
REFETCH_QUERY {
|
|
302
|
+
searchTerm : string!
|
|
303
|
+
}
|
|
304
|
+
@description "Triggers a gallery refresh based on the current search query."
|
|
305
|
+
@synonyms "refresh, reload, update, re-query"
|
|
306
|
+
@tier "domain"
|
|
307
|
+
REFETCH_FILTER {
|
|
308
|
+
field : string!
|
|
309
|
+
value : string!
|
|
310
|
+
}
|
|
311
|
+
@description "Triggers a gallery refresh based on current filter criteria."
|
|
312
|
+
@synonyms "refresh, filter, update, reload"
|
|
313
|
+
@tier "domain"
|
|
314
|
+
REFETCH_PAGE {
|
|
315
|
+
page : number!
|
|
316
|
+
}
|
|
317
|
+
@description "Triggers a gallery page refresh."
|
|
318
|
+
@synonyms "reload, refresh, update, paginate"
|
|
319
|
+
@tier "domain"
|
|
198
320
|
VIEW {
|
|
199
321
|
id : string!
|
|
200
322
|
row : GalleryItem
|
|
@@ -236,6 +358,31 @@ orbital GalleryItemOrbital {
|
|
|
236
358
|
@description "Entity field holding the image URL shown in the lightbox for the selected item"
|
|
237
359
|
@synonyms "image url field, thumbnail field, src field"
|
|
238
360
|
@tier "presentation"
|
|
361
|
+
bodySearch : boolean = false
|
|
362
|
+
@label "Show the gallery's own search box?"
|
|
363
|
+
@description "Embeds a search box above the gallery grid. Set false (the default) when the page already provides a search affordance (a toolbar Search molecule), so the surface shows one search box instead of two."
|
|
364
|
+
@synonyms "inline search, list search, built-in search, show search, duplicate search"
|
|
365
|
+
@tier "presentation"
|
|
366
|
+
searchPlaceholder : string = "Search…"
|
|
367
|
+
@label "Search box placeholder text"
|
|
368
|
+
@description "Hint text inside the gallery search box (e.g. 'Search photos…')."
|
|
369
|
+
@synonyms "search hint, placeholder, search box text, search label"
|
|
370
|
+
@tier "presentation"
|
|
371
|
+
filters : [FilterSpec] = []
|
|
372
|
+
@label "Which filters should appear above the gallery?"
|
|
373
|
+
@description "Dropdown filter facets by field, rendered as an embedded filter bar above the grid; each selection fires REFETCH_FILTER on this trait. Empty (the default) = no filter bar, which is the right setting whenever the page already composes std-filter beside this gallery and routes `XFilter.FILTER -> REFETCH_FILTER` — that is the wired owner and remains the primary path. Use this knob only for a standalone gallery with no page-level filter affordance."
|
|
374
|
+
@synonyms "filter bar, faceted filters, field filters, dropdowns"
|
|
375
|
+
@tier "presentation"
|
|
376
|
+
filterBarLook : "toolbar" | "chips" | "pills" | "popover-trigger" | "inline-column-header" = toolbar
|
|
377
|
+
@label "Filter bar look"
|
|
378
|
+
@description "Visual treatment for the embedded filter bar, mirroring std-filter's own enum. Only applies when `filters` is non-empty."
|
|
379
|
+
@synonyms "filter bar style, facet style, chips, pills"
|
|
380
|
+
@tier "presentation"
|
|
381
|
+
pageSize : number = 20
|
|
382
|
+
@label "How many images to fetch per page?"
|
|
383
|
+
@description "Records fetched from the server when REFETCH_PAGE fires (a composed std-pagination driving this trait). The initial load and revisit fetches stay unpaged (whole collection), unchanged from before this knob existed."
|
|
384
|
+
@synonyms "fetch limit, records per page, batch size, page limit"
|
|
385
|
+
@tier "presentation"
|
|
239
386
|
}
|
|
240
387
|
}
|
|
241
388
|
|