storyloco 2.6.0 → 2.7.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/README.md CHANGED
@@ -84,8 +84,11 @@ import type { DateEntry, Dates } from 'storyloco/dates'
84
84
 
85
85
  const show = ({ timezone }: Dates, entry: DateEntry) =>
86
86
  entry.utc_start
87
- ? new Intl.DateTimeFormat('en-GB', { timeZone: timezone, dateStyle: 'medium', timeStyle: 'short' })
88
- .formatRange(new Date(entry.utc_start), new Date(entry.utc_end ?? entry.utc_start))
87
+ ? new Intl.DateTimeFormat('en-GB', {
88
+ timeZone: timezone,
89
+ dateStyle: 'medium',
90
+ timeStyle: 'short'
91
+ }).formatRange(new Date(entry.utc_start), new Date(entry.utc_end ?? entry.utc_start))
89
92
  : [entry.start_date, entry.end_date].filter(Boolean).join(' – ')
90
93
  ```
91
94
 
@@ -120,7 +123,7 @@ the body of every 404 and 500 storyloco raises — it's typed against your own `
120
123
  ```typescript
121
124
  export const client = new StoryblokClient({
122
125
  token: PUBLIC_STORYBLOK_ACCESS_TOKEN,
123
- error: (status) => ({
126
+ error: status => ({
124
127
  message: status === 404 ? 'Story not found' : 'Storyblok request failed',
125
128
  code: status === 404 ? 'NOT_FOUND' : 'STORYBLOK'
126
129
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "storyloco",
3
- "version": "2.6.0",
3
+ "version": "2.7.0",
4
4
  "description": "A collection of slick Storyblok field plugins built with Svelte 5, TypeScript, and Tailwind CSS",
5
5
  "keywords": [
6
6
  "storyblok",
@@ -31,7 +31,6 @@
31
31
  "packages/vite/dist",
32
32
  "packages/vite/src/lib/currency.ts",
33
33
  "!packages/vite/dist/**/*.test.*",
34
- "packages/ticketsource/types.ts",
35
34
  "packages/input/types.ts",
36
35
  "packages/time/types.ts",
37
36
  "packages/seo/types.ts",
@@ -53,10 +52,6 @@
53
52
  "./plans": {
54
53
  "types": "./packages/plans/types.ts"
55
54
  },
56
- "./ticketsource": {
57
- "types": "./packages/ticketsource/types.ts",
58
- "svelte": "./packages/ticketsource/src/app.svelte.ts"
59
- },
60
55
  "./input": {
61
56
  "types": "./packages/input/types.ts",
62
57
  "svelte": "./packages/input/src/app.svelte.ts"
@@ -71,6 +66,7 @@
71
66
  "./asset": {
72
67
  "types": "./packages/asset/types.ts"
73
68
  },
69
+ "./shared/plugin.css": "./packages/shared/dist/plugin.css",
74
70
  "./shared/utils": {
75
71
  "types": "./packages/shared/dist/utils.d.ts",
76
72
  "svelte": "./packages/shared/dist/utils.js"
@@ -115,7 +111,6 @@
115
111
  "@types/node": "26.1.1",
116
112
  "@typescript-eslint/eslint-plugin": "8.65.0",
117
113
  "@typescript-eslint/parser": "8.65.0",
118
- "eslint": "10.7.0",
119
114
  "kitto": "1.6.0",
120
115
  "oxfmt": "0.59.0",
121
116
  "typescript": "6.0.3",
@@ -131,7 +126,7 @@
131
126
  "scripts": {
132
127
  "add-plugin": "npx @storyblok/field-plugin-cli@latest add --dir \"./packages\" --structure monorepo",
133
128
  "build": "cd packages/vite && npm run build && cd ../../packages/shared && npm run build",
134
- "lint": "oxfmt --check && eslint --ext .js .",
129
+ "lint": "oxfmt --check && bun run --filter './packages/*' lint",
135
130
  "format": "oxfmt"
136
131
  },
137
132
  "type": "module",
@@ -18,4 +18,5 @@
18
18
  className
19
19
  )}
20
20
  bind:value
21
- {...restProps}></textarea>
21
+ {...restProps}
22
+ ></textarea>
@@ -0,0 +1,474 @@
1
+ /* Shared look for UI Loco's Storyblok space plugins (Shop, Mux Library), close to Storyblok's own
2
+ editor UI. Wrap the plugin in `.plugin` and use its classes: card, button, segments, search,
3
+ status, table, empty, problem, skeleton. Import with `import 'storyloco/shared/plugin.css'`. */
4
+ .plugin {
5
+ --sb-ink: #1b243f;
6
+ --sb-muted: #5f6781;
7
+ --sb-line: #e2e5ec;
8
+ --sb-bg: #f6f7f9;
9
+ --sb-surface: #fff;
10
+ /* Hovered rows, open orders and inset controls inside a card. */
11
+ --sb-raised: #f6f7f9;
12
+ --sb-line-strong: #c9ceda;
13
+ --sb-accent: #00b3b0;
14
+ /* Teal text and links; the primary button matches storyloco's field plugins. */
15
+ --sb-accent-ink: #05807f;
16
+ --sb-accent-wash: #e5f7f6;
17
+ --sb-primary: #05807f;
18
+ --sb-primary-hover: #046867;
19
+ --sb-on-primary: #fff;
20
+ /* Tooltips and toasts. */
21
+ --sb-tip: #1b243f;
22
+ --sb-on-tip: #fff;
23
+ --sb-shimmer: #eceef3;
24
+ /* Chart bars: validated for contrast and chroma against the white card. */
25
+ --sb-bar: #00938f;
26
+ --sb-good: #1c7c47;
27
+ --sb-good-wash: #e5f4ea;
28
+ --sb-warn: #8a5a00;
29
+ --sb-warn-wash: #fdf1d8;
30
+ --sb-danger: #b42323;
31
+ --sb-danger-wash: #fce9e9;
32
+ --sb-mono: ui-monospace, 'SF Mono', Menlo, monospace;
33
+ --sb-shadow: 0 1px 2px rgb(27 36 63 / 0.04), 0 1px 3px rgb(27 36 63 / 0.06);
34
+ --sb-ease: cubic-bezier(0.2, 0.8, 0.2, 1);
35
+
36
+ /* Dark tokens follow storyloco's field plugins (packages/shared global.css), which
37
+ also switch on the system colour scheme. */
38
+ @media (prefers-color-scheme: dark) {
39
+ --sb-ink: #f8fafc;
40
+ --sb-muted: #8f93a2;
41
+ --sb-line: rgb(255 255 255 / 0.1);
42
+ --sb-line-strong: rgb(255 255 255 / 0.22);
43
+ /* Storyblok's own dark sidebar and dashboard: cards share the page colour,
44
+ with raised panels a step lighter. */
45
+ --sb-bg: #242328;
46
+ --sb-surface: #242328;
47
+ --sb-raised: #333237;
48
+ --sb-accent-ink: #3bc4b9;
49
+ --sb-accent-wash: rgb(59 196 185 / 0.14);
50
+ --sb-primary: #05807f;
51
+ --sb-primary-hover: #06918f;
52
+ --sb-on-primary: #fff;
53
+ --sb-tip: #45454d;
54
+ --sb-shimmer: #3d3d44;
55
+ /* Validated against the dark card. */
56
+ --sb-bar: #2aa9a3;
57
+ --sb-good: #5fd08f;
58
+ --sb-good-wash: rgb(95 208 143 / 0.14);
59
+ --sb-warn: #f0b650;
60
+ --sb-warn-wash: rgb(240 182 80 / 0.14);
61
+ --sb-danger: #ff6467;
62
+ --sb-danger-wash: rgb(255 100 103 / 0.14);
63
+ --sb-shadow: none;
64
+
65
+ color-scheme: dark;
66
+ }
67
+
68
+ background: var(--sb-bg);
69
+ color: var(--sb-ink);
70
+ font:
71
+ 14px/1.45 system-ui,
72
+ -apple-system,
73
+ 'Segoe UI',
74
+ Roboto,
75
+ sans-serif;
76
+ -webkit-font-smoothing: antialiased;
77
+ min-block-size: 100dvh;
78
+ padding: 28px 32px 48px;
79
+
80
+ @media (width < 640px) {
81
+ padding: 20px 16px 40px;
82
+ }
83
+
84
+ & ::selection {
85
+ background: var(--sb-accent-wash);
86
+ }
87
+
88
+ /* The site's global styles would otherwise leak in. */
89
+ & :is(p, h1, h2, h3, dl, ul) {
90
+ margin: 0;
91
+ }
92
+
93
+ & svg {
94
+ display: inline-block;
95
+ flex: none;
96
+ max-inline-size: none;
97
+ }
98
+
99
+ & .card {
100
+ background: var(--sb-surface);
101
+ border: 1px solid var(--sb-line);
102
+ border-radius: 10px;
103
+ box-shadow: var(--sb-shadow);
104
+ padding: 20px;
105
+
106
+ /* Tables and toolbars run edge to edge; they pad themselves. */
107
+ &.flush {
108
+ overflow: hidden;
109
+ padding: 0;
110
+ }
111
+ }
112
+
113
+ /* In an edge-to-edge card the table lines up with the toolbar above it. */
114
+ & .flush > .table :is(th, td):first-child {
115
+ padding-inline-start: 20px;
116
+ }
117
+
118
+ & .flush > .table :is(th, td):last-child {
119
+ padding-inline-end: 16px;
120
+ }
121
+
122
+ /* A search field with its magnifier inside. */
123
+ & .search {
124
+ align-items: center;
125
+ display: flex;
126
+ position: relative;
127
+
128
+ & svg {
129
+ color: var(--sb-muted);
130
+ inset-inline-start: 11px;
131
+ pointer-events: none;
132
+ position: absolute;
133
+ z-index: 1;
134
+ }
135
+
136
+ & input {
137
+ flex: 1;
138
+ min-inline-size: 0;
139
+ padding-inline-start: 34px;
140
+ }
141
+ }
142
+
143
+ /* Segmented control, e.g. the order status filter. */
144
+ & .segments {
145
+ background: var(--sb-raised);
146
+ border: 1px solid var(--sb-line);
147
+ border-radius: 8px;
148
+ display: flex;
149
+ gap: 2px;
150
+ padding: 3px;
151
+
152
+ & button {
153
+ border-radius: 5px;
154
+ color: var(--sb-muted);
155
+ cursor: pointer;
156
+ font-size: 13px;
157
+ font-weight: 500;
158
+ padding: 5px 11px;
159
+ text-transform: capitalize;
160
+ transition:
161
+ background 0.15s,
162
+ color 0.15s;
163
+
164
+ &:hover {
165
+ color: var(--sb-ink);
166
+ }
167
+
168
+ &[aria-checked='true'] {
169
+ background: var(--sb-surface);
170
+ box-shadow:
171
+ 0 1px 2px rgb(0 0 0 / 0.08),
172
+ 0 0 0 1px var(--sb-line);
173
+ color: var(--sb-ink);
174
+ }
175
+
176
+ &:focus-visible {
177
+ outline: 2px solid var(--sb-accent);
178
+ outline-offset: 1px;
179
+ }
180
+ }
181
+ }
182
+
183
+ & .card-head {
184
+ align-items: baseline;
185
+ display: flex;
186
+ flex-wrap: wrap;
187
+ gap: 4px 12px;
188
+ justify-content: space-between;
189
+
190
+ & h2 {
191
+ font-size: 15px;
192
+ font-weight: 600;
193
+ }
194
+ }
195
+
196
+ & .muted {
197
+ color: var(--sb-muted);
198
+ }
199
+
200
+ & input,
201
+ & textarea {
202
+ background: var(--sb-surface);
203
+ border: 1px solid var(--sb-line);
204
+ border-radius: 8px;
205
+ color: var(--sb-ink);
206
+ font: inherit;
207
+ padding: 8px 12px;
208
+ transition:
209
+ border-color 0.15s,
210
+ box-shadow 0.15s;
211
+
212
+ &::placeholder {
213
+ color: var(--sb-muted);
214
+ opacity: 0.8;
215
+ }
216
+
217
+ &:hover {
218
+ border-color: var(--sb-line-strong);
219
+ }
220
+
221
+ &:focus {
222
+ border-color: var(--sb-accent);
223
+ box-shadow: 0 0 0 3px var(--sb-accent-wash);
224
+ outline: none;
225
+ }
226
+ }
227
+
228
+ & .button {
229
+ align-items: center;
230
+ background: var(--sb-surface);
231
+ border: 1px solid var(--sb-line);
232
+ border-radius: 8px;
233
+ box-shadow: 0 1px 2px rgb(0 0 0 / 0.05);
234
+ color: var(--sb-ink);
235
+ cursor: pointer;
236
+ display: inline-flex;
237
+ font-size: 14px;
238
+ font-weight: 500;
239
+ gap: 6px;
240
+ padding: 8px 14px;
241
+ text-decoration: none;
242
+ transition:
243
+ background 0.15s,
244
+ border-color 0.15s;
245
+ white-space: nowrap;
246
+
247
+ &:hover {
248
+ background: var(--sb-raised);
249
+ border-color: var(--sb-line-strong);
250
+ }
251
+
252
+ &:active {
253
+ transform: translateY(1px);
254
+ }
255
+
256
+ &:focus-visible {
257
+ outline: 2px solid var(--sb-accent);
258
+ outline-offset: 2px;
259
+ }
260
+
261
+ &:disabled {
262
+ cursor: wait;
263
+ opacity: 0.6;
264
+ }
265
+
266
+ &.primary {
267
+ background: var(--sb-primary);
268
+ border-color: var(--sb-primary);
269
+ color: var(--sb-on-primary);
270
+
271
+ &:hover {
272
+ background: var(--sb-primary-hover);
273
+ }
274
+ }
275
+
276
+ &.danger {
277
+ background: var(--sb-danger);
278
+ border-color: var(--sb-danger);
279
+ color: var(--sb-surface);
280
+
281
+ &:hover {
282
+ background: var(--sb-danger);
283
+ filter: brightness(1.08);
284
+ }
285
+ }
286
+ }
287
+
288
+ & .link {
289
+ color: var(--sb-accent-ink);
290
+ cursor: pointer;
291
+ font-weight: 500;
292
+ text-align: start;
293
+
294
+ &:hover {
295
+ text-decoration: underline;
296
+ }
297
+ }
298
+
299
+ & .pill {
300
+ background: var(--sb-accent-wash);
301
+ border-radius: 999px;
302
+ color: var(--sb-accent-ink);
303
+ display: inline-block;
304
+ flex: none;
305
+ font-size: 11px;
306
+ font-weight: 600;
307
+ letter-spacing: 0.02em;
308
+ line-height: 18px;
309
+ margin-inline: 4px;
310
+ padding: 0 8px;
311
+ }
312
+
313
+ /* A dot and the word, so state never rides on colour alone. */
314
+ & .status {
315
+ align-items: center;
316
+ border-radius: 999px;
317
+ display: inline-flex;
318
+ font-size: 12px;
319
+ font-weight: 500;
320
+ gap: 6px;
321
+ padding: 2px 9px 2px 7px;
322
+ text-transform: capitalize;
323
+
324
+ &::before {
325
+ background: currentColor;
326
+ block-size: 6px;
327
+ border-radius: 50%;
328
+ content: '';
329
+ inline-size: 6px;
330
+ }
331
+
332
+ &[data-status='paid'] {
333
+ background: var(--sb-good-wash);
334
+ color: var(--sb-good);
335
+ }
336
+
337
+ &[data-status='pending'] {
338
+ background: var(--sb-warn-wash);
339
+ color: var(--sb-warn);
340
+ }
341
+
342
+ &[data-status='failed'] {
343
+ background: var(--sb-danger-wash);
344
+ color: var(--sb-danger);
345
+ }
346
+
347
+ &[data-status='refunded'] {
348
+ background: var(--sb-raised);
349
+ color: var(--sb-muted);
350
+ }
351
+
352
+ &[data-status='ready'] {
353
+ background: var(--sb-good-wash);
354
+ color: var(--sb-good);
355
+ }
356
+
357
+ &:is([data-status='preparing'], [data-status='unused']) {
358
+ background: var(--sb-warn-wash);
359
+ color: var(--sb-warn);
360
+ }
361
+
362
+ &[data-status='errored'] {
363
+ background: var(--sb-danger-wash);
364
+ color: var(--sb-danger);
365
+ }
366
+ }
367
+
368
+ & .table {
369
+ border-collapse: collapse;
370
+ inline-size: 100%;
371
+
372
+ & th,
373
+ & td {
374
+ border-block-end: 1px solid var(--sb-line);
375
+ padding: 10px 12px;
376
+ text-align: start;
377
+ vertical-align: middle;
378
+ }
379
+
380
+ & th:first-child,
381
+ & td:first-child {
382
+ padding-inline-start: 4px;
383
+ }
384
+
385
+ & th {
386
+ color: var(--sb-muted);
387
+ font-size: 12px;
388
+ font-weight: 500;
389
+ white-space: nowrap;
390
+ }
391
+
392
+ & tbody tr:last-child td {
393
+ border-block-end: 0;
394
+ }
395
+
396
+ & .number {
397
+ font-variant-numeric: tabular-nums;
398
+ text-align: end;
399
+ white-space: nowrap;
400
+ }
401
+ }
402
+
403
+ & .empty {
404
+ padding: 48px 16px;
405
+ text-align: center;
406
+
407
+ & > svg {
408
+ background: var(--sb-raised);
409
+ block-size: 56px;
410
+ border-radius: 50%;
411
+ box-sizing: border-box;
412
+ color: var(--sb-muted);
413
+ inline-size: 56px;
414
+ margin-block-end: 12px;
415
+ padding: 14px;
416
+ }
417
+
418
+ & p + p {
419
+ margin-block-start: 4px;
420
+ }
421
+ }
422
+
423
+ & .problem {
424
+ align-items: center;
425
+ background: var(--sb-danger-wash);
426
+ border-radius: 6px;
427
+ color: var(--sb-danger);
428
+ display: flex;
429
+ flex-wrap: wrap;
430
+ gap: 12px;
431
+ justify-content: space-between;
432
+ padding: 10px 12px;
433
+ }
434
+
435
+ & .skeleton {
436
+ display: grid;
437
+ gap: 10px;
438
+
439
+ & > div {
440
+ animation: shimmer 1.4s ease-in-out infinite;
441
+ background: linear-gradient(
442
+ 90deg,
443
+ var(--sb-raised) 25%,
444
+ var(--sb-shimmer) 50%,
445
+ var(--sb-raised) 75%
446
+ )
447
+ 0 0 / 200% 100%;
448
+ block-size: 36px;
449
+ border-radius: 6px;
450
+ }
451
+ }
452
+
453
+ & .u-hidden {
454
+ clip-path: inset(50%);
455
+ position: absolute;
456
+ white-space: nowrap;
457
+ inline-size: 1px;
458
+ block-size: 1px;
459
+ overflow: hidden;
460
+ }
461
+ }
462
+
463
+ @keyframes shimmer {
464
+ to {
465
+ background-position: -200% 0;
466
+ }
467
+ }
468
+
469
+ @media (prefers-reduced-motion: reduce) {
470
+ .plugin * {
471
+ animation: none !important;
472
+ transition: none !important;
473
+ }
474
+ }
@@ -1,8 +0,0 @@
1
- export interface Ticketsource {
2
- event: string
3
- dates: Array<{
4
- id: string
5
- start: string
6
- price: string
7
- }>
8
- }