remix 3.0.0-beta.2 → 3.0.0-beta.3
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/headers/accept-encoding.d.ts +2 -0
- package/dist/headers/accept-encoding.d.ts.map +1 -0
- package/dist/headers/accept-encoding.js +2 -0
- package/dist/headers/accept-language.d.ts +2 -0
- package/dist/headers/accept-language.d.ts.map +1 -0
- package/dist/headers/accept-language.js +2 -0
- package/dist/headers/accept.d.ts +2 -0
- package/dist/headers/accept.d.ts.map +1 -0
- package/dist/headers/accept.js +2 -0
- package/dist/headers/cache-control.d.ts +2 -0
- package/dist/headers/cache-control.d.ts.map +1 -0
- package/dist/headers/cache-control.js +2 -0
- package/dist/headers/content-disposition.d.ts +2 -0
- package/dist/headers/content-disposition.d.ts.map +1 -0
- package/dist/headers/content-disposition.js +2 -0
- package/dist/headers/content-range.d.ts +2 -0
- package/dist/headers/content-range.d.ts.map +1 -0
- package/dist/headers/content-range.js +2 -0
- package/dist/headers/content-type.d.ts +2 -0
- package/dist/headers/content-type.d.ts.map +1 -0
- package/dist/headers/content-type.js +2 -0
- package/dist/headers/cookie.d.ts +2 -0
- package/dist/headers/cookie.d.ts.map +1 -0
- package/dist/headers/cookie.js +2 -0
- package/dist/headers/if-match.d.ts +2 -0
- package/dist/headers/if-match.d.ts.map +1 -0
- package/dist/headers/if-match.js +2 -0
- package/dist/headers/if-none-match.d.ts +2 -0
- package/dist/headers/if-none-match.d.ts.map +1 -0
- package/dist/headers/if-none-match.js +2 -0
- package/dist/headers/if-range.d.ts +2 -0
- package/dist/headers/if-range.d.ts.map +1 -0
- package/dist/headers/if-range.js +2 -0
- package/dist/headers/range.d.ts +2 -0
- package/dist/headers/range.d.ts.map +1 -0
- package/dist/headers/range.js +2 -0
- package/dist/headers/raw-headers.d.ts +2 -0
- package/dist/headers/raw-headers.d.ts.map +1 -0
- package/dist/headers/raw-headers.js +2 -0
- package/dist/headers/set-cookie.d.ts +2 -0
- package/dist/headers/set-cookie.d.ts.map +1 -0
- package/dist/headers/set-cookie.js +2 -0
- package/dist/headers/vary.d.ts +2 -0
- package/dist/headers/vary.d.ts.map +1 -0
- package/dist/headers/vary.js +2 -0
- package/package.json +95 -35
- package/src/csrf-middleware/README.md +5 -12
- package/src/data-schema/README.md +3 -9
- package/src/data-table/README.md +6 -14
- package/src/data-table-mysql/README.md +5 -11
- package/src/data-table-postgres/README.md +2 -4
- package/src/data-table-sqlite/README.md +2 -4
- package/src/fetch-proxy/README.md +1 -2
- package/src/file-storage-s3/README.md +1 -2
- package/src/form-data-middleware/README.md +1 -2
- package/src/form-data-parser/README.md +2 -4
- package/src/headers/README.md +10 -0
- package/src/headers/accept-encoding.ts +2 -0
- package/src/headers/accept-language.ts +2 -0
- package/src/headers/accept.ts +2 -0
- package/src/headers/cache-control.ts +2 -0
- package/src/headers/content-disposition.ts +2 -0
- package/src/headers/content-range.ts +2 -0
- package/src/headers/content-type.ts +2 -0
- package/src/headers/cookie.ts +2 -0
- package/src/headers/if-match.ts +2 -0
- package/src/headers/if-none-match.ts +2 -0
- package/src/headers/if-range.ts +2 -0
- package/src/headers/range.ts +2 -0
- package/src/headers/raw-headers.ts +2 -0
- package/src/headers/set-cookie.ts +2 -0
- package/src/headers/vary.ts +2 -0
- package/src/node-fetch-server/README.md +1 -2
- package/src/node-tsx/README.md +3 -8
- package/src/route-pattern/README.md +18 -13
- package/src/session-storage-redis/README.md +1 -2
- package/src/test/README.md +2 -5
- package/src/ui/anchor/README.md +11 -3
- package/src/ui/menu/README.md +26 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accept-encoding.d.ts","sourceRoot":"","sources":["../../src/headers/accept-encoding.ts"],"names":[],"mappings":"AACA,cAAc,oCAAoC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accept-language.d.ts","sourceRoot":"","sources":["../../src/headers/accept-language.ts"],"names":[],"mappings":"AACA,cAAc,oCAAoC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accept.d.ts","sourceRoot":"","sources":["../../src/headers/accept.ts"],"names":[],"mappings":"AACA,cAAc,2BAA2B,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache-control.d.ts","sourceRoot":"","sources":["../../src/headers/cache-control.ts"],"names":[],"mappings":"AACA,cAAc,kCAAkC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-disposition.d.ts","sourceRoot":"","sources":["../../src/headers/content-disposition.ts"],"names":[],"mappings":"AACA,cAAc,wCAAwC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-range.d.ts","sourceRoot":"","sources":["../../src/headers/content-range.ts"],"names":[],"mappings":"AACA,cAAc,kCAAkC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-type.d.ts","sourceRoot":"","sources":["../../src/headers/content-type.ts"],"names":[],"mappings":"AACA,cAAc,iCAAiC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cookie.d.ts","sourceRoot":"","sources":["../../src/headers/cookie.ts"],"names":[],"mappings":"AACA,cAAc,2BAA2B,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"if-match.d.ts","sourceRoot":"","sources":["../../src/headers/if-match.ts"],"names":[],"mappings":"AACA,cAAc,6BAA6B,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"if-none-match.d.ts","sourceRoot":"","sources":["../../src/headers/if-none-match.ts"],"names":[],"mappings":"AACA,cAAc,kCAAkC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"if-range.d.ts","sourceRoot":"","sources":["../../src/headers/if-range.ts"],"names":[],"mappings":"AACA,cAAc,6BAA6B,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"range.d.ts","sourceRoot":"","sources":["../../src/headers/range.ts"],"names":[],"mappings":"AACA,cAAc,0BAA0B,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"raw-headers.d.ts","sourceRoot":"","sources":["../../src/headers/raw-headers.ts"],"names":[],"mappings":"AACA,cAAc,gCAAgC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"set-cookie.d.ts","sourceRoot":"","sources":["../../src/headers/set-cookie.ts"],"names":[],"mappings":"AACA,cAAc,+BAA+B,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vary.d.ts","sourceRoot":"","sources":["../../src/headers/vary.ts"],"names":[],"mappings":"AACA,cAAc,yBAAyB,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "remix",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.3",
|
|
4
4
|
"description": "The Remix web framework",
|
|
5
5
|
"author": "Michael Jackson <mjijackson@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -177,6 +177,66 @@
|
|
|
177
177
|
"types": "./dist/headers.d.ts",
|
|
178
178
|
"default": "./dist/headers.js"
|
|
179
179
|
},
|
|
180
|
+
"./headers/accept": {
|
|
181
|
+
"types": "./dist/headers/accept.d.ts",
|
|
182
|
+
"default": "./dist/headers/accept.js"
|
|
183
|
+
},
|
|
184
|
+
"./headers/accept-encoding": {
|
|
185
|
+
"types": "./dist/headers/accept-encoding.d.ts",
|
|
186
|
+
"default": "./dist/headers/accept-encoding.js"
|
|
187
|
+
},
|
|
188
|
+
"./headers/accept-language": {
|
|
189
|
+
"types": "./dist/headers/accept-language.d.ts",
|
|
190
|
+
"default": "./dist/headers/accept-language.js"
|
|
191
|
+
},
|
|
192
|
+
"./headers/cache-control": {
|
|
193
|
+
"types": "./dist/headers/cache-control.d.ts",
|
|
194
|
+
"default": "./dist/headers/cache-control.js"
|
|
195
|
+
},
|
|
196
|
+
"./headers/content-disposition": {
|
|
197
|
+
"types": "./dist/headers/content-disposition.d.ts",
|
|
198
|
+
"default": "./dist/headers/content-disposition.js"
|
|
199
|
+
},
|
|
200
|
+
"./headers/content-range": {
|
|
201
|
+
"types": "./dist/headers/content-range.d.ts",
|
|
202
|
+
"default": "./dist/headers/content-range.js"
|
|
203
|
+
},
|
|
204
|
+
"./headers/content-type": {
|
|
205
|
+
"types": "./dist/headers/content-type.d.ts",
|
|
206
|
+
"default": "./dist/headers/content-type.js"
|
|
207
|
+
},
|
|
208
|
+
"./headers/cookie": {
|
|
209
|
+
"types": "./dist/headers/cookie.d.ts",
|
|
210
|
+
"default": "./dist/headers/cookie.js"
|
|
211
|
+
},
|
|
212
|
+
"./headers/if-match": {
|
|
213
|
+
"types": "./dist/headers/if-match.d.ts",
|
|
214
|
+
"default": "./dist/headers/if-match.js"
|
|
215
|
+
},
|
|
216
|
+
"./headers/if-none-match": {
|
|
217
|
+
"types": "./dist/headers/if-none-match.d.ts",
|
|
218
|
+
"default": "./dist/headers/if-none-match.js"
|
|
219
|
+
},
|
|
220
|
+
"./headers/if-range": {
|
|
221
|
+
"types": "./dist/headers/if-range.d.ts",
|
|
222
|
+
"default": "./dist/headers/if-range.js"
|
|
223
|
+
},
|
|
224
|
+
"./headers/range": {
|
|
225
|
+
"types": "./dist/headers/range.d.ts",
|
|
226
|
+
"default": "./dist/headers/range.js"
|
|
227
|
+
},
|
|
228
|
+
"./headers/raw-headers": {
|
|
229
|
+
"types": "./dist/headers/raw-headers.d.ts",
|
|
230
|
+
"default": "./dist/headers/raw-headers.js"
|
|
231
|
+
},
|
|
232
|
+
"./headers/set-cookie": {
|
|
233
|
+
"types": "./dist/headers/set-cookie.d.ts",
|
|
234
|
+
"default": "./dist/headers/set-cookie.js"
|
|
235
|
+
},
|
|
236
|
+
"./headers/vary": {
|
|
237
|
+
"types": "./dist/headers/vary.d.ts",
|
|
238
|
+
"default": "./dist/headers/vary.js"
|
|
239
|
+
},
|
|
180
240
|
"./html-template": {
|
|
181
241
|
"types": "./dist/html-template.d.ts",
|
|
182
242
|
"default": "./dist/html-template.js"
|
|
@@ -469,50 +529,50 @@
|
|
|
469
529
|
"typescript": "^5.9.3"
|
|
470
530
|
},
|
|
471
531
|
"dependencies": {
|
|
472
|
-
"@remix-run/assets": "^0.4.
|
|
473
|
-
"@remix-run/
|
|
474
|
-
"@remix-run/
|
|
475
|
-
"@remix-run/
|
|
476
|
-
"@remix-run/
|
|
477
|
-
"@remix-run/
|
|
478
|
-
"@remix-run/
|
|
479
|
-
"@remix-run/
|
|
480
|
-
"@remix-run/
|
|
481
|
-
"@remix-run/
|
|
532
|
+
"@remix-run/assets": "^0.4.2",
|
|
533
|
+
"@remix-run/auth": "^0.2.4",
|
|
534
|
+
"@remix-run/compression-middleware": "^0.1.10",
|
|
535
|
+
"@remix-run/cop-middleware": "^0.1.5",
|
|
536
|
+
"@remix-run/csrf-middleware": "^0.1.5",
|
|
537
|
+
"@remix-run/cors-middleware": "^0.1.5",
|
|
538
|
+
"@remix-run/ui": "^0.3.0",
|
|
539
|
+
"@remix-run/async-context-middleware": "^0.3.2",
|
|
540
|
+
"@remix-run/auth-middleware": "^0.2.2",
|
|
541
|
+
"@remix-run/cookie": "^0.5.4",
|
|
482
542
|
"@remix-run/data-schema": "^0.3.0",
|
|
483
|
-
"@remix-run/data-table": "^0.3.0",
|
|
484
|
-
"@remix-run/data-table-mysql": "^0.4.0",
|
|
485
543
|
"@remix-run/data-table-postgres": "^0.4.0",
|
|
486
|
-
"@remix-run/data-table-sqlite": "^0.5.
|
|
487
|
-
"@remix-run/
|
|
488
|
-
"@remix-run/
|
|
489
|
-
"@remix-run/
|
|
490
|
-
"@remix-run/
|
|
491
|
-
"@remix-run/
|
|
492
|
-
"@remix-run/file-storage-s3": "^0.1.
|
|
493
|
-
"@remix-run/
|
|
544
|
+
"@remix-run/data-table-sqlite": "^0.5.1",
|
|
545
|
+
"@remix-run/data-table-mysql": "^0.4.0",
|
|
546
|
+
"@remix-run/data-table": "^0.3.0",
|
|
547
|
+
"@remix-run/fetch-router": "^0.19.2",
|
|
548
|
+
"@remix-run/file-storage": "^0.13.6",
|
|
549
|
+
"@remix-run/fetch-proxy": "^0.8.3",
|
|
550
|
+
"@remix-run/file-storage-s3": "^0.1.3",
|
|
551
|
+
"@remix-run/form-data-middleware": "^0.3.2",
|
|
552
|
+
"@remix-run/form-data-parser": "^0.17.3",
|
|
553
|
+
"@remix-run/fs": "^0.4.5",
|
|
554
|
+
"@remix-run/headers": "^0.21.1",
|
|
555
|
+
"@remix-run/lazy-file": "^5.0.5",
|
|
494
556
|
"@remix-run/html-template": "^0.3.1",
|
|
495
|
-
"@remix-run/
|
|
496
|
-
"@remix-run/
|
|
497
|
-
"@remix-run/
|
|
557
|
+
"@remix-run/logger-middleware": "^0.3.2",
|
|
558
|
+
"@remix-run/method-override-middleware": "^0.1.10",
|
|
559
|
+
"@remix-run/multipart-parser": "^0.16.3",
|
|
498
560
|
"@remix-run/mime": "^0.4.1",
|
|
499
|
-
"@remix-run/multipart-parser": "^0.16.2",
|
|
500
|
-
"@remix-run/method-override-middleware": "^0.1.9",
|
|
501
561
|
"@remix-run/node-fetch-server": "^0.13.3",
|
|
562
|
+
"@remix-run/response": "^0.3.6",
|
|
502
563
|
"@remix-run/node-tsx": "^0.1.1",
|
|
503
|
-
"@remix-run/route-pattern": "^0.
|
|
564
|
+
"@remix-run/route-pattern": "^0.22.0",
|
|
504
565
|
"@remix-run/session": "^0.4.2",
|
|
505
|
-
"@remix-run/
|
|
506
|
-
"@remix-run/session-
|
|
507
|
-
"@remix-run/
|
|
508
|
-
"@remix-run/static-middleware": "^0.4.10",
|
|
566
|
+
"@remix-run/session-middleware": "^0.3.2",
|
|
567
|
+
"@remix-run/session-storage-memcache": "^0.1.2",
|
|
568
|
+
"@remix-run/static-middleware": "^0.4.11",
|
|
509
569
|
"@remix-run/session-storage-redis": "^0.1.1",
|
|
510
570
|
"@remix-run/tar-parser": "^0.7.1",
|
|
571
|
+
"@remix-run/cli": "^0.3.2",
|
|
511
572
|
"@remix-run/assert": "^0.2.1",
|
|
512
|
-
"@remix-run/cli": "^0.3.1",
|
|
513
|
-
"@remix-run/render-middleware": "^0.1.1",
|
|
514
573
|
"@remix-run/terminal": "^0.1.1",
|
|
515
|
-
"@remix-run/test": "^0.4.
|
|
574
|
+
"@remix-run/test": "^0.4.2",
|
|
575
|
+
"@remix-run/render-middleware": "^0.1.2"
|
|
516
576
|
},
|
|
517
577
|
"bin": {
|
|
518
578
|
"remix": "./dist/cli-entry.js"
|
|
@@ -521,7 +581,7 @@
|
|
|
521
581
|
"mysql2": "^3.15.3",
|
|
522
582
|
"pg": "^8.16.3",
|
|
523
583
|
"redis": "^5.10.0",
|
|
524
|
-
"playwright": "^1.
|
|
584
|
+
"playwright": "^1.60.0"
|
|
525
585
|
},
|
|
526
586
|
"peerDependenciesMeta": {
|
|
527
587
|
"mysql2": {
|
|
@@ -74,18 +74,11 @@ For unsafe methods (`POST`, `PUT`, `PATCH`, `DELETE`), the middleware validates
|
|
|
74
74
|
|
|
75
75
|
## Why This Exists
|
|
76
76
|
|
|
77
|
-
Modern browsers now provide stronger cross-origin signals like `Sec-Fetch-Site`, and explicit
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
Remix cannot assume those guarantees for every app. `csrf()` still exists as the conservative
|
|
83
|
-
option for apps that want synchronizer tokens in addition to origin checks, especially for
|
|
84
|
-
session-backed HTML form workflows and mixed deployment environments.
|
|
85
|
-
|
|
86
|
-
If your deployment can guarantee the prerequisites for the tokenless model, this middleware is
|
|
87
|
-
optional. In that case, [`cop-middleware`](https://github.com/remix-run/remix/tree/main/packages/cop-middleware)
|
|
88
|
-
may be a better fit.
|
|
77
|
+
Modern browsers now provide stronger cross-origin signals like `Sec-Fetch-Site`, and explicit `SameSite=Lax` cookies already block many CSRF attacks. We have considered the lighter, tokenless model used by Go's `CrossOriginProtection`, and we think it is a good fit when a deployment can make all of the guarantees that model depends on.
|
|
78
|
+
|
|
79
|
+
Remix cannot assume those guarantees for every app. `csrf()` still exists as the conservative option for apps that want synchronizer tokens in addition to origin checks, especially for session-backed HTML form workflows and mixed deployment environments.
|
|
80
|
+
|
|
81
|
+
If your deployment can guarantee the prerequisites for the tokenless model, this middleware is optional. In that case, [`cop-middleware`](https://github.com/remix-run/remix/tree/main/packages/cop-middleware) may be a better fit.
|
|
89
82
|
|
|
90
83
|
## Related Packages
|
|
91
84
|
|
|
@@ -71,8 +71,7 @@ if (!result.success) {
|
|
|
71
71
|
|
|
72
72
|
Both `parse` and `parseSafe` accept any [Standard Schema](https://standardschema.dev/) v1 schema, not just data-schema's own schemas. You can pass a Zod, Valibot, or ArkType schema and they'll work.
|
|
73
73
|
|
|
74
|
-
For `FormData` and `URLSearchParams`, use the `remix/data-schema/form-data` helpers to build
|
|
75
|
-
schemas that plug into the same `parse()` / `parseSafe()` flow:
|
|
74
|
+
For `FormData` and `URLSearchParams`, use the `remix/data-schema/form-data` helpers to build schemas that plug into the same `parse()` / `parseSafe()` flow:
|
|
76
75
|
|
|
77
76
|
```ts
|
|
78
77
|
import * as s from 'remix/data-schema'
|
|
@@ -95,11 +94,7 @@ let filters = s.parse(
|
|
|
95
94
|
)
|
|
96
95
|
```
|
|
97
96
|
|
|
98
|
-
`f.object(...)` is the root schema for `FormData` and `URLSearchParams`.
|
|
99
|
-
Use `f.field(...)` for one text value, `f.fields(...)` for repeated text values,
|
|
100
|
-
`f.file(...)` for one uploaded file, and `f.files(...)` for repeated files.
|
|
101
|
-
When you want a fallback value, prefer `s.defaulted(s.string(), '')`.
|
|
102
|
-
File helpers are intended for `FormData`; `URLSearchParams` only supports text values.
|
|
97
|
+
`f.object(...)` is the root schema for `FormData` and `URLSearchParams`. Use `f.field(...)` for one text value, `f.fields(...)` for repeated text values, `f.file(...)` for one uploaded file, and `f.files(...)` for repeated files. When you want a fallback value, prefer `s.defaulted(s.string(), '')`. File helpers are intended for `FormData`; `URLSearchParams` only supports text values.
|
|
103
98
|
|
|
104
99
|
You can also customize built-in validation messages with `errorMap`:
|
|
105
100
|
|
|
@@ -127,8 +122,7 @@ let result = parseSafe(User, input, {
|
|
|
127
122
|
})
|
|
128
123
|
```
|
|
129
124
|
|
|
130
|
-
`errorMap` receives `{ code, defaultMessage, path, values, input, locale }`.
|
|
131
|
-
Return `undefined` to keep the default message.
|
|
125
|
+
`errorMap` receives `{ code, defaultMessage, path, values, input, locale }`. Return `undefined` to keep the default message.
|
|
132
126
|
|
|
133
127
|
## Primitives
|
|
134
128
|
|
package/src/data-table/README.md
CHANGED
|
@@ -249,8 +249,7 @@ Return behavior:
|
|
|
249
249
|
|
|
250
250
|
### Validation and Lifecycle
|
|
251
251
|
|
|
252
|
-
Validation is optional and table-scoped. Define `validate(context)` to validate/coerce write
|
|
253
|
-
payloads, and add lifecycle callbacks when you need custom read/write/delete behavior.
|
|
252
|
+
Validation is optional and table-scoped. Define `validate(context)` to validate/coerce write payloads, and add lifecycle callbacks when you need custom read/write/delete behavior.
|
|
254
253
|
|
|
255
254
|
```ts
|
|
256
255
|
import { column as c, fail, table } from 'remix/data-table'
|
|
@@ -344,10 +343,7 @@ await db.transaction(async (tx) => {
|
|
|
344
343
|
|
|
345
344
|
## Migrations
|
|
346
345
|
|
|
347
|
-
`data-table` ships a SQL-first migration system under `remix/data-table/migrations`. Each migration
|
|
348
|
-
is a directory containing hand-written `up.sql` and (optionally) `down.sql`. The runner journals
|
|
349
|
-
applied migrations, detects checksum drift, and wraps each migration in a transaction when the
|
|
350
|
-
adapter supports transactional DDL.
|
|
346
|
+
`data-table` ships a SQL-first migration system under `remix/data-table/migrations`. Each migration is a directory containing hand-written `up.sql` and (optionally) `down.sql`. The runner journals applied migrations, detects checksum drift, and wraps each migration in a transaction when the adapter supports transactional DDL.
|
|
351
347
|
|
|
352
348
|
### Example Setup
|
|
353
349
|
|
|
@@ -392,8 +388,7 @@ drop table if exists users;
|
|
|
392
388
|
|
|
393
389
|
### Multi-Statement Driver Configuration
|
|
394
390
|
|
|
395
|
-
The runner sends each migration to the adapter as a single multi-statement script. Make sure the
|
|
396
|
-
underlying driver accepts multiple statements:
|
|
391
|
+
The runner sends each migration to the adapter as a single multi-statement script. Make sure the underlying driver accepts multiple statements:
|
|
397
392
|
|
|
398
393
|
- `better-sqlite3`: works out of the box (`db.exec`).
|
|
399
394
|
- `pg`: works out of the box when no parameter array is passed.
|
|
@@ -476,8 +471,7 @@ for (let script of plan.sql) {
|
|
|
476
471
|
|
|
477
472
|
### Transaction Modes
|
|
478
473
|
|
|
479
|
-
By default each migration is wrapped in a transaction when the adapter supports transactional DDL.
|
|
480
|
-
Override per migration with a directive on the first non-blank line of `up.sql`:
|
|
474
|
+
By default each migration is wrapped in a transaction when the adapter supports transactional DDL. Override per migration with a directive on the first non-blank line of `up.sql`:
|
|
481
475
|
|
|
482
476
|
```sql
|
|
483
477
|
-- data-table/transaction: none
|
|
@@ -488,11 +482,9 @@ Supported modes:
|
|
|
488
482
|
|
|
489
483
|
- `auto` (default): wrap when the adapter supports transactional DDL.
|
|
490
484
|
- `required`: wrap; the runner throws if the adapter cannot support it.
|
|
491
|
-
- `none`: never wrap. Use this for statements like postgres `CREATE INDEX CONCURRENTLY` that
|
|
492
|
-
cannot run inside a transaction.
|
|
485
|
+
- `none`: never wrap. Use this for statements like postgres `CREATE INDEX CONCURRENTLY` that cannot run inside a transaction.
|
|
493
486
|
|
|
494
|
-
You can also set `transaction` directly on a `MigrationDescriptor` when registering migrations
|
|
495
|
-
programmatically.
|
|
487
|
+
You can also set `transaction` directly on a `MigrationDescriptor` when registering migrations programmatically.
|
|
496
488
|
|
|
497
489
|
### Programmatic Registration
|
|
498
490
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# data-table-mysql
|
|
2
2
|
|
|
3
|
-
MySQL adapter for [`remix/data-table`](https://github.com/remix-run/remix/tree/main/packages/data-table).
|
|
4
|
-
Use this package when you want `data-table` APIs backed by `mysql2`.
|
|
3
|
+
MySQL adapter for [`remix/data-table`](https://github.com/remix-run/remix/tree/main/packages/data-table). Use this package when you want `data-table` APIs backed by `mysql2`.
|
|
5
4
|
|
|
6
5
|
## Features
|
|
7
6
|
|
|
@@ -33,8 +32,7 @@ let pool = createPool(process.env.DATABASE_URL as string)
|
|
|
33
32
|
let db = createDatabase(createMysqlDatabaseAdapter(pool))
|
|
34
33
|
```
|
|
35
34
|
|
|
36
|
-
Use `db.query(...)`, relation loading, and transactions from `remix/data-table`.
|
|
37
|
-
Import any driver-specific types you need directly from `mysql2/promise`.
|
|
35
|
+
Use `db.query(...)`, relation loading, and transactions from `remix/data-table`. Import any driver-specific types you need directly from `mysql2/promise`.
|
|
38
36
|
|
|
39
37
|
## Adapter Capabilities
|
|
40
38
|
|
|
@@ -50,9 +48,7 @@ Import any driver-specific types you need directly from `mysql2/promise`.
|
|
|
50
48
|
|
|
51
49
|
### Multi-Statement Migrations
|
|
52
50
|
|
|
53
|
-
`remix/data-table/migrations` sends each migration to the adapter as a single multi-statement SQL
|
|
54
|
-
script. mysql2 only accepts multi-statement scripts when the connection is created with
|
|
55
|
-
`multipleStatements: true`:
|
|
51
|
+
`remix/data-table/migrations` sends each migration to the adapter as a single multi-statement SQL script. mysql2 only accepts multi-statement scripts when the connection is created with `multipleStatements: true`:
|
|
56
52
|
|
|
57
53
|
```ts
|
|
58
54
|
import { createPool } from 'mysql2/promise'
|
|
@@ -65,11 +61,9 @@ let pool = createPool({
|
|
|
65
61
|
|
|
66
62
|
### `returning` On MySQL
|
|
67
63
|
|
|
68
|
-
MySQL does not natively support SQL `RETURNING`. In this adapter, using `returning` on write
|
|
69
|
-
operations throws `DataTableQueryError`.
|
|
64
|
+
MySQL does not natively support SQL `RETURNING`. In this adapter, using `returning` on write operations throws `DataTableQueryError`.
|
|
70
65
|
|
|
71
|
-
Use write metadata (`affectedRows`, `insertId`) on MySQL, or switch adapters when returned rows
|
|
72
|
-
are required.
|
|
66
|
+
Use write metadata (`affectedRows`, `insertId`) on MySQL, or switch adapters when returned rows are required.
|
|
73
67
|
|
|
74
68
|
```ts
|
|
75
69
|
import { DataTableQueryError } from 'remix/data-table'
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# data-table-postgres
|
|
2
2
|
|
|
3
|
-
PostgreSQL adapter for [`remix/data-table`](https://github.com/remix-run/remix/tree/main/packages/data-table).
|
|
4
|
-
Use this package when you want `data-table` APIs backed by `pg`.
|
|
3
|
+
PostgreSQL adapter for [`remix/data-table`](https://github.com/remix-run/remix/tree/main/packages/data-table). Use this package when you want `data-table` APIs backed by `pg`.
|
|
5
4
|
|
|
6
5
|
## Features
|
|
7
6
|
|
|
@@ -36,8 +35,7 @@ let pool = new Pool({
|
|
|
36
35
|
let db = createDatabase(createPostgresDatabaseAdapter(pool))
|
|
37
36
|
```
|
|
38
37
|
|
|
39
|
-
Use `db.query(...)`, relation loading, and transactions from `remix/data-table`.
|
|
40
|
-
Import any driver-specific types you need directly from `pg`.
|
|
38
|
+
Use `db.query(...)`, relation loading, and transactions from `remix/data-table`. Import any driver-specific types you need directly from `pg`.
|
|
41
39
|
|
|
42
40
|
## Adapter Capabilities
|
|
43
41
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# data-table-sqlite
|
|
2
2
|
|
|
3
|
-
SQLite adapter for [`remix/data-table`](https://github.com/remix-run/remix/tree/main/packages/data-table).
|
|
4
|
-
Use this package when you want `data-table` APIs backed by a synchronous SQLite client.
|
|
3
|
+
SQLite adapter for [`remix/data-table`](https://github.com/remix-run/remix/tree/main/packages/data-table). Use this package when you want `data-table` APIs backed by a synchronous SQLite client.
|
|
5
4
|
|
|
6
5
|
## Features
|
|
7
6
|
|
|
@@ -46,8 +45,7 @@ let sqlite = new Database('app.db')
|
|
|
46
45
|
let db = createDatabase(createSqliteDatabaseAdapter(sqlite))
|
|
47
46
|
```
|
|
48
47
|
|
|
49
|
-
This is a good fit for local development, embedded deployments, and single-node services.
|
|
50
|
-
Import any driver-specific types you need directly from your runtime's SQLite module.
|
|
48
|
+
This is a good fit for local development, embedded deployments, and single-node services. Import any driver-specific types you need directly from your runtime's SQLite module.
|
|
51
49
|
|
|
52
50
|
## Adapter Capabilities
|
|
53
51
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# fetch-proxy
|
|
2
2
|
|
|
3
|
-
HTTP proxy utilities built on the web [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API).
|
|
4
|
-
Use `fetch-proxy` to create `fetch` handlers that forward requests to target servers while optionally rewriting headers and cookies.
|
|
3
|
+
HTTP proxy utilities built on the web [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). Use `fetch-proxy` to create `fetch` handlers that forward requests to target servers while optionally rewriting headers and cookies.
|
|
5
4
|
|
|
6
5
|
## Features
|
|
7
6
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# file-storage-s3
|
|
2
2
|
|
|
3
|
-
S3 backend for [`remix/file-storage`](https://github.com/remix-run/remix/tree/main/packages/file-storage).
|
|
4
|
-
Use this package when you want the `FileStorage` API backed by AWS S3 or an S3-compatible provider.
|
|
3
|
+
S3 backend for [`remix/file-storage`](https://github.com/remix-run/remix/tree/main/packages/file-storage). Use this package when you want the `FileStorage` API backed by AWS S3 or an S3-compatible provider.
|
|
5
4
|
|
|
6
5
|
## Features
|
|
7
6
|
|
|
@@ -69,8 +69,7 @@ let router = createRouter({
|
|
|
69
69
|
|
|
70
70
|
### Limit Multipart Growth
|
|
71
71
|
|
|
72
|
-
`formData()` forwards multipart limit options to `parseFormData()`, so you can cap uploads with
|
|
73
|
-
`maxHeaderSize`, `maxFiles`, `maxFileSize`, `maxParts`, and `maxTotalSize`.
|
|
72
|
+
`formData()` forwards multipart limit options to `parseFormData()`, so you can cap uploads with `maxHeaderSize`, `maxFiles`, `maxFileSize`, `maxParts`, and `maxTotalSize`.
|
|
74
73
|
|
|
75
74
|
```ts
|
|
76
75
|
let router = createRouter({
|
|
@@ -72,8 +72,7 @@ async function requestHandler(request: Request) {
|
|
|
72
72
|
}
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
To validate the resulting `FormData` object with `remix/data-schema`, use the
|
|
76
|
-
`remix/data-schema/form-data` helpers.
|
|
75
|
+
To validate the resulting `FormData` object with `remix/data-schema`, use the `remix/data-schema/form-data` helpers.
|
|
77
76
|
|
|
78
77
|
To limit the overall shape of multipart requests, use the `maxHeaderSize`, `maxFileSize`, `maxFiles`, `maxParts`, and `maxTotalSize` options. By default, `parseFormData()` uses `maxFiles = 20`, `maxParts = 1000`, and `maxTotalSize = maxFiles * maxFileSize + 1 MiB`.
|
|
79
78
|
|
|
@@ -150,8 +149,7 @@ The [`demos` directory](https://github.com/remix-run/remix/tree/main/packages/fo
|
|
|
150
149
|
|
|
151
150
|
## Related Packages
|
|
152
151
|
|
|
153
|
-
- [`data-schema`](https://github.com/remix-run/remix/tree/main/packages/data-schema) - Tiny,
|
|
154
|
-
standards-aligned validation with a `form-data` export for `FormData` and `URLSearchParams`
|
|
152
|
+
- [`data-schema`](https://github.com/remix-run/remix/tree/main/packages/data-schema) - Tiny, standards-aligned validation with a `form-data` export for `FormData` and `URLSearchParams`
|
|
155
153
|
- [`file-storage`](https://github.com/remix-run/remix/tree/main/packages/file-storage) - A simple key/value interface for storing `FileUpload` objects you get from the parser
|
|
156
154
|
- [`multipart-parser`](https://github.com/remix-run/remix/tree/main/packages/multipart-parser) - The parser used internally for parsing `multipart/form-data` HTTP messages
|
|
157
155
|
|
package/src/headers/README.md
CHANGED
|
@@ -94,6 +94,16 @@ The following headers are currently supported:
|
|
|
94
94
|
- [Set-Cookie](./README.md#set-cookie)
|
|
95
95
|
- [Vary](./README.md#vary)
|
|
96
96
|
|
|
97
|
+
If you only need a specific header parser (for example, just `Content-Type`), import that parser directly from its subpath. This avoids pulling the package barrel and `SuperHeaders`:
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
import { ContentType } from 'remix/headers/content-type'
|
|
101
|
+
import { SetCookie } from 'remix/headers/set-cookie'
|
|
102
|
+
|
|
103
|
+
let contentType = ContentType.from('text/plain; charset=utf-8')
|
|
104
|
+
let setCookie = new SetCookie('session=abc; Path=/')
|
|
105
|
+
```
|
|
106
|
+
|
|
97
107
|
### Accept
|
|
98
108
|
|
|
99
109
|
Parse, manipulate and stringify [`Accept` headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept).
|
|
@@ -124,8 +124,7 @@ async function handler(request: Request) {
|
|
|
124
124
|
|
|
125
125
|
### Custom Hostname Configuration
|
|
126
126
|
|
|
127
|
-
Configure custom hostnames for deployment on VPS or custom environments. `node-fetch-server` uses
|
|
128
|
-
the `host` option when constructing `request.url`.
|
|
127
|
+
Configure custom hostnames for deployment on VPS or custom environments. `node-fetch-server` uses the `host` option when constructing `request.url`.
|
|
129
128
|
|
|
130
129
|
```ts
|
|
131
130
|
import * as http from 'node:http'
|
package/src/node-tsx/README.md
CHANGED
|
@@ -2,13 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Run Node.js with TypeScript and JSX syntax support in `.ts`, `.tsx`, and `.jsx` files.
|
|
4
4
|
|
|
5
|
-
`node-tsx` transforms supported source files before Node.js executes them, including
|
|
6
|
-
TypeScript syntax that requires JavaScript code generation such as enums, runtime
|
|
7
|
-
namespaces, and parameter properties. JSX compiler options are read from the nearest
|
|
8
|
-
`tsconfig.json` for each loaded file.
|
|
5
|
+
`node-tsx` transforms supported source files before Node.js executes them, including TypeScript syntax that requires JavaScript code generation such as enums, runtime namespaces, and parameter properties. JSX compiler options are read from the nearest `tsconfig.json` for each loaded file.
|
|
9
6
|
|
|
10
|
-
The loader does not type check, change Node.js module resolution, apply TypeScript
|
|
11
|
-
path aliases, or downlevel JavaScript syntax for older runtimes.
|
|
7
|
+
The loader does not type check, change Node.js module resolution, apply TypeScript path aliases, or downlevel JavaScript syntax for older runtimes.
|
|
12
8
|
|
|
13
9
|
## Installation
|
|
14
10
|
|
|
@@ -47,8 +43,7 @@ Since import resolution still follows Node.js, configure type checking to match
|
|
|
47
43
|
- [`verbatimModuleSyntax`](https://www.typescriptlang.org/tsconfig/#verbatimModuleSyntax) requires type-only imports and exports to be marked so runtime imports are unambiguous.
|
|
48
44
|
- [`rewriteRelativeImportExtensions`](https://www.typescriptlang.org/tsconfig/#rewriteRelativeImportExtensions) preserves a `tsc` emit path by rewriting relative `.ts` and `.tsx` imports to JavaScript extensions.
|
|
49
45
|
|
|
50
|
-
Do not enable `erasableSyntaxOnly` if you want TypeScript to accept the same
|
|
51
|
-
transform-only syntax that `node-tsx` can execute.
|
|
46
|
+
Do not enable `erasableSyntaxOnly` if you want TypeScript to accept the same transform-only syntax that `node-tsx` can execute.
|
|
52
47
|
|
|
53
48
|
### Programmatic usage
|
|
54
49
|
|
|
@@ -55,15 +55,13 @@ createHref('http(s)://:region.cdn.com/assets/*file.:ext', {
|
|
|
55
55
|
|
|
56
56
|
## API at a glance
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
**remix/route-pattern/specificity** - Rank matches by [specificity](#ranking-matches-by-specificity).
|
|
58
|
+
| Import | Description |
|
|
59
|
+
| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
|
60
|
+
| `remix/route-pattern` | Parse and stringify patterns. |
|
|
61
|
+
| `remix/route-pattern/href` | Generate hrefs for patterns with type safe params. |
|
|
62
|
+
| `remix/route-pattern/match` | Match against one pattern with type inference for params, or match against many patterns with deterministic ranking and attached data. |
|
|
63
|
+
| `remix/route-pattern/join` | Combine two patterns into one. Override protocol, hostname, port. Join pathnames. Merge search constraints. |
|
|
64
|
+
| `remix/route-pattern/specificity` | Rank matches by [specificity](#ranking-matches-by-specificity). |
|
|
67
65
|
|
|
68
66
|
For in-depth reference, visit the [`route-pattern` API docs](https://api.remix.run/api/remix/route-pattern)
|
|
69
67
|
|
|
@@ -113,6 +111,15 @@ While variables, wilcards, and optionals are most prevalent in pathnames, you ca
|
|
|
113
111
|
'(:locale.)example.com/docs(/:section)' // matches en.example.com/docs, en.example.com/docs/guides
|
|
114
112
|
```
|
|
115
113
|
|
|
114
|
+
**Escape characters** with `\`:
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
'time/12\\:30' // matches /time/12:30
|
|
118
|
+
'calculator/2\\*3' // matches /calculator/2*3
|
|
119
|
+
'wiki/Mercury_\\(planet\\)' // matches /wiki/Mercury_(planet)
|
|
120
|
+
'wiki/AC\\/DC' // matches /wiki/AC%2FDC
|
|
121
|
+
```
|
|
122
|
+
|
|
116
123
|
### Search
|
|
117
124
|
|
|
118
125
|
**Search constraints** narrow matches using `?key` or `?key=value`:
|
|
@@ -175,8 +182,7 @@ When multiple patterns match the same URL, `route-pattern` chooses the most spec
|
|
|
175
182
|
|
|
176
183
|
This is the same ranking used by `createMultiMatcher`.
|
|
177
184
|
|
|
178
|
-
For advanced use cases, `/specificity` provides comparison utilities: `lessThan`, `greaterThan`, `equal`, `descending`, `ascending`, `compare`.
|
|
179
|
-
For example:
|
|
185
|
+
For advanced use cases, `/specificity` provides comparison utilities: `lessThan`, `greaterThan`, `equal`, `descending`, `ascending`, `compare`. For example:
|
|
180
186
|
|
|
181
187
|
```ts
|
|
182
188
|
import { createMultiMatcher } from 'remix/route-pattern/match'
|
|
@@ -195,8 +201,7 @@ matches.sort(descending).map((match) => match.pattern.toString())
|
|
|
195
201
|
|
|
196
202
|
## Generate hrefs
|
|
197
203
|
|
|
198
|
-
`createHref` turns a pattern and params into a URL string.
|
|
199
|
-
Required variables and wildcards must be provided, while params inside optional groups may be omitted.
|
|
204
|
+
`createHref` turns a pattern and params into a URL string. Required variables and wildcards must be provided, while params inside optional groups may be omitted.
|
|
200
205
|
|
|
201
206
|
```ts
|
|
202
207
|
import { createHref } from 'remix/route-pattern/href'
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# session-storage-redis
|
|
2
2
|
|
|
3
|
-
Redis-backed session storage for [`remix/session`](https://github.com/remix-run/remix/tree/main/packages/session).
|
|
4
|
-
Use this package when app servers need to share session state through Redis.
|
|
3
|
+
Redis-backed session storage for [`remix/session`](https://github.com/remix-run/remix/tree/main/packages/session). Use this package when app servers need to share session state through Redis.
|
|
5
4
|
|
|
6
5
|
## Installation
|
|
7
6
|
|
package/src/test/README.md
CHANGED
|
@@ -209,8 +209,7 @@ suite('My Test Suite', () => {
|
|
|
209
209
|
|
|
210
210
|
### Programmatic runner
|
|
211
211
|
|
|
212
|
-
`remix/test/cli` exports `runRemixTest()` for tools that want to run the test runner without
|
|
213
|
-
exiting the current process:
|
|
212
|
+
`remix/test/cli` exports `runRemixTest()` for tools that want to run the test runner without exiting the current process:
|
|
214
213
|
|
|
215
214
|
```ts
|
|
216
215
|
import { runRemixTest } from 'remix/test/cli'
|
|
@@ -221,9 +220,7 @@ let exitCode = await runRemixTest({
|
|
|
221
220
|
})
|
|
222
221
|
```
|
|
223
222
|
|
|
224
|
-
`runRemixTest()` returns the runner exit code. The `remix test` bin wrapper calls
|
|
225
|
-
`process.exit()` with that code when the run finishes so open workers, browsers, or project handles
|
|
226
|
-
cannot keep the CLI alive.
|
|
223
|
+
`runRemixTest()` returns the runner exit code. The `remix test` bin wrapper calls `process.exit()` with that code when the run finishes so open workers, browsers, or project handles cannot keep the CLI alive.
|
|
227
224
|
|
|
228
225
|
### Test Context
|
|
229
226
|
|
package/src/ui/anchor/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# anchor
|
|
2
2
|
|
|
3
|
-
`anchor` positions a floating element against an anchor element and keeps it constrained to the viewport. Use it for custom floating surfaces that need placement, flipping, offsets, and optional relative alignment.
|
|
3
|
+
`anchor` positions a floating element against an anchor element or viewport coordinates and keeps it constrained to the viewport. Use it for custom floating surfaces that need placement, flipping, offsets, and optional relative alignment.
|
|
4
4
|
|
|
5
5
|
## Usage
|
|
6
6
|
|
|
@@ -49,11 +49,19 @@ popover.addEventListener('beforetoggle', (event) => {
|
|
|
49
49
|
})
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
+
Anchor to coordinates when the surface should open at a pointer location.
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
let cleanup = anchor(popover, { x: event.clientX, y: event.clientY }, { placement: 'bottom-start' })
|
|
56
|
+
```
|
|
57
|
+
|
|
52
58
|
## `anchor.*`
|
|
53
59
|
|
|
54
|
-
- `anchor(floatingElement,
|
|
60
|
+
- `anchor(floatingElement, anchorTarget, options)`: positions `floatingElement` against an element or coordinate target, starts animation-frame polling for geometry changes, and returns a cleanup function.
|
|
55
61
|
- `AnchorOptions`: placement, inset, relative alignment, and offset options.
|
|
62
|
+
- `AnchorPoint`: viewport coordinate target with `x`, `y`, and optional `width`/`height`.
|
|
56
63
|
- `AnchorPlacement`: exported placement names for the main sides and top/bottom start/end alignment.
|
|
64
|
+
- `AnchorTarget`: an `HTMLElement` or `AnchorPoint`.
|
|
57
65
|
|
|
58
66
|
## Placements
|
|
59
67
|
|
|
@@ -149,5 +157,5 @@ anchor(listbox, trigger, {
|
|
|
149
157
|
- Oversized inset surfaces with `relativeTo` preserve alignment by scrolling the nearest scrollable descendant when possible.
|
|
150
158
|
- `offset`, `offsetX`, and `offsetY` may be numbers or functions that receive the floating element.
|
|
151
159
|
- `relativeTo` lets a surface align to an inner element, which is useful for selected options inside popovers.
|
|
152
|
-
- `anchor` polls on animation frames for anchor or floating geometry changes and repositions when either changes.
|
|
160
|
+
- `anchor` polls on animation frames for anchor target or floating geometry changes and repositions when either changes.
|
|
153
161
|
- The returned cleanup function cancels animation-frame polling.
|
package/src/ui/menu/README.md
CHANGED
|
@@ -68,6 +68,29 @@ Use `menuLabel` when the menu surface needs a different accessible label from th
|
|
|
68
68
|
</Menu>
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
+
Use `menu.contextTrigger()` with `menu.Context` and `MenuList` when a menu should open at the right-click location of an element.
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
import * as menu from 'remix/ui/menu'
|
|
75
|
+
import { MenuItem, MenuList } from 'remix/ui/menu'
|
|
76
|
+
|
|
77
|
+
export function FileContextMenu(handle: Handle) {
|
|
78
|
+
return () => (
|
|
79
|
+
<menu.Context label="File actions">
|
|
80
|
+
<div mix={menu.contextTrigger()} tabIndex={0}>
|
|
81
|
+
File.txt
|
|
82
|
+
</div>
|
|
83
|
+
<MenuList>
|
|
84
|
+
<MenuItem name="rename">Rename</MenuItem>
|
|
85
|
+
<MenuItem name="delete">Delete</MenuItem>
|
|
86
|
+
</MenuList>
|
|
87
|
+
</menu.Context>
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Attach `onMenuSelect(...)` to `MenuList` or a shared ancestor when using lower-level context menu composition.
|
|
93
|
+
|
|
71
94
|
## `menu.*`
|
|
72
95
|
|
|
73
96
|
- `Menu`: composed trigger, popover, and list component for the common menu case.
|
|
@@ -77,13 +100,14 @@ Use `menuLabel` when the menu surface needs a different accessible label from th
|
|
|
77
100
|
- `onMenuSelect(...)`: event mixin for the bubbling `MenuSelectEvent`.
|
|
78
101
|
- `MenuSelectEvent`: bubbling event class whose `item` describes the selected item.
|
|
79
102
|
- `MenuSelectItem`: selected item shape with `checked`, `id`, `label`, `name`, `type`, and `value`.
|
|
80
|
-
- `menu.Context`, `menu.trigger()`, `menu.popover()`, `menu.list()`, `menu.item(...)`, and `menu.submenuTrigger(...)`: lower-level composition primitives.
|
|
103
|
+
- `menu.Context`, `menu.trigger()`, `menu.contextTrigger()`, `menu.popover()`, `menu.list()`, `menu.item(...)`, and `menu.submenuTrigger(...)`: lower-level composition primitives.
|
|
81
104
|
- `buttonStyle`, `popoverStyle`, `listStyle`, `itemStyle`, `itemSlotStyle`, `itemLabelStyle`, `itemGlyphStyle`, and `triggerGlyphStyle`: flat style mixins used by the wrappers.
|
|
82
|
-
- `MenuProps`, `MenuItemProps`, `MenuListProps`, `MenuProviderProps`, `MenuTriggerOptions`, `MenuItemOptions`, and `SubmenuProps`: public TypeScript props and option types.
|
|
105
|
+
- `MenuProps`, `MenuItemProps`, `MenuListProps`, `MenuProviderProps`, `MenuTriggerOptions`, `MenuContextTriggerOptions`, `MenuItemOptions`, and `SubmenuProps`: public TypeScript props and option types.
|
|
83
106
|
|
|
84
107
|
## Behavior Notes
|
|
85
108
|
|
|
86
109
|
- Click opens the root menu and focuses the list; clicking the trigger again closes it and restores focus.
|
|
110
|
+
- `menu.contextTrigger()` opens the root menu from a `contextmenu` event at the pointer coordinates and supports keyboard opening with the Context Menu key or Shift+F10.
|
|
87
111
|
- `ArrowDown` opens from the trigger at the first enabled item. `ArrowUp` opens at the last enabled item. Enter and Space open the menu with focus on the list.
|
|
88
112
|
- Keyboard navigation skips disabled items and does not wrap past the first or last enabled item.
|
|
89
113
|
- `Home` and `End` move to the first and last enabled item. Enter and Space activate the highlighted item.
|