@kosdev-code/kos-asset-manager 0.0.1-next.27 → 0.0.1-next.29

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.
Files changed (2) hide show
  1. package/README.md +113 -31
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -119,37 +119,114 @@ keys when the declaration is generated:
119
119
 
120
120
  A pattern that matches nothing is reported during the build.
121
121
 
122
- ## Using assets in a ui
122
+ ## Installing
123
+
124
+ ```sh
125
+ npm install @kosdev-code/kos-asset-manager
126
+ ```
127
+
128
+ The package is ESM. `@kosdev-code/kos-ui-sdk` (`~3.0.0`) and React 18 or
129
+ newer are peer dependencies, so it uses the copies the application already
130
+ has.
131
+
132
+ ## Setting it up
133
+
134
+ Register the models where the application registers everything else. Note the
135
+ second call — `registerAssetModels` returns a function.
123
136
 
124
137
  ```ts
125
- import { initAssets, wrapAsset, resolveAsset, preloadAssets } from '@kosdev-code/kos-asset-manager';
138
+ import { KosModelRegistry } from '@kosdev-code/kos-dispense-sdk';
139
+ import { registerAssetModels } from '@kosdev-code/kos-asset-manager';
126
140
 
127
- // once while starting, naming the applications whose assets this ui uses
128
- await initAssets({ contexts: ['my.app.id'], preload: ['boot'] });
141
+ KosModelRegistry.dispense.models();
142
+ registerAssetModels(KosModelRegistry)();
143
+ ```
129
144
 
130
- const logo = wrapAsset('brand/logo-primary');
131
- <img src={logo.src} alt="" />
145
+ Then wrap the tree in the provider, which loads the declarations everything
146
+ else reads:
132
147
 
133
- // anywhere a url is expected
134
- element.style.backgroundImage = `url(${resolveAsset('brand/logo-primary')})`;
148
+ ```tsx
149
+ import { KosAssetProvider } from '@kosdev-code/kos-asset-manager';
150
+
151
+ const App = () => (
152
+ <KosTranslationProvider>
153
+ <KosAssetProvider contexts={['my.app.id']} gate>
154
+ <MyScreens />
155
+ </KosAssetProvider>
156
+ </KosTranslationProvider>
157
+ );
158
+ ```
135
159
 
136
- // ready assets ahead of a screen that needs them
137
- await preloadAssets('checkout');
160
+ `contexts` names the applications whose assets this ui uses, so it loads only
161
+ what it needs; omit it to load everything the device declares, which is what
162
+ a tool that browses them wants. `gate` holds the children back until the
163
+ assets have loaded, which is worth it when the first screen renders them.
164
+
165
+ `contexts` is also the tie-break order for a bare key: named contexts are
166
+ searched first, in the order given, then everything else.
167
+
168
+ For an application that would rather drive its own loading UI,
169
+ `useAssetManager(contexts?)` is what the provider uses internally and returns
170
+ `{ model, ready, status, KosModelLoader }`. `useKosAssetContext()` returns
171
+ `{ model }` for components that want the model rather than the helpers.
172
+
173
+ ## Using assets
174
+
175
+ ```tsx
176
+ import { kosComponent } from '@kosdev-code/kos-ui-sdk';
177
+ import { wrapAsset } from '@kosdev-code/kos-asset-manager';
178
+
179
+ export const Header = kosComponent(() => {
180
+ const logo = wrapAsset('brand/logo-primary');
181
+
182
+ return (
183
+ <img src={logo.src} width={logo.width} height={logo.height} alt="Home" />
184
+ );
185
+ });
138
186
  ```
139
187
 
188
+ `wrapAsset(key)` returns a `ResolvedAsset`: `src`, `width`, `height`,
189
+ `poster`, `type`, `size`, `hash`, `tags`, `key`, `context` and `exists`.
190
+ Images are measured when the declaration is generated, so passing `width` and
191
+ `height` reserves the space and nothing reflows when the file arrives. An
192
+ unknown key logs a warning and comes back with `exists: false` and an empty
193
+ `src` rather than throwing, so a missing asset never takes a screen down.
194
+
195
+ Two things to get right. The helpers read MobX models, so a component that
196
+ reads them should be a `kosComponent` — otherwise it renders once with
197
+ whatever was loaded at the time and never updates. And resolve inside the
198
+ component rather than at module scope: a value computed when the module loads
199
+ runs before the provider has loaded anything.
200
+
201
+ `resolveAsset(key)` is the url on its own, for anywhere a url is expected:
202
+
203
+ ```ts
204
+ element.style.backgroundImage = `url(${resolveAsset('brand/logo-primary')})`;
205
+ ```
206
+
207
+ `hasAsset(key)` is true when a loaded application declares the key, for
208
+ branching on an optional asset without the warning `wrapAsset` logs.
209
+
210
+ A key resolves in the contexts named at the provider, first match winning,
211
+ unless it names one: `other.app.id:brand/logo-primary`.
212
+
213
+ ## Selecting sets
214
+
140
215
  Sets of assets are selected rather than named, so adding a file to a folder
141
216
  is all it takes to include it:
142
217
 
143
218
  ```ts
219
+ listAssets({ tag: 'beverage-logo' }).map((asset) => asset.src);
220
+
144
221
  await preloadAssets({ prefix: 'beverages/' }); // a folder
145
222
  await preloadAssets({ tag: 'beverage-logo' }); // a tag, across folders
146
223
  await preloadAssets({ type: 'video/' }); // by mime type
147
-
148
- listAssets({ tag: 'beverage-logo' }).map((asset) => asset.src);
149
224
  ```
150
225
 
151
- Selector fields combine, so `{ prefix: 'brand/', type: 'image/svg+xml' }` is
152
- the svg files in that folder.
226
+ An `AssetSelector` has `context`, `prefix`, `tag`, `type` and `keys`, and the
227
+ fields combine — `{ prefix: 'brand/', type: 'image/svg+xml' }` is the svg
228
+ files in that folder. `listAssets()` with no selector is everything loaded,
229
+ and `contextNames()` is the application ids that declared assets.
153
230
 
154
231
  ## Preloading during bootstrap
155
232
 
@@ -160,6 +237,15 @@ which is what makes images pop into view on lower powered hardware. Paying
160
237
  for it during a bootstrap sequence trades a couple of seconds at startup for
161
238
  screens that render immediately afterwards.
162
239
 
240
+ `preloadAssets` takes a group name, a key, an array of either, or a selector:
241
+
242
+ ```ts
243
+ await preloadAssets('boot'); // a group from the declaration
244
+ await preloadAssets('brand/logo-primary'); // one key
245
+ await preloadAssets(['boot', 'checkout']); // several
246
+ await preloadAssets({ prefix: 'beverages/' }); // a selector
247
+ ```
248
+
163
249
  ```ts
164
250
  await preloadAssets({ prefix: 'beverages/' }, {
165
251
  concurrency: 4,
@@ -167,23 +253,19 @@ await preloadAssets({ prefix: 'beverages/' }, {
167
253
  });
168
254
  ```
169
255
 
170
- Assets are readied a few at a time, because a wide fan out on a slow device
171
- only thrashes. A single asset that cannot be readied is reported through
172
- `onProgress` with `ok: false` and never fails the sequence, so one bad file
173
- cannot stop a device from starting.
174
-
175
- Images are measured when the declaration is generated, so the space they
176
- take can be reserved and nothing reflows when they arrive:
256
+ Assets are readied a few at a time (`concurrency`, 4 by default), because a
257
+ wide fan out on a slow device only thrashes. A single asset that cannot be
258
+ readied is reported through `onProgress` with `ok: false` and never fails the
259
+ sequence, so one bad file cannot stop a device from starting.
177
260
 
178
- ```tsx
179
- const logo = wrapAsset('brand/logo-primary');
180
- <img src={logo.src} width={logo.width} height={logo.height} alt="" />
181
- ```
261
+ `preloadDeclared(options?)` readies every loaded asset whose declaration set
262
+ `preload: true`, so the application doesn't have to know which those are.
263
+ `releaseAssets()` drops the decoded images preloading is holding — worth
264
+ calling when leaving a section whose assets are large and won't be shown
265
+ again.
182
266
 
183
- `{ hold: true }` additionally keeps the bytes as an object url, which is
184
- worth it only when an asset must survive the content going away.
185
- `releaseAssets()` drops everything preloading is holding.
267
+ ## Where the files come from
186
268
 
187
- Only the contexts named in `initAssets` are requested. Keys resolve in the
188
- default context (the first one declared) unless they name another, as in
189
- `other.app.id:brand/logo-primary`.
269
+ Urls are built against the `host` query parameter when there is one, and the
270
+ page's own origin otherwise, so a ui run on a laptop against a device
271
+ resolves assets by opening it with `?host=http://device-address`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kosdev-code/kos-asset-manager",
3
- "version": "0.0.1-next.27",
3
+ "version": "0.0.1-next.29",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "types": "./index.d.ts",
@@ -25,7 +25,7 @@
25
25
  },
26
26
  "kos": {
27
27
  "build": {
28
- "gitHash": "e99638c2d2d0816216d4ed2d7c167703fc9b687d"
28
+ "gitHash": "1b2fc996563150e3da055a9506a28b7cb132c060"
29
29
  }
30
30
  },
31
31
  "publishConfig": {