@manyducks.co/dolla 0.69.4 → 0.70.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/lib/app.d.ts CHANGED
@@ -49,13 +49,18 @@ export interface StoreRegistration<O = any> {
49
49
  instance?: ReturnType<typeof initStore>;
50
50
  }
51
51
  interface ConfigureContext {
52
+ /**
53
+ * Returns the shared instance of `store`.
54
+ */
55
+ getStore<T extends Store<any, any>>(store: T): ReturnType<T>;
56
+ /**
57
+ * Returns the shared instance of a built-in store.
58
+ */
59
+ getStore<N extends keyof BuiltInStores>(name: N): BuiltInStores[N];
52
60
  }
53
61
  type ConfigureCallback = (ctx: ConfigureContext) => void | Promise<void>;
54
62
  export interface IApp {
55
63
  readonly isConnected: boolean;
56
- /**
57
- * Makes this store accessible from any other component in the app, except for stores registered before this one.
58
- */
59
64
  /**
60
65
  * Runs `callback` after app-level stores are connected to the app, but before views are connected to the DOM.
61
66
  * Use this function to run async configuration code before displaying content to the user.
package/lib/index.js CHANGED
@@ -2195,10 +2195,65 @@ function RenderStore(ctx) {
2195
2195
  };
2196
2196
  }
2197
2197
 
2198
- // src/app.ts
2199
- function DefaultRootView(_, ctx) {
2198
+ // src/views/default-crash-page.ts
2199
+ function DefaultCrashPage({ message, error, componentName }) {
2200
+ return m(
2201
+ "div",
2202
+ {
2203
+ style: {
2204
+ backgroundColor: "#880000",
2205
+ color: "#fff",
2206
+ padding: "2rem",
2207
+ position: "fixed",
2208
+ inset: 0,
2209
+ fontSize: "20px"
2210
+ }
2211
+ },
2212
+ m("h1", { style: { marginBottom: "0.5rem" } }, "The app has crashed"),
2213
+ m(
2214
+ "p",
2215
+ { style: { marginBottom: "0.25rem" } },
2216
+ m("span", { style: { fontFamily: "monospace" } }, componentName),
2217
+ " says:"
2218
+ ),
2219
+ m(
2220
+ "blockquote",
2221
+ {
2222
+ style: {
2223
+ backgroundColor: "#991111",
2224
+ padding: "0.25em",
2225
+ borderRadius: "6px",
2226
+ fontFamily: "monospace",
2227
+ marginBottom: "1rem"
2228
+ }
2229
+ },
2230
+ m(
2231
+ "span",
2232
+ {
2233
+ style: {
2234
+ display: "inline-block",
2235
+ backgroundColor: "red",
2236
+ padding: "0.1em 0.4em",
2237
+ marginRight: "0.5em",
2238
+ borderRadius: "4px",
2239
+ fontSize: "0.9em",
2240
+ fontWeight: "bold"
2241
+ }
2242
+ },
2243
+ error.name
2244
+ ),
2245
+ message
2246
+ ),
2247
+ m("p", {}, "Please see the browser console for details.")
2248
+ );
2249
+ }
2250
+
2251
+ // src/views/default-view.ts
2252
+ function DefaultView(_, ctx) {
2200
2253
  return ctx.outlet();
2201
2254
  }
2255
+
2256
+ // src/app.ts
2202
2257
  function isAppOptions(value) {
2203
2258
  return isObject(value);
2204
2259
  }
@@ -2207,7 +2262,7 @@ function App(options) {
2207
2262
  throw new TypeError(`App options must be an object. Got: ${options}`);
2208
2263
  }
2209
2264
  let isConnected = false;
2210
- let mainView = m(options?.view ?? DefaultRootView);
2265
+ let mainView = m(options?.view ?? DefaultView);
2211
2266
  let configureCallback;
2212
2267
  const settings = merge(
2213
2268
  {
@@ -2290,14 +2345,42 @@ function App(options) {
2290
2345
  if (configureCallback) {
2291
2346
  await configureCallback({
2292
2347
  // TODO: Add context methods
2348
+ getStore(store) {
2349
+ let name;
2350
+ if (typeof store === "string") {
2351
+ name = store;
2352
+ } else {
2353
+ name = store.name;
2354
+ }
2355
+ if (typeof store !== "string") {
2356
+ let ec = elementContext;
2357
+ while (ec) {
2358
+ if (ec.stores.has(store)) {
2359
+ return ec.stores.get(store)?.instance.exports;
2360
+ }
2361
+ ec = ec.parent;
2362
+ }
2363
+ }
2364
+ if (appContext.stores.has(store)) {
2365
+ const _store = appContext.stores.get(store);
2366
+ if (!_store.instance) {
2367
+ appContext.crashCollector.crash({
2368
+ componentName: "@manyducks.co/dolla/App",
2369
+ error: new Error(`Store '${name}' is not registered on this app.`)
2370
+ });
2371
+ }
2372
+ return _store.instance.exports;
2373
+ }
2374
+ appContext.crashCollector.crash({
2375
+ componentName: "@manyducks.co/dolla/App",
2376
+ error: new Error(`Store '${name}' is not registered on this app.`)
2377
+ });
2378
+ }
2293
2379
  });
2294
2380
  }
2295
- const done = () => {
2296
- appContext.rootView.connect(appContext.rootElement);
2297
- isConnected = true;
2298
- resolve2();
2299
- };
2300
- done();
2381
+ appContext.rootView.connect(appContext.rootElement);
2382
+ isConnected = true;
2383
+ resolve2();
2301
2384
  });
2302
2385
  }
2303
2386
  async function disconnect() {
@@ -2324,54 +2407,6 @@ function App(options) {
2324
2407
  get isConnected() {
2325
2408
  return isConnected;
2326
2409
  },
2327
- // language(tag: string, config: LanguageConfig) {
2328
- // languages.set(tag, config);
2329
- //
2330
- // return app;
2331
- // },
2332
- //
2333
- // setLanguage(tag: string, fallback?: string) {
2334
- // if (tag === "auto") {
2335
- // let tags = [];
2336
- //
2337
- // if (typeof navigator === "object") {
2338
- // const nav = navigator as any;
2339
- //
2340
- // if (nav.languages?.length > 0) {
2341
- // tags.push(...nav.languages);
2342
- // } else if (nav.language) {
2343
- // tags.push(nav.language);
2344
- // } else if (nav.browserLanguage) {
2345
- // tags.push(nav.browserLanguage);
2346
- // } else if (nav.userLanguage) {
2347
- // tags.push(nav.userLanguage);
2348
- // }
2349
- // }
2350
- //
2351
- // for (const tag of tags) {
2352
- // if (languages.has(tag)) {
2353
- // // Found a matching language.
2354
- // currentLanguage = tag;
2355
- // return this;
2356
- // }
2357
- // }
2358
- //
2359
- // if (!currentLanguage && fallback) {
2360
- // if (languages.has(fallback)) {
2361
- // currentLanguage = fallback;
2362
- // }
2363
- // }
2364
- // } else {
2365
- // // Tag is the actual tag to set.
2366
- // if (languages.has(tag)) {
2367
- // currentLanguage = tag;
2368
- // } else {
2369
- // throw new Error(`Language '${tag}' has not been added to this app yet.`);
2370
- // }
2371
- // }
2372
- //
2373
- // return app;
2374
- // },
2375
2410
  configure(callback) {
2376
2411
  if (configureCallback !== void 0) {
2377
2412
  debugChannel.warn(`Configure callback is already defined. Only the final configure call will take effect.`);
@@ -2382,57 +2417,6 @@ function App(options) {
2382
2417
  };
2383
2418
  return app;
2384
2419
  }
2385
- function DefaultCrashPage({ message, error, componentName }) {
2386
- return m(
2387
- "div",
2388
- {
2389
- style: {
2390
- backgroundColor: "#880000",
2391
- color: "#fff",
2392
- padding: "2rem",
2393
- position: "fixed",
2394
- inset: 0,
2395
- fontSize: "20px"
2396
- }
2397
- },
2398
- m("h1", { style: { marginBottom: "0.5rem" } }, "The app has crashed"),
2399
- m(
2400
- "p",
2401
- { style: { marginBottom: "0.25rem" } },
2402
- m("span", { style: { fontFamily: "monospace" } }, componentName),
2403
- " says:"
2404
- ),
2405
- m(
2406
- "blockquote",
2407
- {
2408
- style: {
2409
- backgroundColor: "#991111",
2410
- padding: "0.25em",
2411
- borderRadius: "6px",
2412
- fontFamily: "monospace",
2413
- marginBottom: "1rem"
2414
- }
2415
- },
2416
- m(
2417
- "span",
2418
- {
2419
- style: {
2420
- display: "inline-block",
2421
- backgroundColor: "red",
2422
- padding: "0.1em 0.4em",
2423
- marginRight: "0.5em",
2424
- borderRadius: "4px",
2425
- fontSize: "0.9em",
2426
- fontWeight: "bold"
2427
- }
2428
- },
2429
- error.name
2430
- ),
2431
- message
2432
- ),
2433
- m("p", {}, "Please see the browser console for details.")
2434
- );
2435
- }
2436
2420
 
2437
2421
  // src/views/fragment.ts
2438
2422
  function Fragment(_, ctx) {
@@ -3122,9 +3106,7 @@ function patternToFragments(pattern) {
3122
3106
  const part = parts[i];
3123
3107
  if (part === "*") {
3124
3108
  if (i !== parts.length - 1) {
3125
- throw new Error(
3126
- `Wildcard must be at the end of a pattern. Received: ${pattern}`
3127
- );
3109
+ throw new Error(`Wildcard must be at the end of a pattern. Received: ${pattern}`);
3128
3110
  }
3129
3111
  fragments.push({
3130
3112
  type: 3 /* Wildcard */,
@@ -3149,7 +3131,6 @@ function patternToFragments(pattern) {
3149
3131
  }
3150
3132
 
3151
3133
  // src/stores/router.ts
3152
- var DefaultView = (_, ctx) => ctx.outlet();
3153
3134
  function RouterStore(ctx) {
3154
3135
  ctx.name = "dolla/router";
3155
3136
  const { appContext, elementContext } = getStoreSecrets(ctx);
@@ -3488,37 +3469,71 @@ function LanguageStore(ctx) {
3488
3469
  }
3489
3470
  return template;
3490
3471
  }
3491
- const currentLanguage = ctx.options.default ? languages.get(ctx.options.default) : languages.get([...languages.keys()][0]);
3492
- if (currentLanguage == null) {
3493
- $$isLoaded.set(true);
3494
- } else {
3495
- ctx.info(`Current language is '${currentLanguage.name}'.`);
3496
- getTranslation(currentLanguage).then((translation) => {
3497
- $$language.set(currentLanguage.name);
3472
+ async function setLanguage(tag) {
3473
+ let realTag;
3474
+ if (tag === "auto") {
3475
+ let tags = [];
3476
+ if (typeof navigator === "object") {
3477
+ const nav = navigator;
3478
+ if (nav.languages?.length > 0) {
3479
+ tags.push(...nav.languages);
3480
+ } else if (nav.language) {
3481
+ tags.push(nav.language);
3482
+ } else if (nav.browserLanguage) {
3483
+ tags.push(nav.browserLanguage);
3484
+ } else if (nav.userLanguage) {
3485
+ tags.push(nav.userLanguage);
3486
+ }
3487
+ }
3488
+ for (const tag2 of tags) {
3489
+ if (languages.has(tag2)) {
3490
+ realTag = tag2;
3491
+ }
3492
+ }
3493
+ } else {
3494
+ if (languages.has(tag)) {
3495
+ realTag = tag;
3496
+ }
3497
+ }
3498
+ if (realTag == null) {
3499
+ const firstLanguage = ctx.options.languages[0];
3500
+ if (firstLanguage) {
3501
+ realTag = firstLanguage.name;
3502
+ }
3503
+ }
3504
+ if (!realTag || !languages.has(realTag)) {
3505
+ throw new Error(`Language '${tag}' is not configured for this app.`);
3506
+ }
3507
+ const lang = languages.get(realTag);
3508
+ try {
3509
+ const translation = await getTranslation(lang);
3498
3510
  $$translation.set(translation);
3499
- $$isLoaded.set(true);
3500
- });
3511
+ $$language.set(realTag);
3512
+ ctx.info("set language to " + realTag);
3513
+ } catch (error) {
3514
+ if (error instanceof Error) {
3515
+ ctx.crash(error);
3516
+ }
3517
+ }
3501
3518
  }
3519
+ setLanguage(ctx.options.defaultLanguage ?? "auto").then(() => {
3520
+ $$isLoaded.set(true);
3521
+ });
3502
3522
  return {
3523
+ loaded: new Promise((resolve2, reject) => {
3524
+ const stop = observe($$isLoaded, (isLoaded) => {
3525
+ if (isLoaded) {
3526
+ setTimeout(() => {
3527
+ stop();
3528
+ resolve2();
3529
+ }, 0);
3530
+ }
3531
+ });
3532
+ }),
3503
3533
  $isLoaded: $($$isLoaded),
3504
3534
  $currentLanguage: $($$language),
3505
3535
  supportedLanguages: [...languages.keys()],
3506
- async setLanguage(tag) {
3507
- if (!languages.has(tag)) {
3508
- throw new Error(`Language '${tag}' is not supported.`);
3509
- }
3510
- const lang = languages.get(tag);
3511
- try {
3512
- const translation = await getTranslation(lang);
3513
- $$translation.set(translation);
3514
- $$language.set(tag);
3515
- ctx.info("set language to " + tag);
3516
- } catch (error) {
3517
- if (error instanceof Error) {
3518
- ctx.crash(error);
3519
- }
3520
- }
3521
- },
3536
+ setLanguage,
3522
3537
  /**
3523
3538
  * Returns a Readable of the translated value.
3524
3539