@graview/primitives 0.1.36 → 0.1.37
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/app-link-door.d.ts +17 -2
- package/dist/app-link-door.d.ts.map +1 -1
- package/dist/app-link-door.js +0 -6
- package/dist/app-link-door.js.map +1 -1
- package/dist/app-link.d.ts +2 -15
- package/dist/app-link.d.ts.map +1 -1
- package/dist/app-link.js.map +1 -1
- package/dist/files.d.ts +6 -2
- package/dist/files.d.ts.map +1 -1
- package/dist/files.js +88 -22
- package/dist/files.js.map +1 -1
- package/dist/first-look-door.d.ts +8 -0
- package/dist/first-look-door.d.ts.map +1 -1
- package/dist/first-look-door.js.map +1 -1
- package/dist/first-look.d.ts +1 -8
- package/dist/first-look.d.ts.map +1 -1
- package/dist/first-look.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/kind-stop.d.ts.map +1 -1
- package/dist/kind-stop.js +2 -2
- package/dist/kind-stop.js.map +1 -1
- package/dist/notify-door.d.ts +1 -19
- package/dist/notify-door.d.ts.map +1 -1
- package/dist/notify-door.js.map +1 -1
- package/dist/notify-sheet.d.ts +8 -1
- package/dist/notify-sheet.d.ts.map +1 -1
- package/dist/notify-sheet.js.map +1 -1
- package/dist/pages.d.ts +1 -1
- package/dist/pages.d.ts.map +1 -1
- package/dist/primitives/index.d.ts.map +1 -1
- package/dist/primitives/index.js +10 -0
- package/dist/primitives/index.js.map +1 -1
- package/dist/primitives/measure.d.ts.map +1 -1
- package/dist/primitives/measure.js +14 -1
- package/dist/primitives/measure.js.map +1 -1
- package/dist/scene.d.ts +1 -1
- package/dist/scene.d.ts.map +1 -1
- package/dist/workbench/activity.d.ts +6 -2
- package/dist/workbench/activity.d.ts.map +1 -1
- package/dist/workbench/activity.js +55 -31
- package/dist/workbench/activity.js.map +1 -1
- package/dist/workbench/answer-args.js +1 -1
- package/dist/workbench/answer-args.js.map +1 -1
- package/package.json +6 -6
package/dist/app-link-door.d.ts
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import type { CSSProperties } from "react";
|
|
2
|
+
/** What draws one app-link field: the address, how it is drawn, and the record and field it is the value of. */
|
|
3
|
+
export interface AppLinkProps {
|
|
4
|
+
/** The address the record keeps. */
|
|
5
|
+
readonly address: string;
|
|
6
|
+
/** `record`: the name and the home's headline under it; `chip`: the name alone, on a row or a card. */
|
|
7
|
+
readonly as?: "record" | "chip";
|
|
8
|
+
/**
|
|
9
|
+
* The record and field it is the value of, where it may be changed in
|
|
10
|
+
* place: the control that changes it stands beside the link, never
|
|
11
|
+
* around it, and only where an act may write it.
|
|
12
|
+
*/
|
|
13
|
+
readonly nodeId?: string;
|
|
14
|
+
readonly field?: string;
|
|
15
|
+
readonly style?: CSSProperties;
|
|
16
|
+
}
|
|
17
|
+
export declare const AppLink: import("react").ComponentType<AppLinkProps>;
|
|
3
18
|
//# sourceMappingURL=app-link-door.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-link-door.d.ts","sourceRoot":"","sources":["../src/app-link-door.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"app-link-door.d.ts","sourceRoot":"","sources":["../src/app-link-door.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAS3C,gHAAgH;AAChH,MAAM,WAAW,YAAY;IAC3B,oCAAoC;IACpC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,uGAAuG;IACvG,QAAQ,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IAChC;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC;CAChC;AAID,eAAO,MAAM,OAAO,6CAAmH,CAAC"}
|
package/dist/app-link-door.js
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
import { retryingImport } from "@graview/core/retry";
|
|
2
2
|
import { lazyModule } from "@graview/react/provider";
|
|
3
|
-
/*
|
|
4
|
-
* THE DOOR TO ANOTHER APP ON A RECORD (FR-171): what draws an app-link
|
|
5
|
-
* field as the app the host's `resolveApp` names — fetched with the first
|
|
6
|
-
* one drawn, never in the first paint, and never at all for an app whose
|
|
7
|
-
* records link none.
|
|
8
|
-
*/
|
|
9
3
|
const APP_LINK = /* @__PURE__ */ lazyModule(retryingImport(() => import("./app-link.js")));
|
|
10
4
|
export const AppLink = APP_LINK.part((m, props) => m.drawAppLink(props), { quiet: true });
|
|
11
5
|
//# sourceMappingURL=app-link-door.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-link-door.js","sourceRoot":"","sources":["../src/app-link-door.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"app-link-door.js","sourceRoot":"","sources":["../src/app-link-door.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AA0BrD,MAAM,QAAQ,GAAG,eAAe,CAAC,UAAU,CAAC,cAAc,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;AAE3F,MAAM,CAAC,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAiC,EAAE,KAAmB,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC"}
|
package/dist/app-link.d.ts
CHANGED
|
@@ -1,19 +1,6 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import type { AppLinkProps } from "./app-link-door.js";
|
|
2
3
|
import "./frame.js";
|
|
3
|
-
export interface AppLinkProps {
|
|
4
|
-
/** The address the record keeps. */
|
|
5
|
-
readonly address: string;
|
|
6
|
-
/** `record`: the name and the home's headline under it; `chip`: the name alone, on a row or a card. */
|
|
7
|
-
readonly as?: "record" | "chip";
|
|
8
|
-
/**
|
|
9
|
-
* The record and field it is the value of, where it may be changed in
|
|
10
|
-
* place: the control that changes it stands beside the link, never
|
|
11
|
-
* around it, and only where an act may write it.
|
|
12
|
-
*/
|
|
13
|
-
readonly nodeId?: string;
|
|
14
|
-
readonly field?: string;
|
|
15
|
-
readonly style?: CSSProperties;
|
|
16
|
-
}
|
|
17
4
|
export declare function AppLink({ address, as, nodeId, field, style }: AppLinkProps): ReactNode;
|
|
18
5
|
/** The part the door draws. */
|
|
19
6
|
export declare const drawAppLink: (props: AppLinkProps) => ReactNode;
|
package/dist/app-link.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-link.d.ts","sourceRoot":"","sources":["../src/app-link.tsx"],"names":[],"mappings":"AAGA,OAAO,
|
|
1
|
+
{"version":3,"file":"app-link.d.ts","sourceRoot":"","sources":["../src/app-link.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAuD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAC5F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGvD,OAAO,YAAY,CAAC;AA+GpB,wBAAgB,OAAO,CAAC,EAAE,OAAO,EAAE,EAAW,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,YAAY,GAAG,SAAS,CAyD/F;AAmBD,+BAA+B;AAC/B,eAAO,MAAM,WAAW,GAAI,OAAO,YAAY,KAAG,SAAmC,CAAC"}
|
package/dist/app-link.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-link.js","sourceRoot":"","sources":["../src/app-link.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAuD,MAAM,eAAe,CAAC;AAC5H,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAsC,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"app-link.js","sourceRoot":"","sources":["../src/app-link.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAuD,MAAM,eAAe,CAAC;AAC5H,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAsC,MAAM,OAAO,CAAC;AAE5F,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,YAAY,CAAC,CAAC,2FAA2F;AAiBhH;;;;GAIG;AACH,MAAM,KAAK,GAAG,IAAI,OAAO,EAAmD,CAAC;AAC7E,MAAM,QAAQ,GAAG,IAAI,OAAO,EAA0C,CAAC;AACvE,MAAM,KAAK,GAAG,CAAC,OAAe,EAAE,MAAc,EAAE,EAAE,CAAC,GAAG,MAAM,CAAC,IAAI,SAAS,MAAM,CAAC,EAAE,IAAI,EAAE,SAAS,OAAO,EAAE,CAAC;AAE5G,SAAS,GAAG,CAAC,OAAmB,EAAE,OAAe,EAAE,MAAc;IAC/D,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACnC,IAAI,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC/B,IAAI,CAAC,KAAK;QAAE,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,KAAK,GAAG,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC;IACpD,IAAI,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC5B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,2HAA2H;QAC3H,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE;aACvB,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;aACpC,IAAI,CACH,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EACrC,GAAG,EAAE,CAAC,IAAI,CACX;aACA,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE;YACZ,IAAI,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACrC,IAAI,CAAC,QAAQ;gBAAE,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,QAAQ,GAAG,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC;YAC7D,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YACvB,OAAO,GAAG,CAAC;QACb,CAAC,CAAC,CAAC;QACL,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACzB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,sHAAsH;AACtH,SAAS,QAAQ,CAAC,GAAY;IAC5B,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC9B,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAC/D,IAAK,GAA4B,CAAC,MAAM,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC/D,MAAM,KAAK,GAAG,GAAyB,CAAC;IACxC,OAAO,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC;AACtG,CAAC;AAED,6GAA6G;AAC7G,SAAS,YAAY,CAAC,OAAe;IACnC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,SAAS,CAAC;IACrD,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,8FAA8F;AAC9F,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAsB,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;AAErH,2HAA2H;AAC3H,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAsB,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;AAE7I,MAAM,MAAM,GAAW,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AACzC,MAAM,KAAK,GAAkB,EAAE,KAAK,EAAE,0BAA0B,EAAE,CAAC;AAEnE,SAAS,IAAI,CAAC,EAAE,IAAI,EAAyC;IAC3D,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC3B,IAAI,GAAG;QAAE,OAAO,cAAK,GAAG,EAAE,GAAG,EAAE,GAAG,EAAC,EAAE,EAAC,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,2BAAwB,EAAE,EAAC,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,YAAY,EAAE,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,GAAI,CAAC;IAClL,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACpD,OAAO,KAAK,CAAC,CAAC,CAAC,CACb,8BAAkB,MAAM,2BAAuB,EAAE,EAAC,KAAK,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,YAC9F,KAAK,GACD,CACR,CAAC,CAAC,CAAC,IAAI,CAAC;AACX,CAAC;AAED,qEAAqE;AACrE,SAAS,YAAY,CAAC,OAAe;IACnC,MAAM,OAAO,GAAG,eAAe,EAAE,CAAC;IAClC,wFAAwF;IACxF,MAAM,OAAO,GAAG,UAAU,CAAC,iBAAiB,CAAC,IAAI,OAAO,EAAE,UAAU,CAAC;IACrE,MAAM,MAAM,GAAW,OAAO,EAAE,SAAS,IAAI,MAAM,CAAC;IACpD,MAAM,KAAK,GAAG,GAAW,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAChH,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAS,KAAK,CAAC,CAAC;IACpD,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,GAAG,GAAG,KAAK,EAAE,CAAC;QACpB,SAAS,CAAC,GAAG,CAAC,CAAC;QACf,IAAI,GAAG,KAAK,QAAQ,IAAI,CAAC,OAAO;YAAE,OAAO;QACzC,IAAI,IAAI,GAAG,IAAI,CAAC;QAChB,KAAK,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,IAAI,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;QACzE,OAAO,GAAG,EAAE;YACV,IAAI,GAAG,KAAK,CAAC;QACf,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;IAC/C,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,EAAE,OAAO,EAAE,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAgB;IAClF,MAAM,GAAG,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;IAClC,oGAAoG;IACpG,MAAM,IAAI,GAAkB,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,YAAY,CAAC,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;IACrJ,IAAI,KAAgB,CAAC;IACrB,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;QACrB,KAAK,GAAG,CACN,wCAA4B,EAAE,4BAAwB,QAAQ,eAAW,MAAM,EAAC,KAAK,EAAE,EAAE,GAAG,KAAK,EAAE,GAAG,IAAI,EAAE,2BAErG,CACR,CAAC;IACJ,CAAC;SAAM,IAAI,GAAG,IAAI,QAAQ,IAAI,GAAG,EAAE,CAAC;QAClC,sGAAsG;QACtG,KAAK,GAAG,CACN,wCAA4B,EAAE,4BAAwB,QAAQ,EAAC,KAAK,EAAE,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,uCAEzG,CACR,CAAC;IACJ,CAAC;SAAM,IAAI,CAAC,GAAG,EAAE,CAAC;QAChB,uEAAuE;QACvE,MAAM,IAAI,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;QACnC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CACb,qCAAyB,EAAE,4BAAwB,OAAO,EAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAC,qBAAqB,EAAC,MAAM,EAAC,QAAQ,EAAC,KAAK,EAAE,EAAE,KAAK,EAAE,uBAAuB,EAAE,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,EAAE,GAAG,IAAI,EAAE,YAC7N,OAAO,GACN,CACL,CAAC,CAAC,CAAC,CACF,wCAA4B,EAAE,4BAAwB,OAAO,EAAC,KAAK,EAAE,IAAI,YACtE,OAAO,GACH,CACR,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAChC,MAAM,QAAQ,GAAG,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QACpE,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC;QAChC,KAAK,GAAG,CACN,MAAC,GAAG,6BACoB,EAAE,4BACD,OAAO,KAC1B,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KACtB,CAAC,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAC9D,KAAK,EAAE,EAAE,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,QAAQ,EAAE,SAAS,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,mBAAmB,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,aAE3J,KAAC,IAAI,IAAC,IAAI,EAAE,GAAG,CAAC,IAAI,GAAI,EACxB,gBAAM,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,aAC3C,wCAA4B,EAAE,EAAC,KAAK,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,YACtD,GAAG,CAAC,IAAI,GACJ,EACN,QAAQ,CAAC,CAAC,CAAC,CACV,4CAAgC,EAAE,EAAC,KAAK,EAAE,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,YAC1F,QAAQ,GACJ,CACR,CAAC,CAAC,CAAC,IAAI,IACH,IACH,CACP,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,KAAC,OAAO,IAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,GAAI,CAAC,CAAC,CAAC,KAAK,CAAC;AAC7G,CAAC;AAED,+FAA+F;AAC/F,SAAS,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAA4G;IAC1J,MAAM,QAAQ,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC;IACrF,MAAM,KAAK,GAAG,eAAe,EAAE,EAAE,KAAK,CAAC;IACvC,IAAI,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5B,8FAA8F;IAC9F,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;IACxI,OAAO,CACL,gBAAM,KAAK,EAAE,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,aAC9H,KAAK,EACN,eAAM,KAAK,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE,YACpC,KAAC,aAAa,IAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAC,QAAQ,EAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,IAAI,EAAE,GAAI,GACtG,IACF,CACR,CAAC;AACJ,CAAC;AAED,+BAA+B;AAC/B,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,KAAmB,EAAa,EAAE,CAAC,KAAC,OAAO,OAAK,KAAK,GAAI,CAAC"}
|
package/dist/files.d.ts
CHANGED
|
@@ -32,12 +32,13 @@ interface Prepared {
|
|
|
32
32
|
*/
|
|
33
33
|
export declare function prepare(file: File, host: HostFiles, options: {
|
|
34
34
|
readonly image: boolean;
|
|
35
|
+
readonly video?: boolean;
|
|
35
36
|
readonly camera?: boolean;
|
|
36
37
|
}): Promise<Prepared | {
|
|
37
38
|
readonly refused: string;
|
|
38
39
|
}>;
|
|
39
40
|
/** Send one prepared file and read what the host answered: the value, or why it was not kept. */
|
|
40
|
-
export declare function send(prepared: Prepared, host: HostFiles, image: boolean): Promise<FileValue | {
|
|
41
|
+
export declare function send(prepared: Prepared, host: HostFiles, image: boolean, video?: boolean): Promise<FileValue | {
|
|
41
42
|
readonly refused: string;
|
|
42
43
|
}>;
|
|
43
44
|
export interface FilesEditorProps {
|
|
@@ -49,6 +50,8 @@ export interface FilesEditorProps {
|
|
|
49
50
|
readonly several: boolean;
|
|
50
51
|
/** Only pictures will do. */
|
|
51
52
|
readonly image: boolean;
|
|
53
|
+
/** Only videos will do (0.1.37). */
|
|
54
|
+
readonly video?: boolean;
|
|
52
55
|
/** The field, in the declaration's words. */
|
|
53
56
|
readonly label: string;
|
|
54
57
|
/** Each file small beside its name: where nothing else draws them (a form). Not under a gallery that already does. */
|
|
@@ -60,7 +63,7 @@ export interface FilesEditorProps {
|
|
|
60
63
|
* the host takes, what it refused and why; without a host's files it says
|
|
61
64
|
* this host takes none, and offers no way to add one.
|
|
62
65
|
*/
|
|
63
|
-
export declare function FilesEditor({ value, onChange, several, image, label, thumbs }: FilesEditorProps): import("react").JSX.Element;
|
|
66
|
+
export declare function FilesEditor({ value, onChange, several, image, video, label, thumbs }: FilesEditorProps): import("react").JSX.Element;
|
|
64
67
|
export interface FileRowProps {
|
|
65
68
|
readonly nodeId: string;
|
|
66
69
|
readonly field: string;
|
|
@@ -105,6 +108,7 @@ export type FilesProps = ({
|
|
|
105
108
|
readonly onChange: (next: unknown) => void;
|
|
106
109
|
readonly several: boolean;
|
|
107
110
|
readonly image: boolean;
|
|
111
|
+
readonly video?: boolean;
|
|
108
112
|
readonly label: string;
|
|
109
113
|
readonly title?: string;
|
|
110
114
|
};
|
package/dist/files.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../src/files.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../src/files.tsx"],"names":[],"mappings":"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"}
|
package/dist/files.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { fieldWords, fileSrc, filesOf, isFileValue, isImage, LOCAL_LAYERS } from "@graview/core";
|
|
3
|
-
import { fileFieldsOf, fileWords } from "@graview/core/blocks";
|
|
2
|
+
import { fieldWords, fileSrc, filesOf, isFileValue, isImage, isVideo, LOCAL_LAYERS, videoWords } from "@graview/core";
|
|
3
|
+
import { fileFieldsOf, fileWords, videoClock } from "@graview/core/blocks";
|
|
4
4
|
import { useEditableFields } from "@graview/react/drawing";
|
|
5
5
|
import { useGraview, useHostFiles, useNode, usePlayback } from "@graview/react/provider";
|
|
6
6
|
import { useEffect, useId, useRef, useState } from "react";
|
|
@@ -49,7 +49,7 @@ export function typeWords(file) {
|
|
|
49
49
|
}
|
|
50
50
|
/** The kinds a host takes, in words: "pictures and PDF files". */
|
|
51
51
|
export function kindsWords(kinds) {
|
|
52
|
-
const said = [...new Set(kinds.map((kind) => (kind === "image/*" ? "pictures" : kind === "*/*" || kind === "*" ? "any file" : `${typeWords({ type: kind })} files`)))];
|
|
52
|
+
const said = [...new Set(kinds.map((kind) => (kind === "image/*" ? "pictures" : kind === "video/*" ? "videos" : kind === "*/*" || kind === "*" ? "any file" : `${typeWords({ type: kind })} files`)))];
|
|
53
53
|
return said.length <= 1 ? (said[0] ?? "no files") : `${said.slice(0, -1).join(", ")} and ${said[said.length - 1]}`;
|
|
54
54
|
}
|
|
55
55
|
/** Whether a host's kinds take a type: the type itself, its family (`image/*`), or anything. */
|
|
@@ -61,6 +61,9 @@ export function takes(kinds, type) {
|
|
|
61
61
|
}
|
|
62
62
|
const HEIC = /^image\/hei[cf]$|\.hei[cf]$/i;
|
|
63
63
|
const isHeic = (file) => HEIC.test(file.type) || HEIC.test(file.name);
|
|
64
|
+
/** A video's type from its name's ending, where the browser said none (a phone's `.MOV` often comes untyped). */
|
|
65
|
+
const VIDEO_ENDINGS = { mov: "video/quicktime", mp4: "video/mp4", m4v: "video/mp4", webm: "video/webm", ogv: "video/ogg" };
|
|
66
|
+
const videoTypeOf = (name) => VIDEO_ENDINGS[/\.([a-z0-9]{2,4})$/i.exec(name)?.[1]?.toLowerCase() ?? ""];
|
|
64
67
|
/**
|
|
65
68
|
* A FILE MADE READY TO SEND, or the reason it cannot be, in words.
|
|
66
69
|
*
|
|
@@ -71,11 +74,14 @@ const isHeic = (file) => HEIC.test(file.type) || HEIC.test(file.name);
|
|
|
71
74
|
*/
|
|
72
75
|
export async function prepare(file, host, options) {
|
|
73
76
|
const name = file.name || (options.camera ? "photo.jpg" : "file");
|
|
74
|
-
const type = file.type || (isHeic({ type: "", name }) ? "image/heic" : "application/octet-stream");
|
|
77
|
+
const type = file.type || (isHeic({ type: "", name }) ? "image/heic" : (videoTypeOf(name) ?? "application/octet-stream"));
|
|
75
78
|
const heic = isHeic({ type, name });
|
|
76
79
|
const picture = type.startsWith("image/") || heic;
|
|
77
80
|
if (options.image && !picture)
|
|
78
81
|
return { refused: `“${name}” isn’t a picture.` };
|
|
82
|
+
/* A video goes as it was picked (0.1.37): what a browser plays, its poster and its length are the host's to make. */
|
|
83
|
+
if (options.video)
|
|
84
|
+
return type.startsWith("video/") ? check({ blob: file, name, type }, host) : { refused: `“${name}” isn’t a video.` };
|
|
79
85
|
const redraw = heic || (picture && /^image\/(jpeg|webp)$/.test(type) && (options.camera || file.size > host.maxBytes || file.size > 1_500_000));
|
|
80
86
|
if (redraw) {
|
|
81
87
|
try {
|
|
@@ -111,7 +117,7 @@ function check(prepared, host) {
|
|
|
111
117
|
return prepared;
|
|
112
118
|
}
|
|
113
119
|
/** Send one prepared file and read what the host answered: the value, or why it was not kept. */
|
|
114
|
-
export async function send(prepared, host, image) {
|
|
120
|
+
export async function send(prepared, host, image, video = false) {
|
|
115
121
|
let answered;
|
|
116
122
|
try {
|
|
117
123
|
answered = await host.upload(prepared.blob, prepared.name);
|
|
@@ -124,6 +130,8 @@ export async function send(prepared, host, image) {
|
|
|
124
130
|
return { refused: `The host’s answer for “${prepared.name}” isn’t a file a record can keep.` };
|
|
125
131
|
if (image && !isImage(answered))
|
|
126
132
|
return { refused: `“${prepared.name}” isn’t a picture.` };
|
|
133
|
+
if (video && !isVideo(answered))
|
|
134
|
+
return { refused: `The host kept “${prepared.name}” as ${typeWords(answered)}, not as a video.` };
|
|
127
135
|
const value = { ...answered, ...(answered.width === undefined && prepared.width ? { width: prepared.width } : {}), ...(answered.height === undefined && prepared.height ? { height: prepared.height } : {}) };
|
|
128
136
|
return value;
|
|
129
137
|
}
|
|
@@ -187,8 +195,61 @@ function FileThumb({ file, size = 56, style }) {
|
|
|
187
195
|
const [gone, failed] = useRemovedLater(file);
|
|
188
196
|
if (gone)
|
|
189
197
|
return _jsx(RemovedLater, { file: file, style: { width: size, height: size, flex: "none", ...style } });
|
|
198
|
+
if (isVideo(file))
|
|
199
|
+
return _jsx(VideoThumb, { file: file, size: size, ...(style ? { style } : {}), onError: failed });
|
|
190
200
|
return (_jsx("img", { onError: failed, src: fileSrc(host, file.href), alt: fileWords(file), width: size, height: size, loading: "lazy", decoding: "async", "data-graview-thumb": file.href, style: { width: size, height: size, objectFit: "cover", borderRadius: ROUND, display: "block", flex: "none", background: "var(--graview-edge)", ...style } }));
|
|
191
201
|
}
|
|
202
|
+
/*
|
|
203
|
+
* A VIDEO ON A RECORD (0.1.37). Its length and size are the value's where
|
|
204
|
+
* the host's upload said them, and otherwise what the player read once it
|
|
205
|
+
* loaded the little it fetches before it plays (`preload="metadata"`): said
|
|
206
|
+
* on the page, never written back to the record.
|
|
207
|
+
*/
|
|
208
|
+
function useVideoFacts(file) {
|
|
209
|
+
const [read, setRead] = useState({});
|
|
210
|
+
const duration = file.duration ?? read.duration;
|
|
211
|
+
const facts = { ...(duration !== undefined ? { duration } : {}), width: file.width ?? read.width, height: file.height ?? read.height };
|
|
212
|
+
const onLoadedMetadata = (event) => {
|
|
213
|
+
const video = event.currentTarget;
|
|
214
|
+
setRead({
|
|
215
|
+
...(Number.isFinite(video.duration) && video.duration > 0 ? { duration: video.duration } : {}),
|
|
216
|
+
...(video.videoWidth > 0 && video.videoHeight > 0 ? { width: video.videoWidth, height: video.videoHeight } : {}),
|
|
217
|
+
});
|
|
218
|
+
};
|
|
219
|
+
return [facts, onLoadedMetadata];
|
|
220
|
+
}
|
|
221
|
+
/** The narrowest a player is drawn where there is room: a small video is made larger, so its controls fit. */
|
|
222
|
+
const MIN_PLAYER = 320;
|
|
223
|
+
/** A video said to somebody who cannot see it: what it shows, and how long it is. */
|
|
224
|
+
const videoLabel = (file, duration) => `${fileWords(file)}, ${videoWords({ ...(duration !== undefined ? { duration } : {}) })}`;
|
|
225
|
+
const PLAY_GLYPH = (_jsx("span", { "data-graview-play-glyph": "", "aria-hidden": "true", style: { position: "absolute", inset: 0, display: "grid", placeItems: "center", pointerEvents: "none" }, children: _jsxs("svg", { viewBox: "0 0 24 24", width: "40%", height: "40%", style: { maxWidth: 40, maxHeight: 40, filter: "drop-shadow(0 1px 2px rgb(0 0 0 / 0.5))" }, children: [_jsx("circle", { cx: "12", cy: "12", r: "11", fill: "rgb(0 0 0 / 0.55)" }), _jsx("path", { d: "M9.5 7.5v9l7-4.5z", fill: "#fff" })] }) }));
|
|
226
|
+
/**
|
|
227
|
+
* A VIDEO, SMALL: on a card, a row, an `image` block's `thumb` — its poster
|
|
228
|
+
* with a play glyph and how long it is. Without a poster, its first frame:
|
|
229
|
+
* a muted player with no controls, out of the keyboard's way, fetching no
|
|
230
|
+
* more than it needs to say its length.
|
|
231
|
+
*/
|
|
232
|
+
function VideoThumb({ file, size, style, onError }) {
|
|
233
|
+
const host = useHostFiles();
|
|
234
|
+
const [facts, onLoadedMetadata] = useVideoFacts(file);
|
|
235
|
+
const box = { width: size, height: size, objectFit: "cover", display: "block", borderRadius: ROUND, background: "var(--graview-edge)" };
|
|
236
|
+
return (_jsxs("span", { role: "img", "aria-label": videoLabel(file, facts.duration), "data-graview-thumb": file.href, "data-graview-video-thumb": "", style: { position: "relative", display: "block", width: size, height: size, flex: "none", ...style }, children: [file.poster ? (_jsx("img", { src: fileSrc(host, file.poster), alt: "", width: size, height: size, loading: "lazy", decoding: "async", onError: onError, style: box })) : (_jsx("video", { src: fileSrc(host, file.href), preload: "metadata", muted: true, playsInline: true, "aria-hidden": "true", tabIndex: -1, width: size, height: size, onLoadedMetadata: onLoadedMetadata, onError: onError, style: box })), PLAY_GLYPH, facts.duration !== undefined ? (_jsx("span", { "data-graview-video-clock": "", "aria-hidden": "true", style: { position: "absolute", right: 3, bottom: 3, padding: "0 4px", borderRadius: 4, background: "rgb(0 0 0 / 0.65)", color: "#fff", fontSize: "0.6875rem", lineHeight: "16px", fontVariantNumeric: "tabular-nums" }, children: videoClock(facts.duration) })) : null] }));
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* A VIDEO, PLAYED: its poster first, the browser's own controls — the
|
|
240
|
+
* keyboard reaches them, and they open it full screen — the little it needs
|
|
241
|
+
* fetched before it is played, its captions where the value carries them,
|
|
242
|
+
* and sized from the value so the page does not jump when it loads.
|
|
243
|
+
*/
|
|
244
|
+
function VideoPlayer({ file }) {
|
|
245
|
+
const host = useHostFiles();
|
|
246
|
+
const [gone, failed] = useRemovedLater(file);
|
|
247
|
+
const [facts, onLoadedMetadata] = useVideoFacts(file);
|
|
248
|
+
if (gone)
|
|
249
|
+
return _jsx(RemovedLater, { file: file, style: { width: "100%", aspectRatio: "16 / 9" } });
|
|
250
|
+
const sized = facts.width !== undefined && facts.height !== undefined;
|
|
251
|
+
return (_jsxs("figure", { "data-graview-video-figure": file.href, style: { margin: 0, display: "grid", gap: 4, minWidth: 0 }, children: [_jsx("video", { "data-graview-video": file.href, src: fileSrc(host, file.href), ...(file.poster ? { poster: fileSrc(host, file.poster) } : {}), controls: true, preload: "metadata", playsInline: true, ...(sized ? { width: facts.width, height: facts.height } : {}), "aria-label": videoLabel(file, facts.duration), onLoadedMetadata: onLoadedMetadata, onError: failed, style: { display: "block", width: "100%", maxWidth: sized ? Math.max(facts.width, MIN_PLAYER) : "100%", height: "auto", maxHeight: "80vh", aspectRatio: sized ? `${facts.width} / ${facts.height}` : "16 / 9", borderRadius: ROUND, background: "#000" }, children: file.captions ? _jsx("track", { kind: "captions", src: fileSrc(host, file.captions), srcLang: "en", label: "Captions", default: true }) : null }), _jsx("figcaption", { "data-graview-video-said": "", style: QUIET, children: [file.caption ?? file.name, ...(facts.duration !== undefined ? [videoClock(facts.duration)] : [])].join(" · ") })] }));
|
|
252
|
+
}
|
|
192
253
|
/** One picture of a gallery, opening full size; one removed since the moment drawn is said so, and opens nothing. */
|
|
193
254
|
function GalleryPicture({ file, onOpen }) {
|
|
194
255
|
const host = useHostFiles();
|
|
@@ -207,7 +268,8 @@ function FilesShown({ files, label }) {
|
|
|
207
268
|
const host = useHostFiles();
|
|
208
269
|
const at = (file) => fileSrc(host, file.href);
|
|
209
270
|
const pictures = files.filter(isImage);
|
|
210
|
-
const
|
|
271
|
+
const videos = files.filter(isVideo);
|
|
272
|
+
const documents = files.filter((file) => !isImage(file) && !isVideo(file));
|
|
211
273
|
const [open, setOpen] = useState(null);
|
|
212
274
|
const dialog = useRef(null);
|
|
213
275
|
// The picture it opened from: the keyboard goes back there, in an engine that focuses no button on a press (WebKit) too.
|
|
@@ -221,7 +283,7 @@ function FilesShown({ files, label }) {
|
|
|
221
283
|
return (_jsxs("div", { style: { display: "grid", gap: 10, minWidth: 0 }, children: [pictures.length > 0 ? (_jsx("ul", { "data-graview-gallery": "", "aria-label": label, style: { listStyle: "none", margin: 0, padding: 0, display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(96px, 1fr))", gap: 8 }, children: pictures.map((file, index) => (_jsx("li", { style: { minWidth: 0 }, children: _jsx(GalleryPicture, { file: file, onOpen: (button) => {
|
|
222
284
|
opener.current = button;
|
|
223
285
|
setOpen(index);
|
|
224
|
-
} }) }, file.href))) })) : null, documents.length > 0 ? (_jsx("ul", { "data-graview-documents": "", style: { listStyle: "none", margin: 0, padding: 0, display: "grid", gap: 10 }, children: documents.map((file) => (_jsxs("li", { "data-graview-document": file.type, style: { display: "grid", gap: 6, minWidth: 0 }, children: [file.type === "application/pdf" ? (_jsx("object", { data: at(file), type: "application/pdf", "aria-label": fileWords(file), style: { width: "100%", height: 420, border: "1px solid var(--graview-edge)", borderRadius: ROUND }, children: _jsxs("p", { style: { margin: 0, padding: 12 }, children: ["This browser shows no PDF here: ", _jsxs("a", { href: at(file), download: file.name, children: ["download ", file.name] }), "."] }) })) : null, _jsxs("span", { style: { display: "flex", flexWrap: "wrap", gap: "2px 10px", alignItems: "baseline", minWidth: 0, overflowWrap: "anywhere" }, children: [_jsx("a", { href: at(file), download: file.name, ...(file.type === "application/pdf" ? { target: "_blank", rel: "noopener" } : {}), children: file.name }), _jsxs("span", { style: QUIET, children: [typeWords(file), " \u00B7 ", sizeWords(file.bytes)] })] }), file.caption ? _jsx("span", { style: QUIET, children: file.caption }) : null] }, file.href))) })) : null, shown ? (_jsx("dialog", { ref: dialog, "data-graview-full": shown.href, "aria-label": fileWords(shown), onClose: () => {
|
|
286
|
+
} }) }, file.href))) })) : null, videos.length > 0 ? (_jsx("ul", { "data-graview-videos": "", "aria-label": label, style: { listStyle: "none", margin: 0, padding: 0, display: "grid", gap: 12 }, children: videos.map((file) => (_jsx("li", { style: { minWidth: 0 }, children: _jsx(VideoPlayer, { file: file }) }, file.href))) })) : null, documents.length > 0 ? (_jsx("ul", { "data-graview-documents": "", style: { listStyle: "none", margin: 0, padding: 0, display: "grid", gap: 10 }, children: documents.map((file) => (_jsxs("li", { "data-graview-document": file.type, style: { display: "grid", gap: 6, minWidth: 0 }, children: [file.type === "application/pdf" ? (_jsx("object", { data: at(file), type: "application/pdf", "aria-label": fileWords(file), style: { width: "100%", height: 420, border: "1px solid var(--graview-edge)", borderRadius: ROUND }, children: _jsxs("p", { style: { margin: 0, padding: 12 }, children: ["This browser shows no PDF here: ", _jsxs("a", { href: at(file), download: file.name, children: ["download ", file.name] }), "."] }) })) : null, _jsxs("span", { style: { display: "flex", flexWrap: "wrap", gap: "2px 10px", alignItems: "baseline", minWidth: 0, overflowWrap: "anywhere" }, children: [_jsx("a", { href: at(file), download: file.name, ...(file.type === "application/pdf" ? { target: "_blank", rel: "noopener" } : {}), children: file.name }), _jsxs("span", { style: QUIET, children: [typeWords(file), " \u00B7 ", sizeWords(file.bytes)] })] }), file.caption ? _jsx("span", { style: QUIET, children: file.caption }) : null] }, file.href))) })) : null, shown ? (_jsx("dialog", { ref: dialog, "data-graview-full": shown.href, "aria-label": fileWords(shown), onClose: () => {
|
|
225
287
|
setOpen(null);
|
|
226
288
|
opener.current?.focus();
|
|
227
289
|
}, onClick: (event) => {
|
|
@@ -236,7 +298,7 @@ function FilesShown({ files, label }) {
|
|
|
236
298
|
* the host takes, what it refused and why; without a host's files it says
|
|
237
299
|
* this host takes none, and offers no way to add one.
|
|
238
300
|
*/
|
|
239
|
-
export function FilesEditor({ value, onChange, several, image, label, thumbs = true }) {
|
|
301
|
+
export function FilesEditor({ value, onChange, several, image, video = false, label, thumbs = true }) {
|
|
240
302
|
const host = useHostFiles();
|
|
241
303
|
const id = useId();
|
|
242
304
|
const picker = useRef(null);
|
|
@@ -245,11 +307,15 @@ export function FilesEditor({ value, onChange, several, image, label, thumbs = t
|
|
|
245
307
|
const [said, setSaid] = useState(null);
|
|
246
308
|
const [refused, setRefused] = useState([]);
|
|
247
309
|
const [over, setOver] = useState(false);
|
|
248
|
-
|
|
249
|
-
const
|
|
250
|
-
const
|
|
251
|
-
const
|
|
252
|
-
const
|
|
310
|
+
/* What only pictures, or only videos, will do with: the host's kinds of that family. */
|
|
311
|
+
const family = image ? "image/" : video ? "video/" : undefined;
|
|
312
|
+
const kinds = host ? (family ? host.kinds.filter((kind) => kind.startsWith(family) || kind === "*" || kind === "*/*") : host.kinds) : [];
|
|
313
|
+
const familyKinds = host ? host.kinds.length === 0 || kinds.length > 0 : false;
|
|
314
|
+
const pictureKinds = !video && (host ? host.kinds.length === 0 || (image ? kinds : host.kinds).some((kind) => kind.startsWith("image/") || kind.startsWith("*")) : false);
|
|
315
|
+
const open = Boolean(host) && (!family || familyKinds);
|
|
316
|
+
const sort = image ? "picture" : video ? "video" : "file";
|
|
317
|
+
const noun = several ? `${sort}s` : `a ${sort}`;
|
|
318
|
+
const accept = family ? (kinds.length > 0 ? kinds.join(",") : `${family}*`) : host?.kinds.join(",") || undefined;
|
|
253
319
|
const coarse = typeof matchMedia === "function" && matchMedia("(pointer: coarse)").matches;
|
|
254
320
|
// What the field holds as the upload answers, which somebody else may have changed while it was sent.
|
|
255
321
|
const latest = useRef(value);
|
|
@@ -275,17 +341,17 @@ export function FilesEditor({ value, onChange, several, image, label, thumbs = t
|
|
|
275
341
|
const kept = [];
|
|
276
342
|
const problems = [];
|
|
277
343
|
if (chosen.length < given.length)
|
|
278
|
-
problems.push(`${label} holds one ${
|
|
344
|
+
problems.push(`${label} holds one ${sort}, so ${given.slice(1).map((file) => `“${file.name || "the photo"}”`).join(", ")} ${given.length > 2 ? "were" : "was"} left out.`);
|
|
279
345
|
setRefused([]);
|
|
280
346
|
setSaid(null);
|
|
281
347
|
for (const file of chosen) {
|
|
282
348
|
setBusy(`Sending ${file.name || "the photo"}…`);
|
|
283
|
-
const prepared = await prepare(file, host, { image, camera: from === "camera" });
|
|
349
|
+
const prepared = await prepare(file, host, { image, video, camera: from === "camera" });
|
|
284
350
|
if ("refused" in prepared) {
|
|
285
351
|
problems.push(prepared.refused);
|
|
286
352
|
continue;
|
|
287
353
|
}
|
|
288
|
-
const sent = await send(prepared, host, image);
|
|
354
|
+
const sent = await send(prepared, host, image, video);
|
|
289
355
|
if ("refused" in sent)
|
|
290
356
|
problems.push(sent.refused);
|
|
291
357
|
// The same bytes answer the same address: a file already here is said, not held twice.
|
|
@@ -337,15 +403,15 @@ export function FilesEditor({ value, onChange, several, image, label, thumbs = t
|
|
|
337
403
|
return;
|
|
338
404
|
event.preventDefault();
|
|
339
405
|
setOver(true);
|
|
340
|
-
}, onDragLeave: () => setOver(false), onDrop: onDrop, style: { display: "grid", gap: 8, minWidth: 0, padding: over ? 6 : 0, outline: over ? "2px dashed var(--graview-accent)" : "none", borderRadius: ROUND }, children: [_jsx("span", { id: `${id}-label`, style: { position: "absolute", width: 1, height: 1, overflow: "hidden", clip: "rect(0 0 0 0)", whiteSpace: "nowrap" }, children: label }), value.length > 0 && (several || value.length > 1) ? (_jsx("ul", { ref: list, style: { listStyle: "none", margin: 0, padding: 0, display: "grid", gap: 6 }, children: value.map((file, index) => (_jsxs("li", { style: { display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap", minWidth: 0 }, children: [thumbs && isImage(file) ? _jsx(FileThumb, { file: file, size: 40 }) : null, _jsx("span", { style: { flex: "1 1 8rem", minWidth: 0, overflowWrap: "anywhere" }, children: file.name }), _jsx(CaptionInput, { file: file, onCaption: (caption) => change(value.map((one, at) => (at === index ? withCaption(one, caption) : one)), caption ? `${file.name} now says “${caption}”.` : `${file.name} says its name again.`) }), several && index > 0 ? (_jsx("button", { type: "button", "data-graview-file-move": "", "aria-label": `Move ${file.name} earlier`, onClick: () => change(value.map((one, at) => (at === index - 1 ? file : at === index ? value[index - 1] : one)), `Moved ${file.name} earlier.`, { at: index - 1, what: "move" }), children: "\u2191" })) : null, several ? (_jsx("button", { type: "button", "data-graview-file-remove": "", "aria-label": `Remove ${file.name}`, onClick: () => change(value.filter((_, at) => at !== index), `Removed ${file.name}.`, { at: index, what: "remove" }), children: "Remove" })) : null] }, `${index}:${file.href}`))) })) : value.length === 1 ? (_jsx(CaptionInput, { file: value[0], onCaption: (caption) => change([withCaption(value[0], caption)], caption ? `${value[0].name} now says “${caption}”.` : `${value[0].name} says its name again.`) })) : null, open ? (_jsxs("div", { style: { display: "flex", flexWrap: "wrap", gap: 8, alignItems: "center" }, children: [_jsx("input", { ref: picker, type: "file", hidden: true, multiple: several, ...(accept ? { accept } : {}), "data-graview-file-input": "", "aria-label": `Choose ${noun}`, onChange: (event) => {
|
|
406
|
+
}, onDragLeave: () => setOver(false), onDrop: onDrop, style: { display: "grid", gap: 8, minWidth: 0, padding: over ? 6 : 0, outline: over ? "2px dashed var(--graview-accent)" : "none", borderRadius: ROUND }, children: [_jsx("span", { id: `${id}-label`, style: { position: "absolute", width: 1, height: 1, overflow: "hidden", clip: "rect(0 0 0 0)", whiteSpace: "nowrap" }, children: label }), value.length > 0 && (several || value.length > 1) ? (_jsx("ul", { ref: list, style: { listStyle: "none", margin: 0, padding: 0, display: "grid", gap: 6 }, children: value.map((file, index) => (_jsxs("li", { style: { display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap", minWidth: 0 }, children: [thumbs && (isImage(file) || isVideo(file)) ? _jsx(FileThumb, { file: file, size: 40 }) : null, _jsx("span", { style: { flex: "1 1 8rem", minWidth: 0, overflowWrap: "anywhere" }, children: file.name }), _jsx(CaptionInput, { file: file, onCaption: (caption) => change(value.map((one, at) => (at === index ? withCaption(one, caption) : one)), caption ? `${file.name} now says “${caption}”.` : `${file.name} says its name again.`) }), several && index > 0 ? (_jsx("button", { type: "button", "data-graview-file-move": "", "aria-label": `Move ${file.name} earlier`, onClick: () => change(value.map((one, at) => (at === index - 1 ? file : at === index ? value[index - 1] : one)), `Moved ${file.name} earlier.`, { at: index - 1, what: "move" }), children: "\u2191" })) : null, several ? (_jsx("button", { type: "button", "data-graview-file-remove": "", "aria-label": `Remove ${file.name}`, onClick: () => change(value.filter((_, at) => at !== index), `Removed ${file.name}.`, { at: index, what: "remove" }), children: "Remove" })) : null] }, `${index}:${file.href}`))) })) : value.length === 1 ? (_jsx(CaptionInput, { file: value[0], onCaption: (caption) => change([withCaption(value[0], caption)], caption ? `${value[0].name} now says “${caption}”.` : `${value[0].name} says its name again.`) })) : null, open ? (_jsxs("div", { style: { display: "flex", flexWrap: "wrap", gap: 8, alignItems: "center" }, children: [_jsx("input", { ref: picker, type: "file", hidden: true, multiple: several, ...(accept ? { accept } : {}), "data-graview-file-input": "", "aria-label": `Choose ${noun}`, onChange: (event) => {
|
|
341
407
|
const picked = filesFrom(event.currentTarget.files);
|
|
342
408
|
event.currentTarget.value = "";
|
|
343
409
|
void add(picked, "picked");
|
|
344
|
-
} }), _jsx("button", { type: "button", disabled: busy !== null, onClick: () => picker.current?.click(), "data-graview-file-choose": "", children: value.length > 0 && !several ? `Replace ${
|
|
410
|
+
} }), _jsx("button", { type: "button", disabled: busy !== null, onClick: () => picker.current?.click(), "data-graview-file-choose": "", children: value.length > 0 && !several ? `Replace the ${sort}` : `Choose ${noun}` }), pictureKinds ? (_jsxs(_Fragment, { children: [_jsx("input", { ref: camera, type: "file", hidden: true, accept: "image/*", capture: "environment", "data-graview-photo-input": "", "aria-label": "Take a photo", onChange: (event) => {
|
|
345
411
|
const taken = filesFrom(event.currentTarget.files);
|
|
346
412
|
event.currentTarget.value = "";
|
|
347
413
|
void add(taken, "camera");
|
|
348
|
-
} }), coarse ? (_jsx("button", { type: "button", disabled: busy !== null, onClick: () => camera.current?.click(), children: "Take a photo" })) : null] })) : null, _jsxs("span", { style: QUIET, "data-graview-file-takes": "", children: ["or drop or paste ", several ? "them" : "it", " here \u00B7 ", host.kinds.length > 0 ? kindsWords(kinds.length > 0 ? kinds : host.kinds) : "any file", ", up to ", sizeWords(host.maxBytes)] })] })) : (_jsx("p", { style: { ...QUIET, margin: 0 }, "data-graview-file-none": "", children: host ?
|
|
414
|
+
} }), coarse ? (_jsx("button", { type: "button", disabled: busy !== null, onClick: () => camera.current?.click(), children: "Take a photo" })) : null] })) : null, _jsxs("span", { style: QUIET, "data-graview-file-takes": "", children: ["or drop or paste ", several ? "them" : "it", " here \u00B7 ", host.kinds.length > 0 ? kindsWords(kinds.length > 0 ? kinds : host.kinds) : "any file", ", up to ", sizeWords(host.maxBytes)] })] })) : (_jsx("p", { style: { ...QUIET, margin: 0 }, "data-graview-file-none": "", children: host ? `This host doesn’t take ${video ? "videos" : "pictures"}, so none can be added here.` : "This host doesn’t take files, so none can be added here." })), _jsx("p", { "aria-live": "polite", style: { ...QUIET, margin: 0, minHeight: busy || said ? undefined : 0 }, "data-graview-file-status": "", children: busy ?? said ?? "" }), refused.length > 0 ? (_jsx("div", { role: "alert", "data-graview-file-refused": "", style: { color: "var(--graview-warn)", fontSize: "0.875rem", display: "grid", gap: 2 }, children: refused.map((line, index) => (_jsx("p", { style: { margin: 0 }, children: line }, index))) })) : null] }));
|
|
349
415
|
}
|
|
350
416
|
const withCaption = (file, caption) => {
|
|
351
417
|
const { caption: _was, ...rest } = file;
|
|
@@ -360,7 +426,7 @@ function CaptionInput({ file, onCaption }) {
|
|
|
360
426
|
if (next !== (file.caption ?? ""))
|
|
361
427
|
onCaption(next);
|
|
362
428
|
};
|
|
363
|
-
return (_jsx("input", { type: "text", value: draft, placeholder: isImage(file) ? "What it shows" : "What it is", "aria-label": `What ${file.name} ${isImage(file) ? "shows" : "is"}`, "data-graview-file-caption": file.href, onChange: (event) => setDraft(event.target.value), onBlur: keep, onKeyDown: (event) => {
|
|
429
|
+
return (_jsx("input", { type: "text", value: draft, placeholder: isImage(file) || isVideo(file) ? "What it shows" : "What it is", "aria-label": `What ${file.name} ${isImage(file) || isVideo(file) ? "shows" : "is"}`, "data-graview-file-caption": file.href, onChange: (event) => setDraft(event.target.value), onBlur: keep, onKeyDown: (event) => {
|
|
364
430
|
if (event.key === "Enter") {
|
|
365
431
|
event.preventDefault();
|
|
366
432
|
keep();
|
|
@@ -386,7 +452,7 @@ function FileField(props) {
|
|
|
386
452
|
const several = shape?.type === "several";
|
|
387
453
|
if (files.length === 0 && one?.type !== "file")
|
|
388
454
|
return null;
|
|
389
|
-
return (_jsxs(Row, { ...props, children: [_jsx(FilesShown, { files: files, label: label }), editable && one?.type === "file" ? (_jsx(FilesEditor, { value: files, thumbs: false, several: several, image: one.image === true, label: label, onChange: (next) => {
|
|
455
|
+
return (_jsxs(Row, { ...props, children: [_jsx(FilesShown, { files: files, label: label }), editable && one?.type === "file" ? (_jsx(FilesEditor, { value: files, thumbs: false, several: several, image: one.image === true, video: one.video === true, label: label, onChange: (next) => {
|
|
390
456
|
// One file: what was added replaces it; an edit cannot leave a field with nothing.
|
|
391
457
|
const value = several ? next : next[next.length - 1];
|
|
392
458
|
if (value !== undefined)
|
|
@@ -405,7 +471,7 @@ export function drawFiles(props) {
|
|
|
405
471
|
// A gallery's pictures are pressed, so they stand above the card's own link.
|
|
406
472
|
return props.files.length > 0 ? (_jsx("span", { className: "graview-spec-image", "data-graview-field": props.field, "data-graview-image": props.view, style: { display: "block", minWidth: 0, ...(props.view === "gallery" ? { position: "relative", zIndex: LOCAL_LAYERS.raised } : {}) }, children: props.view === "thumb" ? _jsx(FileThumb, { file: props.files[0], size: 72 }) : _jsx(FilesShown, { files: props.files, label: props.label }) })) : null;
|
|
407
473
|
case "control": {
|
|
408
|
-
const editor = _jsx(FilesEditor, { value: filesOf(props.value), several: props.several, image: props.image, label: props.label, onChange: (next) => props.onChange(props.several ? next : next[next.length - 1]) });
|
|
474
|
+
const editor = _jsx(FilesEditor, { value: filesOf(props.value), several: props.several, image: props.image, video: props.video === true, label: props.label, onChange: (next) => props.onChange(props.several ? next : next[next.length - 1]) });
|
|
409
475
|
return props.title === undefined ? (editor) : (_jsxs("fieldset", { "data-graview-part": "group", "data-graview-field": props.several ? "files" : "file", children: [_jsx("legend", { "data-graview-part": "label", children: props.title }), editor] }));
|
|
410
476
|
}
|
|
411
477
|
}
|