fibrae 0.3.0 → 0.3.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/dist/atom-utils.d.ts +52 -0
- package/dist/atom-utils.js +64 -0
- package/dist/atom-utils.js.map +1 -0
- package/dist/cli/build.js +2 -2
- package/dist/cli/build.js.map +1 -1
- package/dist/cli/html.d.ts +6 -0
- package/dist/cli/html.js +20 -26
- package/dist/cli/html.js.map +1 -1
- package/dist/cli/vite-plugin.js +41 -2
- package/dist/cli/vite-plugin.js.map +1 -1
- package/dist/components.d.ts +3 -3
- package/dist/components.js +3 -1
- package/dist/components.js.map +1 -1
- package/dist/core.js +37 -10
- package/dist/core.js.map +1 -1
- package/dist/dom.d.ts +22 -3
- package/dist/dom.js +100 -5
- package/dist/dom.js.map +1 -1
- package/dist/fiber-commit.d.ts +2 -0
- package/dist/fiber-commit.js +179 -40
- package/dist/fiber-commit.js.map +1 -1
- package/dist/fiber-render.js +13 -27
- package/dist/fiber-render.js.map +1 -1
- package/dist/fiber-update.d.ts +1 -1
- package/dist/fiber-update.js +33 -12
- package/dist/fiber-update.js.map +1 -1
- package/dist/h.d.ts +65 -10
- package/dist/h.js +98 -22
- package/dist/h.js.map +1 -1
- package/dist/head.d.ts +64 -0
- package/dist/head.js +257 -0
- package/dist/head.js.map +1 -0
- package/dist/hydration-dev.d.ts +24 -0
- package/dist/hydration-dev.js +138 -0
- package/dist/hydration-dev.js.map +1 -0
- package/dist/index.d.ts +6 -2
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/jsx-runtime/index.d.ts +288 -8
- package/dist/jsx-runtime/index.js +2 -0
- package/dist/jsx-runtime/index.js.map +1 -1
- package/dist/live/client.js +4 -4
- package/dist/live/client.js.map +1 -1
- package/dist/live/server.js.map +1 -1
- package/dist/live/sse-stream.js +2 -2
- package/dist/live/sse-stream.js.map +1 -1
- package/dist/mdx/index.d.ts +125 -0
- package/dist/mdx/index.js +137 -0
- package/dist/mdx/index.js.map +1 -0
- package/dist/mdx/parse.d.ts +42 -0
- package/dist/mdx/parse.js +147 -0
- package/dist/mdx/parse.js.map +1 -0
- package/dist/mdx/render.d.ts +24 -0
- package/dist/mdx/render.js +261 -0
- package/dist/mdx/render.js.map +1 -0
- package/dist/router/Form.d.ts +90 -0
- package/dist/router/Form.js +166 -0
- package/dist/router/Form.js.map +1 -0
- package/dist/router/History.d.ts +2 -2
- package/dist/router/History.js.map +1 -1
- package/dist/router/Link.d.ts +27 -32
- package/dist/router/Link.js +56 -98
- package/dist/router/Link.js.map +1 -1
- package/dist/router/Navigator.d.ts +49 -44
- package/dist/router/Navigator.js +57 -104
- package/dist/router/Navigator.js.map +1 -1
- package/dist/router/Route.d.ts +8 -4
- package/dist/router/Route.js +10 -2
- package/dist/router/Route.js.map +1 -1
- package/dist/router/Router.d.ts +10 -10
- package/dist/router/Router.js +38 -23
- package/dist/router/Router.js.map +1 -1
- package/dist/router/RouterBuilder.d.ts +69 -6
- package/dist/router/RouterBuilder.js +23 -0
- package/dist/router/RouterBuilder.js.map +1 -1
- package/dist/router/RouterOutlet.js +63 -45
- package/dist/router/RouterOutlet.js.map +1 -1
- package/dist/router/index.d.ts +10 -3
- package/dist/router/index.js +5 -2
- package/dist/router/index.js.map +1 -1
- package/dist/router/register.d.ts +37 -0
- package/dist/router/register.js +18 -0
- package/dist/router/register.js.map +1 -0
- package/dist/router/utils.d.ts +2 -2
- package/dist/runtime.d.ts +1 -1
- package/dist/server.js +7 -3
- package/dist/server.js.map +1 -1
- package/dist/shared.d.ts +36 -6
- package/dist/shared.js +12 -2
- package/dist/shared.js.map +1 -1
- package/dist/tracking.d.ts +3 -2
- package/dist/tracking.js +4 -1
- package/dist/tracking.js.map +1 -1
- package/dist/transition.d.ts +51 -0
- package/dist/transition.js +46 -0
- package/dist/transition.js.map +1 -0
- package/package.json +16 -3
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Form component — declarative form submission with schema-decoded payloads.
|
|
3
|
+
*
|
|
4
|
+
* React Router-inspired: connects to the route's action by default,
|
|
5
|
+
* or accepts an explicit action prop for fetcher-style usage.
|
|
6
|
+
*
|
|
7
|
+
* Submission lifecycle:
|
|
8
|
+
* 1. Serialize FormData → plain record
|
|
9
|
+
* 2. Schema.decodeUnknown(PayloadSchema) → typed payload
|
|
10
|
+
* 3. Decode failure → validation error (no action call)
|
|
11
|
+
* 4. Decode success → invoke action Effect
|
|
12
|
+
* 5. State transitions: idle → pending → success/failure
|
|
13
|
+
* 6. If navigate !== false: navigate after success
|
|
14
|
+
*
|
|
15
|
+
* Design: builds VElement directly (no JSX) — consistent with Link.ts pattern.
|
|
16
|
+
*/
|
|
17
|
+
import * as Effect from "effect/Effect";
|
|
18
|
+
import * as Schema from "effect/Schema";
|
|
19
|
+
import * as Context from "effect/Context";
|
|
20
|
+
import { Atom, Registry as AtomRegistry } from "@effect-atom/atom";
|
|
21
|
+
import { Navigator } from "./Navigator.js";
|
|
22
|
+
import { RouterHandlers, type RouteAction, type SubmissionState } from "./RouterBuilder.js";
|
|
23
|
+
import type { VElement, VChild } from "../shared.js";
|
|
24
|
+
declare const FormValidationError_base: Schema.TaggedErrorClass<FormValidationError, "FormValidationError", {
|
|
25
|
+
readonly _tag: Schema.tag<"FormValidationError">;
|
|
26
|
+
} & {
|
|
27
|
+
message: typeof Schema.String;
|
|
28
|
+
cause: Schema.optional<typeof Schema.Unknown>;
|
|
29
|
+
}>;
|
|
30
|
+
/**
|
|
31
|
+
* Validation error from schema decode failure on form data.
|
|
32
|
+
*/
|
|
33
|
+
export declare class FormValidationError extends FormValidationError_base {
|
|
34
|
+
}
|
|
35
|
+
declare const FormState_base: Context.TagClass<FormState, "fibrae/FormState", {
|
|
36
|
+
readonly state: Atom.Writable<SubmissionState, SubmissionState>;
|
|
37
|
+
}>;
|
|
38
|
+
/**
|
|
39
|
+
* Service tag for the current form's submission state.
|
|
40
|
+
* Components inside a Form can read this to show loading/error states.
|
|
41
|
+
*/
|
|
42
|
+
export declare class FormState extends FormState_base {
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Props for the Form component.
|
|
46
|
+
*/
|
|
47
|
+
export interface FormProps {
|
|
48
|
+
/** Explicit action — overrides the route's action. Accepts decoded payload, returns Effect. */
|
|
49
|
+
readonly action?: RouteAction;
|
|
50
|
+
/** Schema to decode FormData. Required when using explicit action. */
|
|
51
|
+
readonly schema?: Schema.Schema.Any;
|
|
52
|
+
/** HTTP method attribute (default: "post"). */
|
|
53
|
+
readonly method?: string;
|
|
54
|
+
/** When false, skip navigation after success (fetcher-style). Default: true. */
|
|
55
|
+
readonly navigate?: boolean;
|
|
56
|
+
/** Route name to navigate to after success. If unset, stays on current route. */
|
|
57
|
+
readonly navigateTo?: string;
|
|
58
|
+
/** Callback when submission succeeds. */
|
|
59
|
+
readonly onSuccess?: (data: unknown) => void;
|
|
60
|
+
/** Callback when submission fails. */
|
|
61
|
+
readonly onError?: (error: unknown) => void;
|
|
62
|
+
/** Additional CSS class names. */
|
|
63
|
+
readonly class?: string;
|
|
64
|
+
/** Children (form fields, buttons, etc.). Already normalized by JSX runtime. */
|
|
65
|
+
readonly children?: VChild;
|
|
66
|
+
/** Data attributes for testing. */
|
|
67
|
+
readonly "data-cy"?: string;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Create a Form component for the current route context.
|
|
71
|
+
*
|
|
72
|
+
* Wires to the route's action by default. Accepts explicit action for fetcher-style.
|
|
73
|
+
*
|
|
74
|
+
* Usage:
|
|
75
|
+
* ```typescript
|
|
76
|
+
* // Route action form — uses route's action config
|
|
77
|
+
* <Form>
|
|
78
|
+
* <input name="title" />
|
|
79
|
+
* <button type="submit">Save</button>
|
|
80
|
+
* </Form>
|
|
81
|
+
*
|
|
82
|
+
* // Explicit action (fetcher-style)
|
|
83
|
+
* <Form action={myAction} navigate={false}>
|
|
84
|
+
* <input name="query" />
|
|
85
|
+
* <button type="submit">Search</button>
|
|
86
|
+
* </Form>
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
export declare function Form(props: FormProps): Effect.Effect<VElement, never, Navigator | RouterHandlers | AtomRegistry.AtomRegistry>;
|
|
90
|
+
export {};
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Form component — declarative form submission with schema-decoded payloads.
|
|
3
|
+
*
|
|
4
|
+
* React Router-inspired: connects to the route's action by default,
|
|
5
|
+
* or accepts an explicit action prop for fetcher-style usage.
|
|
6
|
+
*
|
|
7
|
+
* Submission lifecycle:
|
|
8
|
+
* 1. Serialize FormData → plain record
|
|
9
|
+
* 2. Schema.decodeUnknown(PayloadSchema) → typed payload
|
|
10
|
+
* 3. Decode failure → validation error (no action call)
|
|
11
|
+
* 4. Decode success → invoke action Effect
|
|
12
|
+
* 5. State transitions: idle → pending → success/failure
|
|
13
|
+
* 6. If navigate !== false: navigate after success
|
|
14
|
+
*
|
|
15
|
+
* Design: builds VElement directly (no JSX) — consistent with Link.ts pattern.
|
|
16
|
+
*/
|
|
17
|
+
import * as Effect from "effect/Effect";
|
|
18
|
+
import * as Schema from "effect/Schema";
|
|
19
|
+
import * as Option from "effect/Option";
|
|
20
|
+
import * as Context from "effect/Context";
|
|
21
|
+
import { Atom, Registry as AtomRegistry } from "@effect-atom/atom";
|
|
22
|
+
import { Navigator } from "./Navigator.js";
|
|
23
|
+
import { RouterHandlers } from "./RouterBuilder.js";
|
|
24
|
+
// =============================================================================
|
|
25
|
+
// Errors
|
|
26
|
+
// =============================================================================
|
|
27
|
+
/**
|
|
28
|
+
* Validation error from schema decode failure on form data.
|
|
29
|
+
*/
|
|
30
|
+
export class FormValidationError extends Schema.TaggedError()("FormValidationError", {
|
|
31
|
+
message: Schema.String,
|
|
32
|
+
cause: Schema.optional(Schema.Unknown),
|
|
33
|
+
}) {
|
|
34
|
+
}
|
|
35
|
+
// =============================================================================
|
|
36
|
+
// Types
|
|
37
|
+
// =============================================================================
|
|
38
|
+
/**
|
|
39
|
+
* Service tag for the current form's submission state.
|
|
40
|
+
* Components inside a Form can read this to show loading/error states.
|
|
41
|
+
*/
|
|
42
|
+
export class FormState extends Context.Tag("fibrae/FormState")() {
|
|
43
|
+
}
|
|
44
|
+
// =============================================================================
|
|
45
|
+
// Helpers
|
|
46
|
+
// =============================================================================
|
|
47
|
+
/**
|
|
48
|
+
* Serialize FormData into a plain record.
|
|
49
|
+
* Multiple values for the same key become arrays.
|
|
50
|
+
*/
|
|
51
|
+
const formDataToRecord = (formData) => {
|
|
52
|
+
const record = {};
|
|
53
|
+
formData.forEach((value, key) => {
|
|
54
|
+
const existing = record[key];
|
|
55
|
+
if (existing !== undefined) {
|
|
56
|
+
record[key] = Array.isArray(existing) ? [...existing, value] : [existing, value];
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
record[key] = value;
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
return record;
|
|
63
|
+
};
|
|
64
|
+
// =============================================================================
|
|
65
|
+
// Form Component
|
|
66
|
+
// =============================================================================
|
|
67
|
+
/**
|
|
68
|
+
* Create a Form component for the current route context.
|
|
69
|
+
*
|
|
70
|
+
* Wires to the route's action by default. Accepts explicit action for fetcher-style.
|
|
71
|
+
*
|
|
72
|
+
* Usage:
|
|
73
|
+
* ```typescript
|
|
74
|
+
* // Route action form — uses route's action config
|
|
75
|
+
* <Form>
|
|
76
|
+
* <input name="title" />
|
|
77
|
+
* <button type="submit">Save</button>
|
|
78
|
+
* </Form>
|
|
79
|
+
*
|
|
80
|
+
* // Explicit action (fetcher-style)
|
|
81
|
+
* <Form action={myAction} navigate={false}>
|
|
82
|
+
* <input name="query" />
|
|
83
|
+
* <button type="submit">Search</button>
|
|
84
|
+
* </Form>
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
export function Form(props) {
|
|
88
|
+
return Effect.gen(function* () {
|
|
89
|
+
const navigator = yield* Navigator;
|
|
90
|
+
const routerHandlers = yield* RouterHandlers;
|
|
91
|
+
const registry = yield* AtomRegistry.AtomRegistry;
|
|
92
|
+
// Resolve the action: explicit prop or from current route
|
|
93
|
+
const resolvedAction = props.action
|
|
94
|
+
? Option.some(props.action)
|
|
95
|
+
: yield* resolveRouteAction(navigator, routerHandlers, registry);
|
|
96
|
+
// Create submission state atom for this form instance
|
|
97
|
+
const stateAtom = Atom.make({ _tag: "Idle" });
|
|
98
|
+
// Build the submit handler
|
|
99
|
+
const handleSubmit = (e) => {
|
|
100
|
+
e.preventDefault();
|
|
101
|
+
const form = e.target;
|
|
102
|
+
const formData = new FormData(form);
|
|
103
|
+
const rawPayload = formDataToRecord(formData);
|
|
104
|
+
return Effect.gen(function* () {
|
|
105
|
+
if (Option.isNone(resolvedAction)) {
|
|
106
|
+
yield* Effect.logWarning("Form submitted but no action configured");
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const routeAction = resolvedAction.value;
|
|
110
|
+
// Transition to pending
|
|
111
|
+
registry.set(stateAtom, { _tag: "Pending" });
|
|
112
|
+
// Decode payload via schema
|
|
113
|
+
const decoded = yield* Schema.decodeUnknown(routeAction.schema)(rawPayload).pipe(Effect.mapError((cause) => new FormValidationError({
|
|
114
|
+
message: "Form validation failed",
|
|
115
|
+
cause,
|
|
116
|
+
})));
|
|
117
|
+
// Invoke action
|
|
118
|
+
const result = yield* routeAction.handler({ payload: decoded });
|
|
119
|
+
// Success
|
|
120
|
+
registry.set(stateAtom, { _tag: "Success", data: result });
|
|
121
|
+
if (props.onSuccess)
|
|
122
|
+
props.onSuccess(result);
|
|
123
|
+
// Navigate after success (unless disabled)
|
|
124
|
+
if (props.navigate !== false && props.navigateTo) {
|
|
125
|
+
yield* navigator.go(props.navigateTo);
|
|
126
|
+
}
|
|
127
|
+
}).pipe(Effect.catchAll((error) => Effect.sync(() => {
|
|
128
|
+
registry.set(stateAtom, { _tag: "Failure", error });
|
|
129
|
+
if (props.onError)
|
|
130
|
+
props.onError(error);
|
|
131
|
+
})));
|
|
132
|
+
};
|
|
133
|
+
// Normalize children
|
|
134
|
+
const normalizedChildren = props.children
|
|
135
|
+
? Array.isArray(props.children)
|
|
136
|
+
? props.children
|
|
137
|
+
: [props.children]
|
|
138
|
+
: [];
|
|
139
|
+
return {
|
|
140
|
+
type: "form",
|
|
141
|
+
props: {
|
|
142
|
+
method: props.method ?? "post",
|
|
143
|
+
class: props.class || undefined,
|
|
144
|
+
"data-cy": props["data-cy"],
|
|
145
|
+
onSubmit: handleSubmit,
|
|
146
|
+
children: normalizedChildren.filter((child) => child !== null && child !== undefined && child !== false && child !== true),
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Resolve the action from the current route.
|
|
153
|
+
* Reads Navigator.currentRoute to find the matched route's action.
|
|
154
|
+
*/
|
|
155
|
+
function resolveRouteAction(navigator, routerHandlers, registry) {
|
|
156
|
+
return Effect.sync(() => {
|
|
157
|
+
const currentRoute = registry.get(navigator.currentRoute);
|
|
158
|
+
if (Option.isNone(currentRoute))
|
|
159
|
+
return Option.none();
|
|
160
|
+
const handler = routerHandlers.getHandler(currentRoute.value.routeName);
|
|
161
|
+
if (Option.isNone(handler))
|
|
162
|
+
return Option.none();
|
|
163
|
+
return handler.value.action;
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=Form.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Form.js","sourceRoot":"","sources":["../../src/router/Form.ts"],"names":[],"mappings":"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"}
|
package/dist/router/History.d.ts
CHANGED
|
@@ -39,11 +39,11 @@ export interface HistoryService {
|
|
|
39
39
|
/**
|
|
40
40
|
* Navigate to a new location (push new entry).
|
|
41
41
|
*/
|
|
42
|
-
readonly push: (path: string, state?: unknown) => Effect.Effect<void
|
|
42
|
+
readonly push: (path: string, state?: unknown) => Effect.Effect<void>;
|
|
43
43
|
/**
|
|
44
44
|
* Replace current location (same entry).
|
|
45
45
|
*/
|
|
46
|
-
readonly replace: (path: string, state?: unknown) => Effect.Effect<void
|
|
46
|
+
readonly replace: (path: string, state?: unknown) => Effect.Effect<void>;
|
|
47
47
|
/**
|
|
48
48
|
* Go back in history.
|
|
49
49
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"History.js","sourceRoot":"","sources":["../../src/router/History.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAC1C,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,IAAI,EAAE,QAAQ,IAAI,YAAY,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"History.js","sourceRoot":"","sources":["../../src/router/History.ts"],"names":[],"mappings":"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"}
|
package/dist/router/Link.d.ts
CHANGED
|
@@ -1,56 +1,51 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Link component - declarative navigation.
|
|
3
3
|
*
|
|
4
|
-
* Renders an <a> element with correct href and handles click for SPA navigation.
|
|
5
|
-
* - <Link
|
|
6
|
-
* - <Link
|
|
7
|
-
* - <Link
|
|
8
|
-
* - <Link
|
|
4
|
+
* Renders an <a> element with the correct href and handles click for SPA navigation.
|
|
5
|
+
* - <Link href="/posts">text</Link>
|
|
6
|
+
* - <Link href={`/posts/${id}`}>text</Link>
|
|
7
|
+
* - <Link href="/search" search={{ q: "effect" }}>text</Link>
|
|
8
|
+
* - <Link href="/posts" replace>text</Link>
|
|
9
9
|
*
|
|
10
|
-
* Design: href is
|
|
11
|
-
* and uses Navigator for SPA navigation.
|
|
10
|
+
* Design: href is passed through directly (works with SSR). onClick prevents
|
|
11
|
+
* default and uses Navigator for SPA navigation.
|
|
12
12
|
*/
|
|
13
13
|
import * as Effect from "effect/Effect";
|
|
14
14
|
import { Registry as AtomRegistry } from "@effect-atom/atom";
|
|
15
15
|
import { Navigator } from "./Navigator.js";
|
|
16
|
-
import type { Router } from "./Router.js";
|
|
17
16
|
import type { VElement, VChild } from "../shared.js";
|
|
17
|
+
import type { ValidHref } from "./register.js";
|
|
18
18
|
/**
|
|
19
19
|
* Props for the Link component.
|
|
20
|
-
*
|
|
20
|
+
* `href` is validated against registered route paths via RegisteredRouter.
|
|
21
21
|
*/
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
readonly params?: Record<string, unknown>;
|
|
22
|
+
/** Props handled by Link — omitted from the passthrough anchor attributes. */
|
|
23
|
+
type LinkOwnProps = {
|
|
24
|
+
/** Path to navigate to — validated against registered route patterns */
|
|
25
|
+
readonly href: ValidHref;
|
|
27
26
|
/** Search/query parameters */
|
|
28
27
|
readonly search?: Record<string, unknown>;
|
|
29
28
|
/** Use history.replace instead of push */
|
|
30
29
|
readonly replace?: boolean;
|
|
31
|
-
/**
|
|
32
|
-
readonly
|
|
30
|
+
/** Enable View Transitions API for this navigation (CSS-driven animations) */
|
|
31
|
+
readonly viewTransition?: boolean;
|
|
33
32
|
/** Active class name (default: "active") */
|
|
34
33
|
readonly activeClass?: string;
|
|
35
|
-
/** Data attributes for testing */
|
|
36
|
-
readonly "data-cy"?: string;
|
|
37
34
|
/** Children to render inside the anchor (already normalized by JSX runtime) */
|
|
38
35
|
readonly children?: VChild;
|
|
39
|
-
}
|
|
36
|
+
};
|
|
37
|
+
/** Anchor attributes that Link doesn't override — class, data-*, aria-*, etc. */
|
|
38
|
+
type AnchorPassthroughProps = Omit<JSX.IntrinsicElements["a"], "href" | "onClick" | "children">;
|
|
39
|
+
export type LinkProps = LinkOwnProps & AnchorPassthroughProps;
|
|
40
40
|
/**
|
|
41
|
-
*
|
|
41
|
+
* Link component — declarative, type-safe navigation with real paths.
|
|
42
42
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* Usage:
|
|
50
|
-
* ```typescript
|
|
51
|
-
* const Link = createLink(appRouter);
|
|
52
|
-
* <Link to="posts" params={{ id: 123 }}>View Post</Link>
|
|
53
|
-
* // <Link to="typo" /> — compile-time error!
|
|
43
|
+
* ```tsx
|
|
44
|
+
* import { Link } from "fibrae/router";
|
|
45
|
+
* <Link href="/posts">Posts</Link>
|
|
46
|
+
* <Link href={`/posts/${id}`}>View Post</Link>
|
|
47
|
+
* // <Link href="/typo" /> — compile-time error (with RegisteredRouter)
|
|
54
48
|
* ```
|
|
55
49
|
*/
|
|
56
|
-
export declare function
|
|
50
|
+
export declare function Link(props: LinkProps): Effect.Effect<VElement, never, Navigator | AtomRegistry.AtomRegistry>;
|
|
51
|
+
export {};
|
package/dist/router/Link.js
CHANGED
|
@@ -1,114 +1,72 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Link component - declarative navigation.
|
|
3
3
|
*
|
|
4
|
-
* Renders an <a> element with correct href and handles click for SPA navigation.
|
|
5
|
-
* - <Link
|
|
6
|
-
* - <Link
|
|
7
|
-
* - <Link
|
|
8
|
-
* - <Link
|
|
4
|
+
* Renders an <a> element with the correct href and handles click for SPA navigation.
|
|
5
|
+
* - <Link href="/posts">text</Link>
|
|
6
|
+
* - <Link href={`/posts/${id}`}>text</Link>
|
|
7
|
+
* - <Link href="/search" search={{ q: "effect" }}>text</Link>
|
|
8
|
+
* - <Link href="/posts" replace>text</Link>
|
|
9
9
|
*
|
|
10
|
-
* Design: href is
|
|
11
|
-
* and uses Navigator for SPA navigation.
|
|
10
|
+
* Design: href is passed through directly (works with SSR). onClick prevents
|
|
11
|
+
* default and uses Navigator for SPA navigation.
|
|
12
12
|
*/
|
|
13
|
+
// eslint-disable-next-line no-unused-vars -- jsx is used by the JSX transform (jsxFactory)
|
|
14
|
+
import { jsx } from "../jsx-runtime/index.js";
|
|
13
15
|
import * as Effect from "effect/Effect";
|
|
14
|
-
import
|
|
15
|
-
import { Atom, Registry as AtomRegistry } from "@effect-atom/atom";
|
|
16
|
+
import { Registry as AtomRegistry } from "@effect-atom/atom";
|
|
16
17
|
import { Navigator } from "./Navigator.js";
|
|
17
|
-
import {
|
|
18
|
+
import { buildSearchString } from "./utils.js";
|
|
18
19
|
// =============================================================================
|
|
19
|
-
//
|
|
20
|
+
// Link Component
|
|
20
21
|
// =============================================================================
|
|
21
22
|
/**
|
|
22
|
-
*
|
|
23
|
-
* Returns Effect since route.interpolate is effectful.
|
|
24
|
-
*/
|
|
25
|
-
function buildHref(router, routeName, params, search, basePath = "") {
|
|
26
|
-
return findRouteByName(router, routeName).pipe(Option.match({
|
|
27
|
-
onNone: () => Effect.succeed("#"),
|
|
28
|
-
onSome: ({ route, group }) => route.interpolate(params ?? {}).pipe(Effect.map((pathname) => {
|
|
29
|
-
const searchString = search ? buildSearchString(search) : "";
|
|
30
|
-
return `${basePath}${groupBasePath(group)}${pathname}${searchString}`;
|
|
31
|
-
}), Effect.catchAll(() => Effect.succeed("#"))),
|
|
32
|
-
}));
|
|
33
|
-
}
|
|
34
|
-
// =============================================================================
|
|
35
|
-
// Link Component Factory
|
|
36
|
-
// =============================================================================
|
|
37
|
-
/**
|
|
38
|
-
* Create a Link component bound to a router.
|
|
39
|
-
*
|
|
40
|
-
* The Link component must know which router to use for:
|
|
41
|
-
* - Building hrefs from route names
|
|
42
|
-
* - Checking active state
|
|
43
|
-
*
|
|
44
|
-
* The returned Link component constrains `to` to the router's valid route names.
|
|
23
|
+
* Link component — declarative, type-safe navigation with real paths.
|
|
45
24
|
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* <Link
|
|
50
|
-
* // <Link
|
|
25
|
+
* ```tsx
|
|
26
|
+
* import { Link } from "fibrae/router";
|
|
27
|
+
* <Link href="/posts">Posts</Link>
|
|
28
|
+
* <Link href={`/posts/${id}`}>View Post</Link>
|
|
29
|
+
* // <Link href="/typo" /> — compile-time error (with RegisteredRouter)
|
|
51
30
|
* ```
|
|
52
31
|
*/
|
|
53
|
-
export function
|
|
54
|
-
return function
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
32
|
+
export function Link(props) {
|
|
33
|
+
return Effect.gen(function* () {
|
|
34
|
+
const navigator = yield* Navigator;
|
|
35
|
+
// Build full href with basePath and search params
|
|
36
|
+
const searchString = props.search ? buildSearchString(props.search) : "";
|
|
37
|
+
const fullHref = `${navigator.basePath}${props.href}${searchString}`;
|
|
38
|
+
// Active state: currentPathname is already basePath-stripped by Navigator
|
|
39
|
+
const isActive = navigator.currentPathname === props.href;
|
|
40
|
+
// Build class string
|
|
41
|
+
const activeClass = props.activeClass ?? "active";
|
|
42
|
+
const classes = [props.class, isActive ? activeClass : null].filter(Boolean).join(" ");
|
|
43
|
+
// Extract user-provided click handler (lowercase or camelCase)
|
|
44
|
+
const userOnClick = props.onclick ?? props.onClick;
|
|
45
|
+
// Click handler - prevent default and use Navigator for SPA navigation
|
|
46
|
+
const handleClick = (e) => {
|
|
47
|
+
// Only intercept left clicks without modifier keys
|
|
48
|
+
if (e.button !== 0 || e.ctrlKey || e.metaKey || e.shiftKey) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
e.preventDefault();
|
|
52
|
+
// Call user's onclick handler if provided
|
|
53
|
+
if (typeof userOnClick === "function") {
|
|
54
|
+
userOnClick(e);
|
|
55
|
+
}
|
|
56
|
+
return navigator.go(props.href, {
|
|
57
|
+
search: props.search,
|
|
58
|
+
replace: props.replace,
|
|
59
|
+
viewTransition: props.viewTransition,
|
|
77
60
|
});
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
return navigator.go(props.to, {
|
|
89
|
-
path: props.params,
|
|
90
|
-
searchParams: props.search,
|
|
91
|
-
replace: props.replace,
|
|
92
|
-
});
|
|
93
|
-
};
|
|
94
|
-
// Return VElement directly - children are already normalized by JSX runtime
|
|
95
|
-
// Normalize children to array (VChild can be single value or array)
|
|
96
|
-
const normalizedChildren = props.children
|
|
97
|
-
? Array.isArray(props.children)
|
|
98
|
-
? props.children
|
|
99
|
-
: [props.children]
|
|
100
|
-
: [];
|
|
101
|
-
return {
|
|
102
|
-
type: "a",
|
|
103
|
-
props: {
|
|
104
|
-
href,
|
|
105
|
-
class: classes || undefined,
|
|
106
|
-
"data-cy": props["data-cy"],
|
|
107
|
-
onClick: handleClick,
|
|
108
|
-
children: normalizedChildren.filter((child) => child !== null && child !== undefined && child !== false && child !== true),
|
|
109
|
-
},
|
|
110
|
-
};
|
|
111
|
-
});
|
|
112
|
-
};
|
|
61
|
+
};
|
|
62
|
+
const normalizedChildren = props.children
|
|
63
|
+
? Array.isArray(props.children)
|
|
64
|
+
? props.children
|
|
65
|
+
: [props.children]
|
|
66
|
+
: [];
|
|
67
|
+
// Separate Link-specific props from HTML anchor attributes
|
|
68
|
+
const { href: _href, search: _search, replace: _replace, viewTransition: _viewTransition, activeClass: _activeClass, class: _className, children: _children, onclick: _onclick, onClick: _onClick, ...anchorProps } = props;
|
|
69
|
+
return (jsx("a", { ...anchorProps, href: fullHref, class: classes || undefined, onClick: handleClick }, normalizedChildren));
|
|
70
|
+
});
|
|
113
71
|
}
|
|
114
72
|
//# sourceMappingURL=Link.js.map
|
package/dist/router/Link.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Link.js","sourceRoot":"","sources":["../../src/router/Link.
|
|
1
|
+
{"version":3,"file":"Link.js","sourceRoot":"","sources":["../../src/router/Link.tsx"],"names":[],"mappings":"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"}
|