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 +69 -10
- package/dist/block/drawer.d.ts.map +1 -1
- package/dist/react/index.cjs +30 -1
- package/dist/react/index.js +30 -1
- package/package.json +1 -1
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
|
-
|
|
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/
|
|
944
|
+
// packages/client/package.json
|
|
906
945
|
{
|
|
907
|
-
"
|
|
908
|
-
"
|
|
909
|
-
|
|
910
|
-
|
|
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
|
-
|
|
971
|
+
```json
|
|
972
|
+
// packages/server/tsconfig.json
|
|
916
973
|
{
|
|
917
974
|
"compilerOptions": {
|
|
918
975
|
"paths": {
|
|
919
|
-
"contracts/*": ["../contracts/src/*"],
|
|
920
|
-
"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;
|
|
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"}
|
package/dist/react/index.cjs
CHANGED
|
@@ -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
|
-
|
|
2895
|
+
portalRoot
|
|
2867
2896
|
);
|
|
2868
2897
|
}
|
|
2869
2898
|
var DrawerContext = React3__namespace.default.createContext({
|
package/dist/react/index.js
CHANGED
|
@@ -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
|
-
|
|
2872
|
+
portalRoot
|
|
2844
2873
|
);
|
|
2845
2874
|
}
|
|
2846
2875
|
var DrawerContext = React3__default.createContext({
|