@atom-forge/brickee 0.0.1 → 0.0.2
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 +19 -1
- package/README-AI.md +24 -21
- package/dist/Editor.svelte +52 -64
- package/dist/Editor.svelte.d.ts +0 -3
- package/dist/LinkPrompt.svelte +3 -3
- package/dist/Preview.svelte +1 -5
- package/dist/Preview.svelte.d.ts +0 -3
- package/dist/PropsBar.svelte +120 -110
- package/dist/PropsBar.svelte.d.ts +0 -3
- package/dist/code.d.ts +12 -0
- package/dist/code.js +15 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/kit/Empty.svelte +2 -2
- package/dist/kit/Frame.svelte +3 -3
- package/dist/locales/hu.js +2 -7
- package/dist/markdown.d.ts +0 -2
- package/dist/markdown.js +1 -1
- package/dist/plugins/Gap.svelte +1 -1
- package/dist/plugins/Map.svelte +1 -1
- package/dist/plugins/Table.svelte +36 -62
- package/dist/plugins/TablePreview.svelte +2 -2
- package/dist/plugins/YouTubePreview.svelte +1 -1
- package/dist/plugins/index.js +12 -1
- package/dist/schema.js +1 -1
- package/dist/types.d.ts +6 -7
- package/docs/en/bundled-plugins.md +1 -1
- package/docs/en/documents-and-markdown.md +4 -5
- package/docs/en/editor-and-preview.md +1 -3
- package/docs/en/getting-started.md +6 -2
- package/docs/en/host-contexts.md +26 -132
- package/docs/en/index.md +4 -4
- package/docs/en/recipes-and-pitfalls.md +10 -9
- package/docs/en/upgrading.md +20 -4
- package/docs/en/writing-plugins.md +111 -11
- package/docs/hu/bundled-plugins.md +1 -1
- package/docs/hu/documents-and-markdown.md +4 -5
- package/docs/hu/editor-and-preview.md +1 -3
- package/docs/hu/getting-started.md +6 -2
- package/docs/hu/host-contexts.md +26 -132
- package/docs/hu/index.md +4 -4
- package/docs/hu/recipes-and-pitfalls.md +10 -9
- package/docs/hu/upgrading.md +20 -4
- package/docs/hu/writing-plugins.md +111 -11
- package/package.json +6 -2
- package/dist/ImagePicker.svelte +0 -131
- package/dist/ImagePicker.svelte.d.ts +0 -13
- package/dist/media.d.ts +0 -17
- package/dist/media.js +0 -12
|
@@ -38,23 +38,22 @@ export const messages: Messages = { ...hu, Button: 'Gomb', 'A link as a button':
|
|
|
38
38
|
```svelte
|
|
39
39
|
<!-- lib/BlockEditor.svelte -->
|
|
40
40
|
<script lang="ts">
|
|
41
|
-
import { Editor, provideView, type
|
|
41
|
+
import { Editor, provideView, type StoredBlock } from '@atom-forge/brickee';
|
|
42
42
|
import { messages, PAGE_PALETTE, plugins } from './blocks';
|
|
43
43
|
|
|
44
44
|
let {
|
|
45
45
|
value = $bindable(),
|
|
46
|
-
media = [],
|
|
47
46
|
palette = PAGE_PALETTE,
|
|
48
47
|
view
|
|
49
|
-
}: { value: StoredBlock[];
|
|
48
|
+
}: { value: StoredBlock[]; palette?: string[]; view?: string } = $props();
|
|
50
49
|
|
|
51
50
|
provideView(() => view);
|
|
52
51
|
</script>
|
|
53
52
|
|
|
54
|
-
<Editor bind:value {plugins} {palette} {
|
|
53
|
+
<Editor bind:value {plugins} {palette} {messages} />
|
|
55
54
|
```
|
|
56
55
|
|
|
57
|
-
A közzétett oldalt a `<Preview blocks={doc} {plugins} {
|
|
56
|
+
A közzétett oldalt a `<Preview blocks={doc} {plugins} {messages} />`-val és ugyanazokkal a `plugins`-szal rendereld, így egy blokk ugyanúgy néz ki a szerkesztő előnézetében és az oldalon.
|
|
58
57
|
|
|
59
58
|
## A teljes dokumentum másolása markdownként
|
|
60
59
|
|
|
@@ -62,13 +61,13 @@ A kijelölt blokkok másolása már most is markdownt tesz a vágólapra. Egy, a
|
|
|
62
61
|
|
|
63
62
|
```svelte
|
|
64
63
|
<script lang="ts">
|
|
65
|
-
import { fromStored, toMarkdown, type
|
|
64
|
+
import { fromStored, toMarkdown, type StoredBlock } from '@atom-forge/brickee';
|
|
66
65
|
import { plugins } from './blocks';
|
|
67
66
|
|
|
68
|
-
let { doc
|
|
67
|
+
let { doc }: { doc: StoredBlock[] } = $props();
|
|
69
68
|
|
|
70
69
|
// Kattintáskezelőben fut, így a csak böngészőben működő toMarkdown itt biztonságos.
|
|
71
|
-
const copy = () => navigator.clipboard.writeText(toMarkdown(doc.map(fromStored), { plugins
|
|
70
|
+
const copy = () => navigator.clipboard.writeText(toMarkdown(doc.map(fromStored), { plugins }));
|
|
72
71
|
</script>
|
|
73
72
|
|
|
74
73
|
<button type="button" onclick={copy}>Copy as markdown</button>
|
|
@@ -106,10 +105,12 @@ Az alkalmazás a csomag lefordított `dist/` mappáját olvassa, ezért minden v
|
|
|
106
105
|
| Tünet | Ok | Megoldás |
|
|
107
106
|
|---|---|---|
|
|
108
107
|
| A blokkmezők és a popupok stílus nélkülinek tűnnek | Hiányzik az `@atom-forge/ui` téma CSS-e vagy a Tailwind `@source`-a | Állítsd be az `@atom-forge/ui`-t a dokumentációja szerint, és az alkalmazást a `Root`-ja alatt rendereld. |
|
|
108
|
+
| A blokkfogantyúk balra kilógnak | A fogantyúk a szerkesztő dobozától balra jelennek meg | Adj a szerkesztőt körülvevő elemnek kb. 22 px bal oldali paddinget. |
|
|
109
109
|
| A popupok nem nyílnak meg, vagy „context not found” hibák linkelt csomaggal | A `svelte` vagy az `@atom-forge/ui` két példányban van jelen | Vedd fel őket a Vite `resolve.dedupe` listájába. |
|
|
110
110
|
| Egy plugin blokkja „Unknown block” feliratot mutat | A pluginja hiányzik a `plugins`-ból | Egy dokumentumtípus minden szerkesztőjének és előnézetének ugyanazokat a pluginokat add át. |
|
|
111
111
|
| Egy blokk hiányzik a `Preview`-ból | A pluginjának nincs `preview`-ja | Adj a pluginnak `preview`-t (a mellékelt pluginoknak van alapértelmezett), vagy rendereld a blokkot a saját megjelenítőddel. |
|
|
112
|
-
| Egy plugin blokkja hiányzik az oldalról | Az előnézet más `plugins`-t kapott
|
|
112
|
+
| Egy plugin blokkja hiányzik az oldalról | Az előnézet más `plugins`-t kapott | Ugyanazt a pluginlistát használd a szerkesztőhöz és az előnézethez. |
|
|
113
|
+
| Egy képblokk semmit sem renderel | Az előnézete elutasítja a tárolt címet, vagy az alkalmazás saját keresése nem ismeri a tárolt azonosítót | Olyan címet tárolj, amelyet az előnézet elfogad (`http(s)` vagy az oldalon belüli); az azonosítókat a szerkesztőben és az oldalon ugyanabból az adatból oldd fel. |
|
|
113
114
|
| Egy doboz nem tehető egy másik dobozba | A beágyazott szerkesztők egy szint mélyek | A tárolókat tartalomblokkok befogadására tervezd, ne más tárolókéra. |
|
|
114
115
|
| Egy blokk nem érhető el egy tárolón belül | Az `accepts` vagy az alkalmazás `palette`-je kihagyja | Vedd fel a palettaazonosítót az `accepts`-be (vagy használd az `'md:*'`-ot az összes beépítetthez). |
|
|
115
116
|
| Egy egyedi blokk kétszer jelenik meg | A séma nem kényszeríti ki a `unique`-ot | Ellenőrizd a szerveren, ha az alkalmazásod számít rá. |
|
package/docs/hu/upgrading.md
CHANGED
|
@@ -6,11 +6,27 @@ Ez a fejezet azokat a változásokat sorolja fel, amelyek teendőt igényelhetne
|
|
|
6
6
|
|
|
7
7
|
A tőled semmit nem igénylő változások – új exportok, javítások, dokumentációfrissítések – csak a változásnaplóban (`CHANGELOG.md`, a csomaggal együtt szállítva) szerepelnek.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
>
|
|
11
|
-
> Az `@atom-forge/brickee`-nek még nem volt olyan kiadása, amely változtatást igényelt volna az azt használó kódban. Az alábbi szakasz csak azoknak az alkalmazásoknak szól, amelyek már azelőtt használták a szerkesztőt, hogy ebből a csomagból lett volna.
|
|
9
|
+
## A 0.0.1-ről a 0.0.2-re
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
### A képek és a média már nem részei a csomagnak
|
|
12
|
+
|
|
13
|
+
A csomag többé nem ismeri a kép fogalmát. Hogy egy blokk a kép URL-jét vagy egy azonosítót tárol, hogyan választja ki a felhasználó a képet, és hogyan lesz a tárolt értékből cím, azt az alkalmazás saját képpluginja dönti el (lásd [Képek](writing-plugins.md#képek)).
|
|
14
|
+
|
|
15
|
+
| Eltávolítva | Mi volt |
|
|
16
|
+
|---|---|
|
|
17
|
+
| `kind: 'image'`, `kind: 'images'` | Mezőfajták, amelyek egy kiválasztott médiaelem azonosítóját (vagy azonosítóit) tárolták, a beépített képválasztó felugró ablakkal |
|
|
18
|
+
| Az `Editor` és a `Preview` `media` propja | A képek, amelyek közül a blokkok választhattak, és amelyekre hivatkozhattak |
|
|
19
|
+
| `provideMedia`, `useMedia`, `findMedia`, `MediaItem` | A média-kontextus, a keresője és a típusa |
|
|
20
|
+
| A `toMarkdown` `media` beállítása | A `toMarkdown(blocks, { plugins, media })` helyett most `toMarkdown(blocks, { plugins })` |
|
|
21
|
+
| `ctx.media` egy plugin `markdown(props, ctx)` függvényében | A `MarkdownContext` most `{ inner }` |
|
|
22
|
+
|
|
23
|
+
A `PropField['kind']` most `'text' | 'textarea' | 'color' | 'select' | 'slider'`. A tárolt dokumentumokat ez nem érinti: egy blokk propsa megtartja, amit tárolt, az azonosítókat is.
|
|
24
|
+
|
|
25
|
+
**Teendő:** a képpluginjaidban cseréld le az `image` és `images` mezőket saját kiválasztásra — egy gombra a plugin nézetében, amely megnyitja a médiaablakodat vagy egy feltöltést, és az eredményt a `props`-ba írja, vagy egy egyszerű `text` mezőre egy URL-hez —, és tartsd meg a prop-kulcsokat, hogy a tárolt dokumentumok továbbra is betöltődjenek. Töröld a `media` propot a szerkesztőidről és előnézeteidről, és a `media` beállítást a `toMarkdown`-hívásaidból. Ahol egy nézet, előnézet vagy `markdown` alak a `useMedia`, `findMedia` vagy `ctx.media` segítségével alakított címmé egy azonosítót, ott az alkalmazásod saját adatában keresd ki: az alkalmazásod egy moduljában, vagy egy saját Svelte-kontextusban, amelyet a szerkesztő és az előnézet fölött adsz meg (a `markdown` komponenseken kívül fut, ezért modulbeli adat kell neki). Ha továbbra is szükséged van rá, definiálj egy `MediaItem`-szerű típust az alkalmazásodban.
|
|
26
|
+
|
|
27
|
+
## A `block-editor` munkaterület-csomagról a 0.0.1-es változatra
|
|
28
|
+
|
|
29
|
+
Ez a szakasz csak azoknak az alkalmazásoknak szól, amelyek már azelőtt használták a szerkesztőt, hogy ebből a csomagból lett volna.
|
|
14
30
|
|
|
15
31
|
A szerkesztő `block-editor` néven, egy alkalmazáson belüli munkaterület-csomagként indult. Ugyanazzal a tárolt alakkal költözött ide `@atom-forge/brickee` néven, így az elmentett dokumentumokat nem kell átalakítani. Az import-azonosítók megváltoztak, a plugin-kit a pluginok belépési pontjából átkerült a magba, az oldalspecifikus pluginok pedig az alkalmazásnál maradtak.
|
|
16
32
|
|
|
@@ -141,7 +141,7 @@ Regisztráld a többi mellé:
|
|
|
141
141
|
|
|
142
142
|
> **A props a felhasználóktól jön**
|
|
143
143
|
>
|
|
144
|
-
> Egy plugin propsa az, ami el lett tárolva — amit egy szerkesztő begépelt, beillesztett, vagy amit bárki, aki dokumentumot menthet, elküldött az API-dnak. A séma csak az alakot ellenőrzi. Minden előnézetnek megbízhatatlanként kell kezelnie a propsot: a színeket a `safeColor`-on keresztül vedd át, ellenőrizd a linkeket
|
|
144
|
+
> Egy plugin propsa az, ami el lett tárolva — amit egy szerkesztő begépelt, beillesztett, vagy amit bárki, aki dokumentumot menthet, elküldött az API-dnak. A séma csak az alakot ellenőrzi. Minden előnézetnek megbízhatatlanként kell kezelnie a propsot: a színeket a `safeColor`-on keresztül vedd át, ellenőrizd a linkeket és a képek címét, és korlátozd a számokat. A Svelte escape-eli a `{...}`-ban lévő szöveget; propot soha ne tegyél `{@html}`-be.
|
|
145
145
|
|
|
146
146
|
## Mezők
|
|
147
147
|
|
|
@@ -151,9 +151,9 @@ Minden `PropField` egy beállítás a blokk tetején. A szerkesztő rajzolja ki
|
|
|
151
151
|
|---|---|
|
|
152
152
|
| `key` | A prop, amelyet szerkeszt. |
|
|
153
153
|
| `label` | A címkéje (a `messages`-en keresztül fordítva). |
|
|
154
|
-
| `kind` | `text`, `textarea`, `color`, `select`, `slider
|
|
154
|
+
| `kind` | `text`, `textarea`, `color`, `select`, `slider` vagy `toggles`. |
|
|
155
155
|
| `placeholder` | Üres szövegmező tippje; opcionális színnél az a szín, amelyről a „none” gombra kattintás indul. |
|
|
156
|
-
| `options` | `{ value, label }[]` a `select`-hez, valamint az értékeken lépkedő `slider`-hez. Egy opció címkéje tartalmazhat `{n}`-t, amelyet az értéke tölt ki. |
|
|
156
|
+
| `options` | `{ value, label }[]` a `select`-hez, valamint az értékeken lépkedő `slider`-hez. Egy opció címkéje tartalmazhat `{n}`-t, amelyet az értéke tölt ki. A `toggles`-hez `{ value, label, icon }[]`: az `icon` a gomb, a `label` a buboréksúgója. |
|
|
157
157
|
| `min`, `max`, `step`, `unit` | Számcsúszka (`slider`): a tartománya, a lépésköze (alapértelmezés 1) és az érték után mutatott mértékegység. |
|
|
158
158
|
| `none` | A beállítatlan állapot címkéje: számcsúszkánál a `min`-től balra lévő rovátka, opcionális színnél a „none” gomb. |
|
|
159
159
|
| `views` | Nézetenkénti értékek egy `slider`-hez (lásd [Nézetenkénti értékek](host-contexts.md#nézetenkénti-értékek)). |
|
|
@@ -169,8 +169,7 @@ Az egyes fajták így tárolják az értéküket:
|
|
|
169
169
|
| `slider` `options`-szel | A csúszka pozíciójában lévő opció értéke. |
|
|
170
170
|
| `slider` `min`-nel és `max`-szal | A szám szövegként (például `'56'`); a `none` rovátka eltávolítja a kulcsot. |
|
|
171
171
|
| `color` | Egy `#rrggbb` szöveg; `none` esetén a törlése eltávolítja a kulcsot. |
|
|
172
|
-
| `
|
|
173
|
-
| `images` | Azonosítók tömbje, kiválasztási sorrendben; egy már kiválasztott elem újbóli kiválasztása kiveszi. |
|
|
172
|
+
| `toggles` | A bekapcsolt opciók listája az `options` sorrendjében (ha egy sincs, `[]`); ilyen a beépített táblázat fejléce és lábléce. |
|
|
174
173
|
|
|
175
174
|
Az üres érték (`''`, `null` vagy `undefined`, akkor is, ha a `parse` adja vissza) eltávolítja a kulcsot a `props`-ból, ezért az előnézetnek mindig kezelnie kell a hiányzó propot. A mező `parse`-a jó hely a bemenet normalizálására is: a mellékelt YouTube-mező bármilyen YouTube-linket elfogad, de csak a videó azonosítóját tárolja.
|
|
176
175
|
|
|
@@ -189,7 +188,7 @@ A `props`-ot és a `blocks`-ot akkor is `$bindable()`-lel deklaráld, ha a néze
|
|
|
189
188
|
|
|
190
189
|
## Az előnézet
|
|
191
190
|
|
|
192
|
-
Az előnézet `PluginPreviewProps`-ot kap: `props`, `blocks` és `plugins`. Ez egy egyszerű Svelte-komponens, amely az oldal számára rendereli a blokkot. A
|
|
191
|
+
Az előnézet `PluginPreviewProps`-ot kap: `props`, `blocks` és `plugins`. Ez egy egyszerű Svelte-komponens, amely az oldal számára rendereli a blokkot. A szövegek és a nézet a kontextusokon keresztül érhető el (`useText`, `useView`; lásd [Az alkalmazás kontextusai](host-contexts.md)); a képek a plugin saját ügyei (lásd [Képek](#képek)).
|
|
193
192
|
|
|
194
193
|
## Tárolók
|
|
195
194
|
|
|
@@ -275,10 +274,7 @@ Mindkettőre tipikus példa egy csak támogatóknak szóló elválasztó: dokume
|
|
|
275
274
|
|
|
276
275
|
## Markdown alak
|
|
277
276
|
|
|
278
|
-
A `markdown(props, { inner
|
|
279
|
-
|
|
280
|
-
- `inner` — a beágyazott blokkok, már markdownként (`''`, ha nincsenek);
|
|
281
|
-
- `media` — a dokumentum médiája, hogy egy kép azonosítóját a `findMedia(media, id)?.url`-lel URL-lé alakíthasd.
|
|
277
|
+
A `markdown(props, { inner })` markdownként adja vissza a blokkot; ezt használja a szerkesztő blokkok másolásakor, és a `toMarkdown`. Az `inner` a beágyazott blokkok, már markdownként (`''`, ha nincsenek).
|
|
282
278
|
|
|
283
279
|
`''` visszaadásával kihagyod a blokkot. Azoknak a pluginoknak, amelyek kimenetének szövegként nincs értelme (hirdetéshely, a mellékelt `gap`), egyszerűen nincs markdown alakjuk. Három segédfüggvény gondoskodik a jól formált kimenetről:
|
|
284
280
|
|
|
@@ -288,6 +284,111 @@ A `markdown(props, { inner, media })` markdownként adja vissza a blokkot; ezt h
|
|
|
288
284
|
| `mdJoin(...parts)` | Különálló blokkokként fűzi össze a részeket (köztük üres sorral), kihagyva az üreseket és a hamis értékűeket. |
|
|
289
285
|
| `mdQuote(md)` | Minden sor elé `> `-t tesz, idézetblokként renderelt dobozhoz. |
|
|
290
286
|
|
|
287
|
+
## Képek
|
|
288
|
+
|
|
289
|
+
A szerkesztő nem ismeri a kép fogalmát: nincs képmező, nincs képválasztó, nincs médialista. Hogy egy blokk a kép URL-jét vagy az alkalmazás saját médiatárában lévő kép azonosítóját tárolja, hogyan választja ki a felhasználó, és hogyan lesz a tárolt értékből cím, az az alkalmazás döntése, így a képblokk az alkalmazás egy közönséges pluginja.
|
|
290
|
+
|
|
291
|
+
Egy minimális `photo` plugin a kép címét tárolja, amelyet egy szövegmezőbe kell beírni, mellette alternatív szöveggel és magasságcsúszkával:
|
|
292
|
+
|
|
293
|
+
```svelte
|
|
294
|
+
<!-- PhotoView.svelte -->
|
|
295
|
+
<script lang="ts" module>
|
|
296
|
+
export interface PhotoProps extends Record<string, unknown> {
|
|
297
|
+
/** A kép címe. */
|
|
298
|
+
url: string;
|
|
299
|
+
alt: string;
|
|
300
|
+
/** A magasság a szélesség %-ában (20–150), vagy beállítatlan a kép saját alakjához. */
|
|
301
|
+
ratio?: unknown;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/** A tárolt cím, ha http(s) vagy az oldalon belüli, különben ''. */
|
|
305
|
+
export const imageSrc = (u: unknown) => {
|
|
306
|
+
const s = String(u ?? '').trim();
|
|
307
|
+
return /^(https?:|\/(?!\/))/.test(s) ? s : '';
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
/** CSS aspect-ratio egy tárolt magasságból, vagy undefined. */
|
|
311
|
+
export const aspect = (v: unknown) => {
|
|
312
|
+
const n = Number(v);
|
|
313
|
+
return n >= 20 && n <= 150 ? `100 / ${n}` : undefined;
|
|
314
|
+
};
|
|
315
|
+
</script>
|
|
316
|
+
|
|
317
|
+
<script lang="ts">
|
|
318
|
+
import { ImageIcon } from '@lucide/svelte';
|
|
319
|
+
import { Empty, type PluginViewProps } from '@atom-forge/brickee';
|
|
320
|
+
|
|
321
|
+
let { props = $bindable(), blocks = $bindable() }: PluginViewProps<PhotoProps> = $props();
|
|
322
|
+
const src = $derived(imageSrc(props.url));
|
|
323
|
+
</script>
|
|
324
|
+
|
|
325
|
+
{#if src}
|
|
326
|
+
<img {src} alt={props.alt} style="max-width: 100%" />
|
|
327
|
+
{:else}
|
|
328
|
+
<Empty icon={ImageIcon} text="Set the image's address above" />
|
|
329
|
+
{/if}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
```svelte
|
|
333
|
+
<!-- PhotoPreview.svelte -->
|
|
334
|
+
<script lang="ts">
|
|
335
|
+
import { baseValue, type PluginPreviewProps } from '@atom-forge/brickee';
|
|
336
|
+
import { aspect, imageSrc, type PhotoProps } from './PhotoView.svelte';
|
|
337
|
+
|
|
338
|
+
let { props }: PluginPreviewProps<PhotoProps> = $props();
|
|
339
|
+
const src = $derived(imageSrc(props.url));
|
|
340
|
+
</script>
|
|
341
|
+
|
|
342
|
+
{#if src}
|
|
343
|
+
<img {src} alt={props.alt ?? ''} style:aspect-ratio={aspect(baseValue(props.ratio))} />
|
|
344
|
+
{/if}
|
|
345
|
+
|
|
346
|
+
<style>
|
|
347
|
+
img {
|
|
348
|
+
width: 100%;
|
|
349
|
+
object-fit: cover;
|
|
350
|
+
}
|
|
351
|
+
</style>
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
```ts
|
|
355
|
+
// photo.ts
|
|
356
|
+
import { ImageIcon } from '@lucide/svelte';
|
|
357
|
+
import { mdText, type BlockPlugin } from '@atom-forge/brickee';
|
|
358
|
+
import PhotoView, { type PhotoProps } from './PhotoView.svelte';
|
|
359
|
+
import PhotoPreview from './PhotoPreview.svelte';
|
|
360
|
+
|
|
361
|
+
export const photo: BlockPlugin<PhotoProps> = {
|
|
362
|
+
type: 'photo',
|
|
363
|
+
label: 'Photo',
|
|
364
|
+
hint: 'One image',
|
|
365
|
+
keys: 'photo image picture',
|
|
366
|
+
icon: ImageIcon,
|
|
367
|
+
props: () => ({ url: '', alt: '' }),
|
|
368
|
+
fields: [
|
|
369
|
+
{ key: 'url', label: 'Image', kind: 'text', placeholder: 'https://…', parse: (s) => s.trim() },
|
|
370
|
+
{ key: 'alt', label: 'Alt text', kind: 'text', placeholder: 'What the image shows' },
|
|
371
|
+
{ key: 'ratio', label: 'Height', kind: 'slider', min: 20, max: 150, unit: '%', none: 'Original' }
|
|
372
|
+
],
|
|
373
|
+
tag: () => 'photo',
|
|
374
|
+
content: 'none',
|
|
375
|
+
markdown: (p) => {
|
|
376
|
+
const src = imageSrc(p.url);
|
|
377
|
+
return src ? `` : '';
|
|
378
|
+
},
|
|
379
|
+
view: PhotoView,
|
|
380
|
+
preview: PhotoPreview
|
|
381
|
+
};
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
A cím ugyanolyan prop, mint a többi, tehát megbízhatatlan: a nézet és az előnézet csak akkor rendereli, ha `http(s)` vagy az oldalon belüli cím, ahogy a gomb is ellenőrzi a linkjét.
|
|
385
|
+
|
|
386
|
+
Más alkalmazások másként döntenek, de mindezt a pluginon belül:
|
|
387
|
+
|
|
388
|
+
- **Médiatár.** A blokk URL helyett az alkalmazás médiatárában lévő kép azonosítóját tárolja. A nézet, az előnézet és a `markdown` az alkalmazás saját keresésével alakítja címmé az azonosítót: az alkalmazás egy moduljával, vagy egy saját Svelte-kontextussal, amelyet a szerkesztő és az előnézet fölött ad meg (a plugin-komponensek ezek alatt renderelődnek). A `markdown` komponenseken kívül fut — blokkok másolásakor és a `toMarkdown`-ban —, ezért csak modulbeli adatot használhat.
|
|
389
|
+
- **Kiválasztás.** Szövegmező helyett a nézet saját választót is mutathat — egy gombot, amely megnyitja az alkalmazás médiaablakát vagy egy feltöltést —, és az eredményt a `props`-ba írja, amely kötött (bindable).
|
|
390
|
+
- **Több kép.** Egy galéria egyetlen propban tárol egy listát (URL-ekből vagy azonosítókból), és ugyanígy a nézetében szerkeszti.
|
|
391
|
+
|
|
291
392
|
## A kit
|
|
292
393
|
|
|
293
394
|
| Export | Fajta | Használat |
|
|
@@ -299,7 +400,6 @@ A `markdown(props, { inner, media })` markdownként adja vissza a blokkot; ezt h
|
|
|
299
400
|
| `baseValue(value)` | függvény | Egy nézetenkénti prop alapértéke. |
|
|
300
401
|
| `viewValues(value, keys)` | függvény | Egy nézetenkénti prop értéke minden nézetre. |
|
|
301
402
|
| `mdText`, `mdJoin`, `mdQuote` | függvények | Markdown alakok (fent). |
|
|
302
|
-
| `useMedia`, `findMedia` | függvények | A dokumentum képei (lásd [Az alkalmazás kontextusai](host-contexts.md)). |
|
|
303
403
|
| `useText` | függvény | Lefordítja a plugin saját felületi szövegeit. |
|
|
304
404
|
| `useView` | függvény | Az alkalmazás által éppen mutatott nézet. |
|
|
305
405
|
| `Preview` | komponens | Egy tároló beágyazott blokkjait rendereli (`nested`-del). |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atom-forge/brickee",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"author": {
|
|
5
5
|
"name": "Gergely Laborci",
|
|
6
6
|
"url": "https://github.com/laborci"
|
|
@@ -87,13 +87,17 @@
|
|
|
87
87
|
"@atom-forge/ship": "0.2.0",
|
|
88
88
|
"@atom-forge/ui": "^0.0.37",
|
|
89
89
|
"@lucide/svelte": "^1.59.0",
|
|
90
|
-
"@sveltejs/adapter-
|
|
90
|
+
"@sveltejs/adapter-static": "^4.0.1",
|
|
91
91
|
"@sveltejs/kit": "^2.50.2",
|
|
92
92
|
"@sveltejs/package": "^2.5.7",
|
|
93
93
|
"@sveltejs/vite-plugin-svelte": "^6.2.4",
|
|
94
|
+
"@tailwindcss/forms": "^0.5.11",
|
|
95
|
+
"@tailwindcss/typography": "^0.5.20",
|
|
96
|
+
"@tailwindcss/vite": "^4.3.5",
|
|
94
97
|
"publint": "^0.3.15",
|
|
95
98
|
"svelte": "^5.57.1",
|
|
96
99
|
"svelte-check": "^4.4.2",
|
|
100
|
+
"tailwindcss": "^4.3.5",
|
|
97
101
|
"typescript": "^5.9.3",
|
|
98
102
|
"vite": "^7.3.1",
|
|
99
103
|
"zod": "^4.6.7"
|
package/dist/ImagePicker.svelte
DELETED
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
<!-- The image picker popup of an image field (see PropsBar): every image attached to the document, grouped. One image:
|
|
2
|
-
a click picks it and closes; several: clicks add and remove (the numbers show the order), Done or Escape closes. -->
|
|
3
|
-
<script lang="ts">
|
|
4
|
-
import { Button, getPopupManager } from '@atom-forge/ui';
|
|
5
|
-
import type { Translate } from './i18n';
|
|
6
|
-
import type { MediaItem } from './media';
|
|
7
|
-
|
|
8
|
-
interface Props {
|
|
9
|
-
media: MediaItem[];
|
|
10
|
-
multiple: boolean;
|
|
11
|
-
/** The picked ids as they are now, in order. */
|
|
12
|
-
getPicked: () => string[];
|
|
13
|
-
pick: (id: string) => void;
|
|
14
|
-
tr: Translate;
|
|
15
|
-
}
|
|
16
|
-
let { media, multiple, getPicked, pick, tr }: Props = $props();
|
|
17
|
-
const popups = getPopupManager();
|
|
18
|
-
const picked = $derived(getPicked());
|
|
19
|
-
|
|
20
|
-
function choose(id: string) {
|
|
21
|
-
pick(id);
|
|
22
|
-
if (!multiple) popups.resolve('focus');
|
|
23
|
-
}
|
|
24
|
-
function onkeydown(e: KeyboardEvent) {
|
|
25
|
-
if (e.key === 'Escape') {
|
|
26
|
-
e.preventDefault();
|
|
27
|
-
popups.resolve('focus');
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
let root = $state<HTMLElement>();
|
|
31
|
-
$effect(() => root?.focus());
|
|
32
|
-
</script>
|
|
33
|
-
|
|
34
|
-
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
|
35
|
-
<div class="light picker" role="dialog" aria-label={tr('Images')} tabindex="-1" {onkeydown} bind:this={root}>
|
|
36
|
-
{#if media.length}
|
|
37
|
-
<div class="grid">
|
|
38
|
-
{#each media as m, n (m.id)}
|
|
39
|
-
{@const at = picked.indexOf(m.id)}
|
|
40
|
-
{#if m.group && m.group !== media[n - 1]?.group}<span class="group">{m.group}</span>{/if}
|
|
41
|
-
<button type="button" class="thumb" class:on={at >= 0} title={m.name} aria-pressed={at >= 0} onclick={() => choose(m.id)}>
|
|
42
|
-
<img src={m.url} alt={m.name} />
|
|
43
|
-
{#if at >= 0}<span class="n">{multiple ? at + 1 : '✓'}</span>{/if}
|
|
44
|
-
</button>
|
|
45
|
-
{/each}
|
|
46
|
-
</div>
|
|
47
|
-
{#if multiple}
|
|
48
|
-
<div class="foot">
|
|
49
|
-
<span>{tr('{n} picked', { n: picked.length })}</span>
|
|
50
|
-
<Button compact onclick={() => popups.resolve('focus')}>{tr('Done')}</Button>
|
|
51
|
-
</div>
|
|
52
|
-
{/if}
|
|
53
|
-
{:else}
|
|
54
|
-
<p class="none">{tr('No images attached to this page yet.')}</p>
|
|
55
|
-
{/if}
|
|
56
|
-
</div>
|
|
57
|
-
|
|
58
|
-
<style>
|
|
59
|
-
.picker {
|
|
60
|
-
width: 360px;
|
|
61
|
-
max-height: min(440px, calc(100vh - 24px));
|
|
62
|
-
overflow: auto;
|
|
63
|
-
padding: 10px;
|
|
64
|
-
display: grid;
|
|
65
|
-
gap: 10px;
|
|
66
|
-
background: #fff;
|
|
67
|
-
color: #1d1d1f;
|
|
68
|
-
border: 1px solid #e5e5ea;
|
|
69
|
-
border-radius: 12px;
|
|
70
|
-
box-shadow: 0 10px 36px rgba(0, 0, 0, 0.2);
|
|
71
|
-
font: 13px/1.4 system-ui, sans-serif;
|
|
72
|
-
outline: none;
|
|
73
|
-
}
|
|
74
|
-
.grid {
|
|
75
|
-
display: grid;
|
|
76
|
-
grid-template-columns: repeat(5, 1fr);
|
|
77
|
-
gap: 4px;
|
|
78
|
-
}
|
|
79
|
-
.group {
|
|
80
|
-
grid-column: 1 / -1;
|
|
81
|
-
margin-top: 4px;
|
|
82
|
-
color: #8e8e96;
|
|
83
|
-
font-size: 11px;
|
|
84
|
-
}
|
|
85
|
-
.thumb {
|
|
86
|
-
position: relative;
|
|
87
|
-
padding: 0;
|
|
88
|
-
border: 2px solid transparent;
|
|
89
|
-
border-radius: 7px;
|
|
90
|
-
background: none;
|
|
91
|
-
cursor: pointer;
|
|
92
|
-
}
|
|
93
|
-
.thumb img {
|
|
94
|
-
display: block;
|
|
95
|
-
width: 100%;
|
|
96
|
-
aspect-ratio: 1;
|
|
97
|
-
object-fit: cover;
|
|
98
|
-
border-radius: 5px;
|
|
99
|
-
}
|
|
100
|
-
.thumb.on {
|
|
101
|
-
border-color: var(--color-accent, #ea580c);
|
|
102
|
-
}
|
|
103
|
-
.n {
|
|
104
|
-
position: absolute;
|
|
105
|
-
top: 2px;
|
|
106
|
-
right: 2px;
|
|
107
|
-
min-width: 16px;
|
|
108
|
-
height: 16px;
|
|
109
|
-
padding: 0 3px;
|
|
110
|
-
border-radius: 8px;
|
|
111
|
-
background: var(--color-accent, #ea580c);
|
|
112
|
-
color: #fff;
|
|
113
|
-
font: 600 10px/16px system-ui, sans-serif;
|
|
114
|
-
}
|
|
115
|
-
.foot {
|
|
116
|
-
position: sticky;
|
|
117
|
-
bottom: -10px;
|
|
118
|
-
margin: 0 -10px -10px;
|
|
119
|
-
padding: 8px 10px;
|
|
120
|
-
display: flex;
|
|
121
|
-
align-items: center;
|
|
122
|
-
justify-content: space-between;
|
|
123
|
-
background: #fff;
|
|
124
|
-
border-top: 1px solid #e5e5ea;
|
|
125
|
-
color: #8e8e96;
|
|
126
|
-
}
|
|
127
|
-
.none {
|
|
128
|
-
margin: 0;
|
|
129
|
-
color: #8e8e96;
|
|
130
|
-
}
|
|
131
|
-
</style>
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { Translate } from './i18n';
|
|
2
|
-
import type { MediaItem } from './media';
|
|
3
|
-
interface Props {
|
|
4
|
-
media: MediaItem[];
|
|
5
|
-
multiple: boolean;
|
|
6
|
-
/** The picked ids as they are now, in order. */
|
|
7
|
-
getPicked: () => string[];
|
|
8
|
-
pick: (id: string) => void;
|
|
9
|
-
tr: Translate;
|
|
10
|
-
}
|
|
11
|
-
declare const ImagePicker: import("svelte").Component<Props, {}, "">;
|
|
12
|
-
type ImagePicker = ReturnType<typeof ImagePicker>;
|
|
13
|
-
export default ImagePicker;
|
package/dist/media.d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
export interface MediaItem {
|
|
2
|
-
/** Stored in the block's props. */
|
|
3
|
-
id: string;
|
|
4
|
-
/** File name or title, shown in the picker. */
|
|
5
|
-
name: string;
|
|
6
|
-
/** Address of the image (a thumbnail size is fine for the editor). */
|
|
7
|
-
url: string;
|
|
8
|
-
/** Where it comes from (e.g. the attachment category), shown as a heading in the picker. */
|
|
9
|
-
group?: string;
|
|
10
|
-
}
|
|
11
|
-
type Get = () => MediaItem[];
|
|
12
|
-
/** Call in a component's init. Without its own list, a nested editor or preview keeps its parent's. */
|
|
13
|
-
export declare function provideMedia(own: () => MediaItem[] | undefined): void;
|
|
14
|
-
/** Call in a component's init; the returned function reads the current list. */
|
|
15
|
-
export declare const useMedia: () => Get;
|
|
16
|
-
export declare const findMedia: (list: MediaItem[], id: unknown) => MediaItem | undefined;
|
|
17
|
-
export {};
|
package/dist/media.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
// The images a document can use: the host's attachments (on a CMS page, the page's uploaded images). Blocks store
|
|
2
|
-
// an attachment's id; the editor, the preview and the plugins look it up here.
|
|
3
|
-
import { getContext, setContext } from 'svelte';
|
|
4
|
-
const KEY = 'block-editor:media';
|
|
5
|
-
/** Call in a component's init. Without its own list, a nested editor or preview keeps its parent's. */
|
|
6
|
-
export function provideMedia(own) {
|
|
7
|
-
const parent = getContext(KEY);
|
|
8
|
-
setContext(KEY, () => own() ?? parent?.() ?? []);
|
|
9
|
-
}
|
|
10
|
-
/** Call in a component's init; the returned function reads the current list. */
|
|
11
|
-
export const useMedia = () => getContext(KEY) ?? (() => []);
|
|
12
|
-
export const findMedia = (list, id) => list.find((m) => m.id === id);
|