qstd 0.3.58 → 0.3.59

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/ATOMIC_MODULES.md CHANGED
@@ -451,6 +451,43 @@ type DurationMs = number;
451
451
  type TokenId = string;
452
452
  ```
453
453
 
454
+ ### Module-aware type naming
455
+
456
+ **Since we use namespace imports, avoid redundant prefixes.** The module name already provides context:
457
+
458
+ ```typescript
459
+ // ❌ AVOID: Redundant module prefix in type name
460
+ // contracts/media/types.ts
461
+ export interface Media { ... } // Used as Media.Media
462
+ export type MediaType = "song" | "video"; // Used as Media.MediaType
463
+
464
+ // ✅ PREFER: Short names, let module provide context
465
+ // contracts/media/types.ts
466
+ export interface t { ... } // Used as Media.t
467
+ export type Type = "song" | "video"; // Used as Media.Type
468
+ export interface Version { ... } // Used as Media.Version
469
+ ```
470
+
471
+ **The `t` convention:** For the "main" type of a module (the one it's named after), use lowercase `t`. This pattern comes from ML/OCaml and reads naturally with namespace imports:
472
+
473
+ ```typescript
474
+ // Usage is clean and non-redundant
475
+ const song: Media.t = { ... };
476
+ const item: Queue.t = { ... };
477
+
478
+ // Compare to awkward repetition
479
+ const song: Media.Media = { ... }; // ❌
480
+ const item: Queue.QueueItem = { ... }; // ❌
481
+ ```
482
+
483
+ **Guidelines:**
484
+ - `Module.t` — The main/primary type (the entity the module represents)
485
+ - `Module.Type` — A discriminated union of subtypes (e.g., `"song" | "video"`)
486
+ - `Module.Config`, `Module.Options` — Configuration/options for the module
487
+ - `Module.Event`, `Module.Version` — Related concepts without redundant prefix
488
+
489
+ **When to keep prefixes:** If a type is re-exported and used without the module namespace, a prefix may still be appropriate to avoid ambiguity at the consumption site.
490
+
454
491
  ### Document type fields with JSDoc
455
492
 
456
493
  **Add JSDoc to fields when the name is generic but the value has specific meaning.** Especially `id` fields with particular formats, foreign key references, or `string`/`number` fields with constraints.
@@ -899,30 +936,52 @@ import type { ContentBlock } from "contracts/entry";
899
936
 
900
937
  ### Import path setup
901
938
 
902
- Configure path aliases in each package's tsconfig.json:
939
+ **Option 1: Workspace dependency (recommended)**
940
+
941
+ Add contracts as a workspace dependency in each consuming package:
903
942
 
904
943
  ```json
905
- // packages/server/tsconfig.json
944
+ // packages/client/package.json
906
945
  {
907
- "compilerOptions": {
908
- "paths": {
909
- "contracts/*": ["../contracts/src/*"],
910
- "entities/*": ["src/entities/*"]
911
- }
946
+ "dependencies": {
947
+ "contracts": "workspace:*"
948
+ }
949
+ }
950
+ ```
951
+
952
+ Configure the contracts package exports to expose submodules:
953
+
954
+ ```json
955
+ // packages/contracts/package.json
956
+ {
957
+ "name": "contracts",
958
+ "exports": {
959
+ ".": "./src/index.ts",
960
+ "./*": "./src/*/index.ts"
912
961
  }
913
962
  }
963
+ ```
964
+
965
+ This uses standard package resolution. No tsconfig paths needed for contracts.
966
+
967
+ **Option 2: Path aliases**
968
+
969
+ If you prefer path aliases, configure them in tsconfig.json:
914
970
 
915
- // packages/client/tsconfig.json
971
+ ```json
972
+ // packages/server/tsconfig.json
916
973
  {
917
974
  "compilerOptions": {
918
975
  "paths": {
919
- "contracts/*": ["../contracts/src/*"],
920
- "entities/*": ["./src/entities/*"]
976
+ "contracts/*": ["../contracts/src/*/index.ts", "../contracts/src/*"],
977
+ "entities/*": ["src/entities/*"]
921
978
  }
922
979
  }
923
980
  }
924
981
  ```
925
982
 
983
+ Note: The path pattern needs both `*/index.ts` fallback for directories and `*` for direct files.
984
+
926
985
  ### When to create a contract
927
986
 
928
987
  1. **Same type in both environments** - If you're copying a type from server to client (or vice versa), it belongs in contracts.
@@ -1 +1 @@
1
- {"version":3,"file":"drawer.d.ts","sourceRoot":"","sources":["../../src/block/drawer.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAmX9B,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,KAAK,EAAE,EAAE,CAAC,gBAAgB,2CAMxD;AAID,wBAAgB,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,mBAAmB,2CAYrD;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,mBAAmB,uDAkDrD"}
1
+ {"version":3,"file":"drawer.d.ts","sourceRoot":"","sources":["../../src/block/drawer.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAsZ9B,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,KAAK,EAAE,EAAE,CAAC,gBAAgB,2CAMxD;AAID,wBAAgB,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,mBAAmB,2CAYrD;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,mBAAmB,uDAkDrD"}
@@ -2622,6 +2622,34 @@ var accordion_default = Accordion;
2622
2622
  var MotionDiv4 = motionTags.div;
2623
2623
  var MotionBtn2 = motionTags.button;
2624
2624
  var breakpoint = ["(min-width: 600px)"];
2625
+ function requirePortalRootForDrawer() {
2626
+ if (typeof document === "undefined") {
2627
+ throw new Error(
2628
+ [
2629
+ `[qstd] Block is="drawer" requires a DOM (document is undefined).`,
2630
+ `If you're server-rendering, render drawers only on the client.`
2631
+ ].join("\n")
2632
+ );
2633
+ }
2634
+ const portal = document.getElementById("portal");
2635
+ if (!portal) {
2636
+ throw new Error(
2637
+ [
2638
+ `[qstd] You cannot use <Block is="drawer" /> unless your HTML contains a portal root: <div id="portal"></div>.`,
2639
+ ``,
2640
+ `No element with id="portal" was found in the document.`,
2641
+ ``,
2642
+ `Fix: add this next to your app root in your HTML (usually right under #root):`,
2643
+ ``,
2644
+ ` <div id="root"></div>`,
2645
+ ` <div id="portal"></div>`,
2646
+ ``,
2647
+ `Without #portal, qstd cannot render drawers (they use React portals).`
2648
+ ].join("\n")
2649
+ );
2650
+ }
2651
+ return portal;
2652
+ }
2625
2653
  function DrawerComponent(props) {
2626
2654
  const ref = React3__namespace.default.useRef(null);
2627
2655
  const dragHandleRef = React3__namespace.default.useRef(null);
@@ -2717,6 +2745,7 @@ function DrawerComponent(props) {
2717
2745
  closeFnRef.current?.();
2718
2746
  };
2719
2747
  if (!mounted) return null;
2748
+ const portalRoot = requirePortalRootForDrawer();
2720
2749
  return reactDom.createPortal(
2721
2750
  /* @__PURE__ */ jsxRuntime.jsx(
2722
2751
  framerMotion.AnimatePresence,
@@ -2863,7 +2892,7 @@ function DrawerComponent(props) {
2863
2892
  )
2864
2893
  }
2865
2894
  ),
2866
- document.getElementById("portal")
2895
+ portalRoot
2867
2896
  );
2868
2897
  }
2869
2898
  var DrawerContext = React3__namespace.default.createContext({
@@ -2599,6 +2599,34 @@ var accordion_default = Accordion;
2599
2599
  var MotionDiv4 = motionTags.div;
2600
2600
  var MotionBtn2 = motionTags.button;
2601
2601
  var breakpoint = ["(min-width: 600px)"];
2602
+ function requirePortalRootForDrawer() {
2603
+ if (typeof document === "undefined") {
2604
+ throw new Error(
2605
+ [
2606
+ `[qstd] Block is="drawer" requires a DOM (document is undefined).`,
2607
+ `If you're server-rendering, render drawers only on the client.`
2608
+ ].join("\n")
2609
+ );
2610
+ }
2611
+ const portal = document.getElementById("portal");
2612
+ if (!portal) {
2613
+ throw new Error(
2614
+ [
2615
+ `[qstd] You cannot use <Block is="drawer" /> unless your HTML contains a portal root: <div id="portal"></div>.`,
2616
+ ``,
2617
+ `No element with id="portal" was found in the document.`,
2618
+ ``,
2619
+ `Fix: add this next to your app root in your HTML (usually right under #root):`,
2620
+ ``,
2621
+ ` <div id="root"></div>`,
2622
+ ` <div id="portal"></div>`,
2623
+ ``,
2624
+ `Without #portal, qstd cannot render drawers (they use React portals).`
2625
+ ].join("\n")
2626
+ );
2627
+ }
2628
+ return portal;
2629
+ }
2602
2630
  function DrawerComponent(props) {
2603
2631
  const ref = React3__default.useRef(null);
2604
2632
  const dragHandleRef = React3__default.useRef(null);
@@ -2694,6 +2722,7 @@ function DrawerComponent(props) {
2694
2722
  closeFnRef.current?.();
2695
2723
  };
2696
2724
  if (!mounted) return null;
2725
+ const portalRoot = requirePortalRootForDrawer();
2697
2726
  return createPortal(
2698
2727
  /* @__PURE__ */ jsx(
2699
2728
  AnimatePresence,
@@ -2840,7 +2869,7 @@ function DrawerComponent(props) {
2840
2869
  )
2841
2870
  }
2842
2871
  ),
2843
- document.getElementById("portal")
2872
+ portalRoot
2844
2873
  );
2845
2874
  }
2846
2875
  var DrawerContext = React3__default.createContext({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qstd",
3
- "version": "0.3.58",
3
+ "version": "0.3.59",
4
4
  "description": "Standard Block component and utilities library with Panda CSS",
5
5
  "author": "malin1",
6
6
  "license": "MIT",