@things-factory/figure-ui 10.1.178 → 10.1.179
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/client/figure-released.ts +6 -0
- package/client/graphql/index.ts +101 -25
- package/client/graphql/store.ts +2 -1
- package/client/index.ts +1 -0
- package/client/modeller/editor.ts +10 -2
- package/client/modeller/figure-first-save.ts +23 -3
- package/client/modeller/figure-parts.ts +53 -23
- package/client/modeller/figure-properties.ts +41 -7
- package/client/modeller/first-save.ts +2 -0
- package/client/modeller/product-session.ts +5 -3
- package/client/modeller/property-sections.ts +45 -0
- package/client/pages/figure-list-page.ts +52 -10
- package/client/pages/figure-modeller-page.ts +15 -3
- package/client/setting-let/figure-namespace-setting-let.ts +113 -31
- package/client/types.ts +8 -0
- package/client/viewparts/figure-detail-dock.ts +124 -9
- package/client/viewparts/figure-thumb.ts +6 -3
- package/dist-client/figure-released.d.ts +3 -0
- package/dist-client/figure-released.js +5 -0
- package/dist-client/figure-released.js.map +1 -0
- package/dist-client/graphql/index.d.ts +25 -13
- package/dist-client/graphql/index.js +85 -16
- package/dist-client/graphql/index.js.map +1 -1
- package/dist-client/graphql/store.js +2 -1
- package/dist-client/graphql/store.js.map +1 -1
- package/dist-client/index.d.ts +1 -0
- package/dist-client/index.js +1 -0
- package/dist-client/index.js.map +1 -1
- package/dist-client/modeller/editor.d.ts +2 -0
- package/dist-client/modeller/editor.js +13 -2
- package/dist-client/modeller/editor.js.map +1 -1
- package/dist-client/modeller/figure-first-save.d.ts +8 -0
- package/dist-client/modeller/figure-first-save.js +28 -3
- package/dist-client/modeller/figure-first-save.js.map +1 -1
- package/dist-client/modeller/figure-parts.d.ts +5 -0
- package/dist-client/modeller/figure-parts.js +54 -22
- package/dist-client/modeller/figure-parts.js.map +1 -1
- package/dist-client/modeller/figure-properties.d.ts +4 -0
- package/dist-client/modeller/figure-properties.js +45 -7
- package/dist-client/modeller/figure-properties.js.map +1 -1
- package/dist-client/modeller/first-save.d.ts +2 -0
- package/dist-client/modeller/first-save.js.map +1 -1
- package/dist-client/modeller/product-session.d.ts +4 -1
- package/dist-client/modeller/product-session.js +3 -3
- package/dist-client/modeller/product-session.js.map +1 -1
- package/dist-client/modeller/property-sections.d.ts +26 -0
- package/dist-client/modeller/property-sections.js +17 -0
- package/dist-client/modeller/property-sections.js.map +1 -0
- package/dist-client/pages/figure-list-page.d.ts +4 -1
- package/dist-client/pages/figure-list-page.js +56 -10
- package/dist-client/pages/figure-list-page.js.map +1 -1
- package/dist-client/pages/figure-modeller-page.d.ts +2 -0
- package/dist-client/pages/figure-modeller-page.js +19 -3
- package/dist-client/pages/figure-modeller-page.js.map +1 -1
- package/dist-client/setting-let/figure-namespace-setting-let.d.ts +16 -5
- package/dist-client/setting-let/figure-namespace-setting-let.js +129 -31
- package/dist-client/setting-let/figure-namespace-setting-let.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/types.d.ts +12 -0
- package/dist-client/types.js.map +1 -1
- package/dist-client/viewparts/figure-detail-dock.d.ts +33 -2
- package/dist-client/viewparts/figure-detail-dock.js +171 -11
- package/dist-client/viewparts/figure-detail-dock.js.map +1 -1
- package/dist-client/viewparts/figure-thumb.d.ts +2 -0
- package/dist-client/viewparts/figure-thumb.js +13 -3
- package/dist-client/viewparts/figure-thumb.js.map +1 -1
- package/package.json +8 -7
- package/test/browser/category-page.js +28 -0
- package/test/browser/category.check.mjs +54 -0
- package/test/browser/mesh-select.check.mjs +1 -1
- package/test/browser/namespace-page.js +75 -0
- package/test/browser/namespace.check.mjs +104 -0
- package/test/browser/parts-page.js +8 -2
- package/test/browser/places-mesh.check.mjs +30 -2
- package/test/browser/property-section-page.js +45 -0
- package/test/browser/property-section.check.mjs +48 -0
- package/test/browser/props.check.mjs +13 -2
- package/test/browser/public-page.js +39 -0
- package/test/browser/public.check.mjs +79 -0
- package/test/browser/retire-page.js +37 -0
- package/test/browser/retire.check.mjs +96 -0
- package/test/figure-released.test.ts +54 -0
- package/test/modeller-route.test.ts +2 -1
- package/translations/en.json +20 -5
- package/translations/es.json +20 -5
- package/translations/ja.json +20 -5
- package/translations/ko.json +20 -5
- package/translations/ms.json +20 -5
- package/translations/zh.json +20 -5
package/dist-client/types.d.ts
CHANGED
|
@@ -30,6 +30,18 @@ export interface Figure {
|
|
|
30
30
|
source?: string;
|
|
31
31
|
properties?: string;
|
|
32
32
|
version?: number;
|
|
33
|
+
/** Retired: still released and served, no longer offered for new use. */
|
|
34
|
+
deprecated?: boolean;
|
|
35
|
+
/** The type to use instead of a retired figure, in the same domain. */
|
|
36
|
+
supersededBy?: string | null;
|
|
37
|
+
/** Public: other domains read its releases and draw it by full name (LV figure-server design G2). */
|
|
38
|
+
public?: boolean;
|
|
39
|
+
/** The domain it belongs to; carried by another domain's public figure, so a screen says whose it is. */
|
|
40
|
+
domain?: {
|
|
41
|
+
id: string;
|
|
42
|
+
name?: string;
|
|
43
|
+
subdomain?: string;
|
|
44
|
+
} | null;
|
|
33
45
|
}
|
|
34
46
|
/**
|
|
35
47
|
* 발행된 판 하나.
|
package/dist-client/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../client/types.ts"],"names":[],"mappings":"AAAA;;GAEG","sourcesContent":["/**\n * 화면이 다루는 모양. 도형 자산의 타입은 `@hatiolab/figure-model` 이 정한다 — 여기서 다시 적지 않는다.\n */\n\n/** 서버가 돌려주는 Figure 한 벌. `source` 는 상세 또는 저작면을 열 때만 받는다. */\nexport interface Figure {\n id: string\n /** 컴포넌트 타입 이름. **만든 뒤에 고칠 수 없다.** */\n type: string\n /** Namespace of the full name {namespace}/{type} (ADR-0114). */\n namespace?: string | null\n name: string\n description?: string\n /** 분류 하나 — 팔레트에서 묶어 보이는 기준. */\n category?: string\n /** 태그 여럿 — 분류로는 못 하는 찾기. */\n tags?: string[]\n state?: 'draft' | 'released'\n /** 재활용 점수 0~100. 서버가 정본에서 계산한다 — 화면이 보내는 값이 아니다. */\n score?: number\n triangles?: number\n groups?: number\n thumbnail?: string\n thumbnailUpdatedAt?: string\n updatedAt?: string\n updater?: { id: string; name: string }\n /** 도형 자산 JSON 문자열. 목록 질의에는 실려 오지 않는다. */\n source?: string\n properties?: string\n version?: number\n}\n\n/**\n * 발행된 판 하나.\n *\n * 저작 중 저장은 여기 오지 않는다 — 판본은 발행에서만 생긴다.\n */\nexport interface FigureVersion {\n version: number\n /** 발행하며 남긴 말. */\n comment?: string\n state?: 'draft' | 'released'\n updatedAt?: string\n updater?: { id: string; name: string }\n score?: number\n triangles?: number\n groups?: number\n}\n\nexport interface FigureListResult {\n items: Figure[]\n total: number\n}\n\n/**\n * 새 Figure.\n *\n * `score`·`triangles`·`groups` 가 없다 — **서버가 정본에서 계산한다.** 화면이 보내면\n * 거짓이 들어오고 목록이 실물과 다른 것을 보여 준다.\n */\nexport interface NewFigure {\n type: string\n name: string\n description?: string\n category?: string\n tags?: string[]\n source: string\n properties?: string\n state?: 'draft' | 'released'\n thumbnail?: string\n}\n\n/**\n * 수정.\n *\n * `type` 이 없다 — 저장되는 식별자라 고칠 수 없다. 배치된 도면이 그 이름으로 타입을\n * 찾으므로, 바꾸면 그 도면이 빈 화면이 된다.\n */\nexport interface FigurePatch {\n /** 발행 전 초안에서만 받는다 — 발행된 뒤에는 문이 거절한다(ADR-0063 결정 4). */\n type?: string\n name?: string\n description?: string\n category?: string\n /** 빈 배열은 「전부 지운다」, 안 주는 것은 「건드리지 않는다」. */\n tags?: string[]\n source?: string\n properties?: string\n state?: 'draft' | 'released'\n thumbnail?: string\n}\n\n/** 검증 결과 한 줄. 코드는 figure-model 의 것을 그대로 쓴다. */\nexport interface FigureFinding {\n code: string\n message: string\n at?: string\n}\n\n/**\n * 발행 판정 한 줄.\n *\n * `FigureFinding` 과 달리 **`blocking` 이 있다.** 저장 쪽 발견은 정의상 막지 않는 것들이고,\n * 발행 판정은 막는 것과 알리는 것을 함께 내므로 어느 쪽인지를 값이 스스로 말한다 — 코드를 보고\n * 화면이 다시 분류하면 규칙이 두 벌이 된다.\n */\nexport interface FigureGateFinding {\n code: string\n message: string\n /** 왜 그것이 문제인가 — 사람이 무엇을 보게 되나. */\n why?: string\n /** 무엇을 바꾸면 되나 — 부품 이름과 값으로. */\n how?: string\n /** 갈라진 부품 이름들, 또는 정본 안의 경로. */\n at?: string\n blocking: boolean\n}\n\n/** 발행해도 되나. `blocked` 면 서버가 거절한다. */\nexport interface FigureInspection {\n blocked: boolean\n findings: FigureGateFinding[]\n}\n"]}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../client/types.ts"],"names":[],"mappings":"AAAA;;GAEG","sourcesContent":["/**\n * 화면이 다루는 모양. 도형 자산의 타입은 `@hatiolab/figure-model` 이 정한다 — 여기서 다시 적지 않는다.\n */\n\n/** 서버가 돌려주는 Figure 한 벌. `source` 는 상세 또는 저작면을 열 때만 받는다. */\nexport interface Figure {\n id: string\n /** 컴포넌트 타입 이름. **만든 뒤에 고칠 수 없다.** */\n type: string\n /** Namespace of the full name {namespace}/{type} (ADR-0114). */\n namespace?: string | null\n name: string\n description?: string\n /** 분류 하나 — 팔레트에서 묶어 보이는 기준. */\n category?: string\n /** 태그 여럿 — 분류로는 못 하는 찾기. */\n tags?: string[]\n state?: 'draft' | 'released'\n /** 재활용 점수 0~100. 서버가 정본에서 계산한다 — 화면이 보내는 값이 아니다. */\n score?: number\n triangles?: number\n groups?: number\n thumbnail?: string\n thumbnailUpdatedAt?: string\n updatedAt?: string\n updater?: { id: string; name: string }\n /** 도형 자산 JSON 문자열. 목록 질의에는 실려 오지 않는다. */\n source?: string\n properties?: string\n version?: number\n /** Retired: still released and served, no longer offered for new use. */\n deprecated?: boolean\n /** The type to use instead of a retired figure, in the same domain. */\n supersededBy?: string | null\n /** Public: other domains read its releases and draw it by full name (LV figure-server design G2). */\n public?: boolean\n /** The domain it belongs to; carried by another domain's public figure, so a screen says whose it is. */\n domain?: { id: string; name?: string; subdomain?: string } | null\n}\n\n/**\n * 발행된 판 하나.\n *\n * 저작 중 저장은 여기 오지 않는다 — 판본은 발행에서만 생긴다.\n */\nexport interface FigureVersion {\n version: number\n /** 발행하며 남긴 말. */\n comment?: string\n state?: 'draft' | 'released'\n updatedAt?: string\n updater?: { id: string; name: string }\n score?: number\n triangles?: number\n groups?: number\n}\n\nexport interface FigureListResult {\n items: Figure[]\n total: number\n}\n\n/**\n * 새 Figure.\n *\n * `score`·`triangles`·`groups` 가 없다 — **서버가 정본에서 계산한다.** 화면이 보내면\n * 거짓이 들어오고 목록이 실물과 다른 것을 보여 준다.\n */\nexport interface NewFigure {\n type: string\n name: string\n description?: string\n category?: string\n tags?: string[]\n source: string\n properties?: string\n state?: 'draft' | 'released'\n thumbnail?: string\n}\n\n/**\n * 수정.\n *\n * `type` 이 없다 — 저장되는 식별자라 고칠 수 없다. 배치된 도면이 그 이름으로 타입을\n * 찾으므로, 바꾸면 그 도면이 빈 화면이 된다.\n */\nexport interface FigurePatch {\n /** 발행 전 초안에서만 받는다 — 발행된 뒤에는 문이 거절한다(ADR-0063 결정 4). */\n type?: string\n name?: string\n description?: string\n category?: string\n /** 빈 배열은 「전부 지운다」, 안 주는 것은 「건드리지 않는다」. */\n tags?: string[]\n source?: string\n properties?: string\n state?: 'draft' | 'released'\n thumbnail?: string\n}\n\n/** 검증 결과 한 줄. 코드는 figure-model 의 것을 그대로 쓴다. */\nexport interface FigureFinding {\n code: string\n message: string\n at?: string\n}\n\n/**\n * 발행 판정 한 줄.\n *\n * `FigureFinding` 과 달리 **`blocking` 이 있다.** 저장 쪽 발견은 정의상 막지 않는 것들이고,\n * 발행 판정은 막는 것과 알리는 것을 함께 내므로 어느 쪽인지를 값이 스스로 말한다 — 코드를 보고\n * 화면이 다시 분류하면 규칙이 두 벌이 된다.\n */\nexport interface FigureGateFinding {\n code: string\n message: string\n /** 왜 그것이 문제인가 — 사람이 무엇을 보게 되나. */\n why?: string\n /** 무엇을 바꾸면 되나 — 부품 이름과 값으로. */\n how?: string\n /** 갈라진 부품 이름들, 또는 정본 안의 경로. */\n at?: string\n blocking: boolean\n}\n\n/** 발행해도 되나. `blocked` 면 서버가 거절한다. */\nexport interface FigureInspection {\n blocked: boolean\n findings: FigureGateFinding[]\n}\n"]}
|
|
@@ -5,12 +5,18 @@ import '@things-factory/figure-viewer/dist-client/figure-asset-viewer.js';
|
|
|
5
5
|
import '@things-factory/figure-viewer/dist-client/figure-facts-list.js';
|
|
6
6
|
/** Said on window when a figure's card picture was replaced from the preview: `{ detail: { id } }`. */
|
|
7
7
|
export declare const FIGURE_THUMBNAIL_CHANGED = "figure-thumbnail-changed";
|
|
8
|
+
/** Said on window when a figure was retired or brought back: `{ detail: { id } }`. */
|
|
9
|
+
export declare const FIGURE_RETIREMENT_CHANGED = "figure-retirement-changed";
|
|
10
|
+
/** Said on window when a figure was made public or private: `{ detail: { id } }`. */
|
|
11
|
+
export declare const FIGURE_PUBLIC_CHANGED = "figure-public-changed";
|
|
8
12
|
export interface FigureDetailRecord {
|
|
9
13
|
saved: Figure;
|
|
10
14
|
released?: {
|
|
11
15
|
version: number;
|
|
12
16
|
source: string;
|
|
13
17
|
};
|
|
18
|
+
/** Another domain's public figure (G2): only its release is shown, and nothing on it is changed from here. */
|
|
19
|
+
foreign?: boolean;
|
|
14
20
|
}
|
|
15
21
|
declare const FigureDetailDock_base: (new (...args: any[]) => LitElement) & typeof LitElement;
|
|
16
22
|
/** Catalogue details stay while the catalogue is shown; choosing a size or a pose has no write path. */
|
|
@@ -22,6 +28,15 @@ export declare class FigureDetailDock extends FigureDetailDock_base {
|
|
|
22
28
|
* released figure a draft (`updateFigure`). The catalogue is told, and reads its cards again.
|
|
23
29
|
*/
|
|
24
30
|
keepThumbnail: (id: string, dataUrl: string) => Promise<unknown>;
|
|
31
|
+
/** Retires a released figure or brings it back (`setFigureDeprecation`); a check gives its own. */
|
|
32
|
+
retire: (id: string, deprecated: boolean, supersededBy: string | null) => Promise<Figure>;
|
|
33
|
+
/** The types a retired figure may name instead: the domain's other figures. */
|
|
34
|
+
typeNamesOf: () => Promise<string[]>;
|
|
35
|
+
/** Makes a released figure public or private (`setFigurePublic`); a check gives its own. */
|
|
36
|
+
publish: (id: string, isPublic: boolean) => Promise<Figure>;
|
|
37
|
+
/** Another domain's public figure as the catalogue listed it, with its latest release; its draft is not this domain's to open. */
|
|
38
|
+
loadPublic: (row: Figure) => Promise<FigureDetailRecord>;
|
|
39
|
+
private publishError;
|
|
25
40
|
private figureId;
|
|
26
41
|
private record?;
|
|
27
42
|
private asset?;
|
|
@@ -29,17 +44,33 @@ export declare class FigureDetailDock extends FigureDetailDock_base {
|
|
|
29
44
|
private loading;
|
|
30
45
|
private error;
|
|
31
46
|
private cost?;
|
|
47
|
+
private retiring;
|
|
48
|
+
private instead;
|
|
49
|
+
private typeNames;
|
|
50
|
+
private retireError;
|
|
51
|
+
private retireBusy;
|
|
32
52
|
private ticket;
|
|
33
53
|
private unsubscribe?;
|
|
34
54
|
private boundary?;
|
|
35
55
|
private boundaryObserver?;
|
|
36
|
-
show(id: string): Promise<void>;
|
|
56
|
+
show(id: string, publicRow?: Figure): Promise<void>;
|
|
37
57
|
private choose;
|
|
38
58
|
connectedCallback(): void;
|
|
39
59
|
disconnectedCallback(): void;
|
|
40
60
|
private place;
|
|
41
61
|
private escape;
|
|
62
|
+
/**
|
|
63
|
+
* Retiring a released figure (it stays released and served; boards already using it keep it), naming the type to use
|
|
64
|
+
* instead, or bringing it back. A figure never released is deleted from the catalogue instead.
|
|
65
|
+
*/
|
|
66
|
+
private retireRow;
|
|
67
|
+
/** Public to other domains, or not (G2): they read its releases and draw it by full name; its draft stays here. */
|
|
68
|
+
private publishRow;
|
|
69
|
+
private setPublic;
|
|
70
|
+
private openRetire;
|
|
71
|
+
private setRetired;
|
|
42
72
|
render(): import("lit-html").TemplateResult<1>;
|
|
43
73
|
}
|
|
44
|
-
|
|
74
|
+
/** Opens the preview of a figure; `publicRow` for another domain's public figure, shown by its release alone. */
|
|
75
|
+
export declare function openFigureDetail(figure: Figure, publicRow?: boolean): void;
|
|
45
76
|
export {};
|
|
@@ -10,7 +10,8 @@ import { ASSISTANT_DOCK_VIEWPART } from '@things-factory/ai-assistant/dist-clien
|
|
|
10
10
|
import { figureCostOf, parseFigureAsset } from '@hatiolab/figure-model';
|
|
11
11
|
import { capacityFrom } from '../modeller/scene-budget.js';
|
|
12
12
|
import { SidePanelStyles } from '@things-factory/figure-viewer/dist-client/side-panel-styles.js';
|
|
13
|
-
import { fetchFigure, fetchReleasedFigure, updateFigure } from '../graphql/index.js';
|
|
13
|
+
import { fetchFigure, fetchFigureTypeNames, fetchReleasedFigure, setFigureDeprecation, setFigurePublic, updateFigure } from '../graphql/index.js';
|
|
14
|
+
import { hasBeenReleased } from '../figure-released.js';
|
|
14
15
|
import { modellerPathOf } from '../modeller/modeller-route.js';
|
|
15
16
|
import { leftOpeningPage } from './detail-route.js';
|
|
16
17
|
import '@things-factory/figure-viewer/dist-client/figure-asset-viewer.js';
|
|
@@ -18,6 +19,10 @@ import '@things-factory/figure-viewer/dist-client/figure-facts-list.js';
|
|
|
18
19
|
const VIEWPART = 'figure-detail';
|
|
19
20
|
/** Said on window when a figure's card picture was replaced from the preview: `{ detail: { id } }`. */
|
|
20
21
|
export const FIGURE_THUMBNAIL_CHANGED = 'figure-thumbnail-changed';
|
|
22
|
+
/** Said on window when a figure was retired or brought back: `{ detail: { id } }`. */
|
|
23
|
+
export const FIGURE_RETIREMENT_CHANGED = 'figure-retirement-changed';
|
|
24
|
+
/** Said on window when a figure was made public or private: `{ detail: { id } }`. */
|
|
25
|
+
export const FIGURE_PUBLIC_CHANGED = 'figure-public-changed';
|
|
21
26
|
/** Catalogue details stay while the catalogue is shown; choosing a size or a pose has no write path. */
|
|
22
27
|
let FigureDetailDock = class FigureDetailDock extends localize(i18next)(LitElement) {
|
|
23
28
|
constructor() {
|
|
@@ -36,10 +41,28 @@ let FigureDetailDock = class FigureDetailDock extends localize(i18next)(LitEleme
|
|
|
36
41
|
await updateFigure(id, { thumbnail: dataUrl });
|
|
37
42
|
window.dispatchEvent(new CustomEvent(FIGURE_THUMBNAIL_CHANGED, { detail: { id } }));
|
|
38
43
|
};
|
|
44
|
+
/** Retires a released figure or brings it back (`setFigureDeprecation`); a check gives its own. */
|
|
45
|
+
this.retire = setFigureDeprecation;
|
|
46
|
+
/** The types a retired figure may name instead: the domain's other figures. */
|
|
47
|
+
this.typeNamesOf = fetchFigureTypeNames;
|
|
48
|
+
/** Makes a released figure public or private (`setFigurePublic`); a check gives its own. */
|
|
49
|
+
this.publish = setFigurePublic;
|
|
50
|
+
/** Another domain's public figure as the catalogue listed it, with its latest release; its draft is not this domain's to open. */
|
|
51
|
+
this.loadPublic = async (row) => ({
|
|
52
|
+
saved: row,
|
|
53
|
+
released: await fetchReleasedFigure(row.id),
|
|
54
|
+
foreign: true
|
|
55
|
+
});
|
|
56
|
+
this.publishError = '';
|
|
39
57
|
this.figureId = '';
|
|
40
58
|
this.sourceKind = 'saved';
|
|
41
59
|
this.loading = false;
|
|
42
60
|
this.error = '';
|
|
61
|
+
this.retiring = false;
|
|
62
|
+
this.instead = '';
|
|
63
|
+
this.typeNames = [];
|
|
64
|
+
this.retireError = '';
|
|
65
|
+
this.retireBusy = false;
|
|
43
66
|
this.ticket = 0;
|
|
44
67
|
this.place = () => {
|
|
45
68
|
const bar = visibleBar(document);
|
|
@@ -99,11 +122,21 @@ let FigureDetailDock = class FigureDetailDock extends localize(i18next)(LitEleme
|
|
|
99
122
|
p { margin: 0; line-height: 1.5; }
|
|
100
123
|
[description] { padding: 10px 16px 0; color: var(--md-sys-color-on-surface-variant); }
|
|
101
124
|
[status] { display: grid; gap: 8px; justify-items: start; padding: 12px 16px; }
|
|
125
|
+
/* Retiring: quiet words in the line under what is shown, like the source switch above it. */
|
|
126
|
+
[retire], [publish] { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; color: var(--md-sys-color-on-surface-variant); }
|
|
127
|
+
[retire] button, [publish] button { padding: 0; border: none; background: none; color: var(--md-sys-color-on-surface-variant); }
|
|
128
|
+
[retire] button:hover:not(:disabled), [publish] button:hover:not(:disabled) { color: var(--md-sys-color-on-surface); }
|
|
129
|
+
[publish] [published] { color: var(--md-sys-color-on-surface); font-weight: 600; }
|
|
130
|
+
[retire] button[primary] { color: var(--md-sys-color-primary); font-weight: 600; }
|
|
131
|
+
[retire] input { font: inherit; min-width: 0; flex: 1 1 160px; padding: 2px 6px; border: 1px solid var(--md-sys-color-outline-variant);
|
|
132
|
+
border-radius: 6px; background: var(--md-sys-color-surface); color: var(--md-sys-color-on-surface); }
|
|
133
|
+
[retire] [retired] { color: var(--md-sys-color-on-surface); font-weight: 600; }
|
|
134
|
+
[retire-error] { color: var(--md-sys-color-error); }
|
|
102
135
|
@media (max-width: 1439.98px) {
|
|
103
136
|
:host { position: fixed; inset: var(--figure-detail-top, 44px) 0 0 auto; height: calc(100dvh - var(--figure-detail-top, 44px)); z-index: 11; box-shadow: -4px 0 16px rgb(0 0 0 / 20%); }
|
|
104
137
|
}
|
|
105
138
|
`]; }
|
|
106
|
-
async show(id) {
|
|
139
|
+
async show(id, publicRow) {
|
|
107
140
|
const ticket = ++this.ticket;
|
|
108
141
|
this.figureId = id;
|
|
109
142
|
this.record = undefined;
|
|
@@ -111,8 +144,11 @@ let FigureDetailDock = class FigureDetailDock extends localize(i18next)(LitEleme
|
|
|
111
144
|
this.cost = undefined;
|
|
112
145
|
this.loading = true;
|
|
113
146
|
this.error = '';
|
|
147
|
+
this.retiring = false;
|
|
148
|
+
this.retireError = '';
|
|
149
|
+
this.publishError = '';
|
|
114
150
|
try {
|
|
115
|
-
const record = await this.load(id);
|
|
151
|
+
const record = publicRow ? await this.loadPublic(publicRow) : await this.load(id);
|
|
116
152
|
if (ticket !== this.ticket)
|
|
117
153
|
return;
|
|
118
154
|
this.record = record;
|
|
@@ -168,6 +204,85 @@ let FigureDetailDock = class FigureDetailDock extends localize(i18next)(LitEleme
|
|
|
168
204
|
this.boundaryObserver?.disconnect();
|
|
169
205
|
this.boundary = undefined;
|
|
170
206
|
}
|
|
207
|
+
/**
|
|
208
|
+
* Retiring a released figure (it stays released and served; boards already using it keep it), naming the type to use
|
|
209
|
+
* instead, or bringing it back. A figure never released is deleted from the catalogue instead.
|
|
210
|
+
*/
|
|
211
|
+
retireRow(saved) {
|
|
212
|
+
if (saved.deprecated) {
|
|
213
|
+
return html `<div retire>
|
|
214
|
+
<span retired>${saved.supersededBy
|
|
215
|
+
? i18next.t('figure.text.retired-instead', { type: saved.supersededBy })
|
|
216
|
+
: i18next.t('figure.label.retired')}</span>
|
|
217
|
+
<button ?disabled=${this.retireBusy} @click=${() => this.setRetired(saved, false)}>${i18next.t('figure.button.unretire')}</button>
|
|
218
|
+
</div>
|
|
219
|
+
${this.retireError ? html `<p retire-error role="alert">${this.retireError}</p>` : nothing}`;
|
|
220
|
+
}
|
|
221
|
+
if (!this.retiring) {
|
|
222
|
+
return html `<div retire><button @click=${() => this.openRetire(saved)}>${i18next.t('figure.button.retire')}</button></div>`;
|
|
223
|
+
}
|
|
224
|
+
return html `<div retire>
|
|
225
|
+
<input list="figure-retire-instead" aria-label=${i18next.t('figure.label.superseded-by')}
|
|
226
|
+
placeholder=${i18next.t('figure.label.superseded-by')} .value=${this.instead}
|
|
227
|
+
@input=${(e) => (this.instead = e.target.value)} />
|
|
228
|
+
<datalist id="figure-retire-instead">${this.typeNames.map(name => html `<option value=${name}></option>`)}</datalist>
|
|
229
|
+
<button primary ?disabled=${this.retireBusy} @click=${() => this.setRetired(saved, true)}>${i18next.t('figure.button.retire')}</button>
|
|
230
|
+
<button ?disabled=${this.retireBusy} @click=${() => { this.retiring = false; this.retireError = ''; }}>${i18next.t('figure.button.cancel')}</button>
|
|
231
|
+
</div>
|
|
232
|
+
${this.retireError ? html `<p retire-error role="alert">${this.retireError}</p>` : nothing}`;
|
|
233
|
+
}
|
|
234
|
+
/** Public to other domains, or not (G2): they read its releases and draw it by full name; its draft stays here. */
|
|
235
|
+
publishRow(saved) {
|
|
236
|
+
return html `<div publish>
|
|
237
|
+
${saved.public ? html `<span published>${i18next.t('figure.label.public')}</span>` : nothing}
|
|
238
|
+
<button ?disabled=${this.retireBusy} @click=${() => this.setPublic(saved, !saved.public)}>${i18next.t(saved.public ? 'figure.button.make-private' : 'figure.button.make-public')}</button>
|
|
239
|
+
</div>
|
|
240
|
+
${this.publishError ? html `<p retire-error role="alert">${this.publishError}</p>` : nothing}`;
|
|
241
|
+
}
|
|
242
|
+
async setPublic(saved, isPublic) {
|
|
243
|
+
this.retireBusy = true;
|
|
244
|
+
this.publishError = '';
|
|
245
|
+
try {
|
|
246
|
+
const figure = await this.publish(saved.id, isPublic);
|
|
247
|
+
if (this.record?.saved.id === saved.id)
|
|
248
|
+
this.record = { ...this.record, saved: { ...this.record.saved, ...figure } };
|
|
249
|
+
window.dispatchEvent(new CustomEvent(FIGURE_PUBLIC_CHANGED, { detail: { id: saved.id } }));
|
|
250
|
+
}
|
|
251
|
+
catch (e) {
|
|
252
|
+
this.publishError = e.message;
|
|
253
|
+
}
|
|
254
|
+
finally {
|
|
255
|
+
this.retireBusy = false;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
async openRetire(saved) {
|
|
259
|
+
this.retiring = true;
|
|
260
|
+
this.instead = '';
|
|
261
|
+
this.retireError = '';
|
|
262
|
+
try {
|
|
263
|
+
this.typeNames = (await this.typeNamesOf()).filter(name => name !== saved.type);
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
this.typeNames = [];
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
async setRetired(saved, deprecated) {
|
|
270
|
+
this.retireBusy = true;
|
|
271
|
+
this.retireError = '';
|
|
272
|
+
try {
|
|
273
|
+
const figure = await this.retire(saved.id, deprecated, deprecated ? this.instead.trim() || null : null);
|
|
274
|
+
if (this.record?.saved.id === saved.id)
|
|
275
|
+
this.record = { ...this.record, saved: { ...this.record.saved, ...figure } };
|
|
276
|
+
this.retiring = false;
|
|
277
|
+
window.dispatchEvent(new CustomEvent(FIGURE_RETIREMENT_CHANGED, { detail: { id: saved.id } }));
|
|
278
|
+
}
|
|
279
|
+
catch (e) {
|
|
280
|
+
this.retireError = e.message;
|
|
281
|
+
}
|
|
282
|
+
finally {
|
|
283
|
+
this.retireBusy = false;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
171
286
|
render() {
|
|
172
287
|
const saved = this.record?.saved;
|
|
173
288
|
const released = this.record?.released;
|
|
@@ -179,24 +294,26 @@ let FigureDetailDock = class FigureDetailDock extends localize(i18next)(LitEleme
|
|
|
179
294
|
return html `<header>
|
|
180
295
|
<div title-block><h2 title=${saved?.name || ''}>${saved?.name || i18next.t('figure.title.detail')}</h2>
|
|
181
296
|
${saved ? html `<code title=${fullName(saved)}>${fullName(saved)}</code>` : nothing}</div>
|
|
182
|
-
|
|
297
|
+
${this.record?.foreign ? nothing : html `<button icon edit aria-label=${i18next.t('figure.button.detail-edit')} title=${i18next.t('figure.button.detail-edit')} ?disabled=${!saved}
|
|
183
298
|
@click=${() => {
|
|
184
299
|
// The modeller shows the figure itself; a preview of it beside the editor only covers the work.
|
|
185
300
|
hideDetail();
|
|
186
301
|
navigate(modellerPathOf(saved));
|
|
187
|
-
}}><md-icon>edit</md-icon></button
|
|
302
|
+
}}><md-icon>edit</md-icon></button>`}
|
|
188
303
|
<button icon aria-label=${i18next.t('figure.button.close')} title=${i18next.t('figure.button.close')} @click=${() => hideDetail()}><md-icon>close</md-icon></button>
|
|
189
304
|
</header>
|
|
190
305
|
<div body>
|
|
191
306
|
${this.loading ? html `<p status role="status">${i18next.t('figure.text.detail-loading')}</p>` : nothing}
|
|
192
307
|
${this.error ? html `<div status role="alert"><p>${this.error}</p><button act @click=${() => this.show(this.figureId)}><md-icon>refresh</md-icon>${i18next.t('figure.button.detail-retry')}</button></div>` : nothing}
|
|
193
308
|
${saved ? html `<section meta>
|
|
194
|
-
${released
|
|
195
|
-
? html `<div source
|
|
309
|
+
${released && this.record?.foreign
|
|
310
|
+
? html `<div source><span>${i18next.t('figure.label.detail-released', { version: released.version })}</span>${saved.domain?.name ? html `<span owner>${saved.domain.name}</span>` : nothing}</div>`
|
|
311
|
+
: released
|
|
312
|
+
? html `<div source role="group" aria-label=${i18next.t('figure.label.detail-source')}>
|
|
196
313
|
<button aria-pressed=${String(this.sourceKind === 'released')} @click=${() => this.choose('released')}>${i18next.t('figure.label.detail-released', { version: released.version })}</button>
|
|
197
314
|
<button aria-pressed=${String(this.sourceKind === 'saved')} @click=${() => this.choose('saved')}>${savedLabel}</button>
|
|
198
315
|
</div>`
|
|
199
|
-
|
|
316
|
+
: html `<div source><span title=${i18next.t('figure.text.detail-unreleased')}>${savedLabel}</span></div>`}
|
|
200
317
|
${this.asset ? html `<dl stats>
|
|
201
318
|
<div><dt>${i18next.t('figure.label.placement-limit')}</dt><dd>${capacity
|
|
202
319
|
? i18next.t('figure.text.fits-n-instances', { n: capacity.instances.toLocaleString() })
|
|
@@ -205,9 +322,11 @@ let FigureDetailDock = class FigureDetailDock extends localize(i18next)(LitEleme
|
|
|
205
322
|
<div><dt>${i18next.t('figure.label.budget-groups')}</dt><dd>${cost?.groups ?? '—'}</dd></div>
|
|
206
323
|
<div><dt>${i18next.t('figure.label.budget-parts')}</dt><dd>${this.asset.appearance.length}</dd></div>
|
|
207
324
|
</dl>` : nothing}
|
|
325
|
+
${hasBeenReleased(saved) && !this.record?.foreign ? this.retireRow(saved) : nothing}
|
|
326
|
+
${hasBeenReleased(saved) && !this.record?.foreign ? this.publishRow(saved) : nothing}
|
|
208
327
|
</section>` : nothing}
|
|
209
328
|
${this.asset ? html `<figure-asset-viewer .asset=${this.asset}
|
|
210
|
-
.keepShot=${saved?.id ? (dataUrl) => this.keepThumbnail(saved.id, dataUrl) : undefined}>
|
|
329
|
+
.keepShot=${saved?.id && !this.record?.foreign ? (dataUrl) => this.keepThumbnail(saved.id, dataUrl) : undefined}>
|
|
211
330
|
${saved?.description ? html `<p slot="facts" description>${saved.description}</p>` : nothing}
|
|
212
331
|
${saved ? html `<figure-facts-list slot="facts" .asset=${this.asset} .row=${saved}></figure-facts-list>` : nothing}
|
|
213
332
|
</figure-asset-viewer>` : nothing}
|
|
@@ -222,6 +341,26 @@ __decorate([
|
|
|
222
341
|
property({ attribute: false }),
|
|
223
342
|
__metadata("design:type", Function)
|
|
224
343
|
], FigureDetailDock.prototype, "keepThumbnail", void 0);
|
|
344
|
+
__decorate([
|
|
345
|
+
property({ attribute: false }),
|
|
346
|
+
__metadata("design:type", Function)
|
|
347
|
+
], FigureDetailDock.prototype, "retire", void 0);
|
|
348
|
+
__decorate([
|
|
349
|
+
property({ attribute: false }),
|
|
350
|
+
__metadata("design:type", Function)
|
|
351
|
+
], FigureDetailDock.prototype, "typeNamesOf", void 0);
|
|
352
|
+
__decorate([
|
|
353
|
+
property({ attribute: false }),
|
|
354
|
+
__metadata("design:type", Function)
|
|
355
|
+
], FigureDetailDock.prototype, "publish", void 0);
|
|
356
|
+
__decorate([
|
|
357
|
+
property({ attribute: false }),
|
|
358
|
+
__metadata("design:type", Function)
|
|
359
|
+
], FigureDetailDock.prototype, "loadPublic", void 0);
|
|
360
|
+
__decorate([
|
|
361
|
+
state(),
|
|
362
|
+
__metadata("design:type", Object)
|
|
363
|
+
], FigureDetailDock.prototype, "publishError", void 0);
|
|
225
364
|
__decorate([
|
|
226
365
|
state(),
|
|
227
366
|
__metadata("design:type", Object)
|
|
@@ -250,6 +389,26 @@ __decorate([
|
|
|
250
389
|
state(),
|
|
251
390
|
__metadata("design:type", void 0)
|
|
252
391
|
], FigureDetailDock.prototype, "cost", void 0);
|
|
392
|
+
__decorate([
|
|
393
|
+
state(),
|
|
394
|
+
__metadata("design:type", Object)
|
|
395
|
+
], FigureDetailDock.prototype, "retiring", void 0);
|
|
396
|
+
__decorate([
|
|
397
|
+
state(),
|
|
398
|
+
__metadata("design:type", Object)
|
|
399
|
+
], FigureDetailDock.prototype, "instead", void 0);
|
|
400
|
+
__decorate([
|
|
401
|
+
state(),
|
|
402
|
+
__metadata("design:type", Array)
|
|
403
|
+
], FigureDetailDock.prototype, "typeNames", void 0);
|
|
404
|
+
__decorate([
|
|
405
|
+
state(),
|
|
406
|
+
__metadata("design:type", Object)
|
|
407
|
+
], FigureDetailDock.prototype, "retireError", void 0);
|
|
408
|
+
__decorate([
|
|
409
|
+
state(),
|
|
410
|
+
__metadata("design:type", Object)
|
|
411
|
+
], FigureDetailDock.prototype, "retireBusy", void 0);
|
|
253
412
|
FigureDetailDock = __decorate([
|
|
254
413
|
customElement('figure-detail-dock')
|
|
255
414
|
], FigureDetailDock);
|
|
@@ -275,7 +434,8 @@ let openedOn;
|
|
|
275
434
|
function hideDetail() {
|
|
276
435
|
store.dispatch({ type: UPDATE_VIEWPART, name: VIEWPART, override: { show: false } });
|
|
277
436
|
}
|
|
278
|
-
|
|
437
|
+
/** Opens the preview of a figure; `publicRow` for another domain's public figure, shown by its release alone. */
|
|
438
|
+
export function openFigureDetail(figure, publicRow = false) {
|
|
279
439
|
if (!dock) {
|
|
280
440
|
dock = document.createElement('figure-detail-dock');
|
|
281
441
|
}
|
|
@@ -283,6 +443,6 @@ export function openFigureDetail(figure) {
|
|
|
283
443
|
store.dispatch({ type: UPDATE_VIEWPART, name: ASSISTANT_DOCK_VIEWPART, override: { show: false } });
|
|
284
444
|
appendViewpart({ name: VIEWPART, position: VIEWPART_POSITION.ASIDEBAR,
|
|
285
445
|
viewpart: { show: true, persistent: true, level: VIEWPART_LEVEL.TOPMOST, template: html `${dock}` } });
|
|
286
|
-
void dock.show(figure.id);
|
|
446
|
+
void dock.show(figure.id, publicRow ? figure : undefined);
|
|
287
447
|
}
|
|
288
448
|
//# sourceMappingURL=figure-detail-dock.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"figure-detail-dock.js","sourceRoot":"","sources":["../../client/viewparts/figure-detail-dock.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport { css, html, LitElement, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { i18next, localize } from '@operato/i18n'\nimport { appendViewpart, UPDATE_VIEWPART, VIEWPART_POSITION, VIEWPART_LEVEL } from '@operato/layout'\nimport { navigate, store } from '@operato/shell'\nimport { ScrollbarStyles } from '@operato/styles'\nimport { ASSISTANT_DOCK_VIEWPART } from '@things-factory/ai-assistant/dist-client/components/assistant-dock.js'\nimport { figureCostOf, parseFigureAsset, type FigureAsset } from '@hatiolab/figure-model'\nimport { capacityFrom } from '../modeller/scene-budget.js'\nimport { SidePanelStyles } from '@things-factory/figure-viewer/dist-client/side-panel-styles.js'\nimport { fetchFigure, fetchReleasedFigure, updateFigure } from '../graphql/index.js'\nimport { modellerPathOf } from '../modeller/modeller-route.js'\nimport { leftOpeningPage } from './detail-route.js'\nimport type { Figure } from '../types.js'\nimport '@things-factory/figure-viewer/dist-client/figure-asset-viewer.js'\nimport '@things-factory/figure-viewer/dist-client/figure-facts-list.js'\n\nconst VIEWPART = 'figure-detail'\n/** Said on window when a figure's card picture was replaced from the preview: `{ detail: { id } }`. */\nexport const FIGURE_THUMBNAIL_CHANGED = 'figure-thumbnail-changed'\nexport interface FigureDetailRecord {\n saved: Figure\n released?: { version: number; source: string }\n}\n\n/** Catalogue details stay while the catalogue is shown; choosing a size or a pose has no write path. */\n@customElement('figure-detail-dock')\nexport class FigureDetailDock extends localize(i18next)(LitElement) {\n /*\n The modeller's side panel look (SidePanelStyles). What the preview is opened for sits above the model -- name, type,\n which model is shown, how many fit -- as plain text; the controls are borderless icons and quiet text, so the model\n and its numbers are what the eye meets.\n */\n static styles = [ScrollbarStyles, SidePanelStyles, css`\n :host { display: flex; flex-direction: column; width: 400px; max-width: 100vw; height: 100%; min-height: 0;\n background: var(--md-sys-color-surface); border-left: 1px solid var(--md-sys-color-outline-variant); }\n header { display: flex; align-items: flex-start; gap: 2px; padding: 12px 8px 4px 16px; }\n [title-block] { flex: 1; min-width: 0; display: grid; gap: 2px; }\n h2 { margin: 0; font-size: var(--ui-t-title, 16px); font-weight: 600; line-height: 1.3; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n code { font-family: var(--ui-mono, ui-monospace, monospace); color: var(--md-sys-color-on-surface-variant); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n button { font: inherit; color: inherit; cursor: pointer; }\n button:focus-visible { outline: 2px solid var(--md-sys-color-primary); outline-offset: 1px; }\n button[icon] { display: inline-grid; place-items: center; width: 28px; height: 28px; padding: 0; border: none; border-radius: 6px;\n background: transparent; color: var(--md-sys-color-on-surface-variant); }\n button[icon]:hover:not(:disabled) { background: var(--md-sys-color-surface-container-high); color: var(--md-sys-color-on-surface); }\n button[icon]:disabled { opacity: 0.38; cursor: default; }\n button[icon] md-icon { --md-icon-size: 18px; }\n /*\n The figure stays in view under what it is (which model, how many fit) and its tabs; only the tab's body scrolls\n (user 2026-10-15: a clip played at the bottom of a scrolled dock moved a figure no longer seen).\n */\n [body] { flex: 1; display: flex; flex-direction: column; min-height: 0; }\n [body] > * { flex: none; }\n [body] > figure-asset-viewer { flex: 1; }\n [meta] { display: grid; gap: 8px; padding: 0 16px 12px; }\n /* Which model is shown: words that change colour, not boxes. */\n [source] { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 12px; color: var(--md-sys-color-on-surface-variant); }\n [source] button { padding: 0; border: none; background: none; color: var(--md-sys-color-on-surface-variant); }\n [source] button:hover { color: var(--md-sys-color-on-surface); }\n [source] button[aria-pressed='true'] { color: var(--md-sys-color-primary); font-weight: 600; }\n dl { margin: 0; }\n dt { color: var(--md-sys-color-on-surface-variant); }\n dd { margin: 0; color: var(--md-sys-color-on-surface); }\n dl[stats] { display: flex; flex-wrap: wrap; gap: 4px 16px; }\n dl[stats] > div { display: flex; align-items: baseline; gap: 4px; }\n dl[stats] dd { font-weight: 600; font-variant-numeric: tabular-nums; }\n p { margin: 0; line-height: 1.5; }\n [description] { padding: 10px 16px 0; color: var(--md-sys-color-on-surface-variant); }\n [status] { display: grid; gap: 8px; justify-items: start; padding: 12px 16px; }\n @media (max-width: 1439.98px) {\n :host { position: fixed; inset: var(--figure-detail-top, 44px) 0 0 auto; height: calc(100dvh - var(--figure-detail-top, 44px)); z-index: 11; box-shadow: -4px 0 16px rgb(0 0 0 / 20%); }\n }\n `]\n @property({ attribute: false }) load: (id: string) => Promise<FigureDetailRecord> = async id => {\n const [saved, released] = await Promise.all([fetchFigure(id), fetchReleasedFigure(id)])\n if (!saved?.source) throw Error(i18next.t('figure.text.detail-no-source'))\n return { saved, released }\n }\n /**\n * Keeps the picture taken in the preview as the figure's card picture. A patch of the picture alone does not make a\n * released figure a draft (`updateFigure`). The catalogue is told, and reads its cards again.\n */\n @property({ attribute: false }) keepThumbnail: (id: string, dataUrl: string) => Promise<unknown> = async (id, dataUrl) => {\n await updateFigure(id, { thumbnail: dataUrl })\n window.dispatchEvent(new CustomEvent(FIGURE_THUMBNAIL_CHANGED, { detail: { id } }))\n }\n @state() private figureId = ''\n @state() private record?: FigureDetailRecord\n @state() private asset?: FigureAsset\n @state() private sourceKind: 'saved' | 'released' = 'saved'\n @state() private loading = false\n @state() private error = ''\n @state() private cost?: ReturnType<typeof figureCostOf>\n private ticket = 0\n private unsubscribe?: () => void\n private boundary?: Element\n private boundaryObserver?: ResizeObserver\n\n async show(id: string) {\n const ticket = ++this.ticket\n this.figureId = id\n this.record = undefined\n this.asset = undefined\n this.cost = undefined\n this.loading = true\n this.error = ''\n try {\n const record = await this.load(id)\n if (ticket !== this.ticket) return\n this.record = record\n this.choose(record.released ? 'released' : 'saved')\n } catch {\n if (ticket === this.ticket) this.error = i18next.t('figure.text.detail-load-failed')\n } finally {\n if (ticket === this.ticket) this.loading = false\n }\n }\n private choose(kind: 'saved' | 'released') {\n this.sourceKind = kind\n this.asset = undefined\n this.cost = undefined\n this.error = ''\n try {\n const source = kind === 'released' ? this.record?.released?.source : this.record?.saved.source\n if (!source) throw Error('missing source')\n const asset = parseFigureAsset(source)\n this.cost = figureCostOf(asset)\n this.asset = asset\n } catch {\n this.error = i18next.t('figure.text.detail-invalid-source')\n }\n }\n connectedCallback() {\n super.connectedCallback()\n window.addEventListener('resize', this.place)\n window.addEventListener('popstate', this.place)\n this.unsubscribe = store.subscribe(() => {\n const state = store.getState() as { layout?: { viewparts?: Record<string, { show?: boolean }> }; route?: { page?: string } }\n const parts = state.layout?.viewparts\n if (parts?.[VIEWPART]?.show && (parts?.[ASSISTANT_DOCK_VIEWPART]?.show || leftOpeningPage(openedOn, state.route?.page))) hideDetail()\n else requestAnimationFrame(this.place)\n })\n this.place()\n window.addEventListener('keydown', this.escape)\n }\n disconnectedCallback() {\n super.disconnectedCallback()\n window.removeEventListener('resize', this.place)\n window.removeEventListener('popstate', this.place)\n window.removeEventListener('keydown', this.escape)\n this.unsubscribe?.()\n this.boundaryObserver?.disconnect()\n this.boundary = undefined\n }\n private place = () => {\n const bar = visibleBar(document)\n if (bar !== this.boundary) {\n this.boundaryObserver?.disconnect()\n this.boundary = bar\n if (bar) {\n this.boundaryObserver = new ResizeObserver(this.place)\n this.boundaryObserver.observe(bar)\n }\n }\n this.style.setProperty('--figure-detail-top', `${bar?.getBoundingClientRect().bottom ?? 44}px`)\n }\n private escape = (e: KeyboardEvent) => {\n if (e.key === 'Escape') hideDetail()\n }\n render() {\n const saved = this.record?.saved\n const released = this.record?.released\n const cost = this.cost\n const capacity = cost ? capacityFrom(cost.groups, cost.triangles) : undefined\n const savedLabel = saved\n ? `${i18next.t('figure.label.detail-saved')} · ${i18next.t(saved.state === 'released' ? 'figure.label.figure-released' : 'figure.label.draft')}`\n : ''\n return html`<header>\n <div title-block><h2 title=${saved?.name || ''}>${saved?.name || i18next.t('figure.title.detail')}</h2>\n ${saved ? html`<code title=${fullName(saved)}>${fullName(saved)}</code>` : nothing}</div>\n <button icon edit aria-label=${i18next.t('figure.button.detail-edit')} title=${i18next.t('figure.button.detail-edit')} ?disabled=${!saved}\n @click=${() => {\n // The modeller shows the figure itself; a preview of it beside the editor only covers the work.\n hideDetail()\n navigate(modellerPathOf(saved))\n }}><md-icon>edit</md-icon></button>\n <button icon aria-label=${i18next.t('figure.button.close')} title=${i18next.t('figure.button.close')} @click=${() => hideDetail()}><md-icon>close</md-icon></button>\n </header>\n <div body>\n ${this.loading ? html`<p status role=\"status\">${i18next.t('figure.text.detail-loading')}</p>` : nothing}\n ${this.error ? html`<div status role=\"alert\"><p>${this.error}</p><button act @click=${() => this.show(this.figureId)}><md-icon>refresh</md-icon>${i18next.t('figure.button.detail-retry')}</button></div>` : nothing}\n ${saved ? html`<section meta>\n ${released\n ? html`<div source role=\"group\" aria-label=${i18next.t('figure.label.detail-source')}>\n <button aria-pressed=${String(this.sourceKind === 'released')} @click=${() => this.choose('released')}>${i18next.t('figure.label.detail-released', { version: released.version })}</button>\n <button aria-pressed=${String(this.sourceKind === 'saved')} @click=${() => this.choose('saved')}>${savedLabel}</button>\n </div>`\n : html`<div source><span title=${i18next.t('figure.text.detail-unreleased')}>${savedLabel}</span></div>`}\n ${this.asset ? html`<dl stats>\n <div><dt>${i18next.t('figure.label.placement-limit')}</dt><dd>${capacity\n ? i18next.t('figure.text.fits-n-instances', { n: capacity.instances.toLocaleString() })\n : i18next.t('figure.text.not-measured-yet')}</dd></div>\n <div><dt>${i18next.t('figure.label.triangles')}</dt><dd>${cost?.triangles.toLocaleString() ?? '—'}</dd></div>\n <div><dt>${i18next.t('figure.label.budget-groups')}</dt><dd>${cost?.groups ?? '—'}</dd></div>\n <div><dt>${i18next.t('figure.label.budget-parts')}</dt><dd>${this.asset.appearance.length}</dd></div>\n </dl>` : nothing}\n </section>` : nothing}\n ${this.asset ? html`<figure-asset-viewer .asset=${this.asset}\n .keepShot=${saved?.id ? (dataUrl: string) => this.keepThumbnail(saved.id, dataUrl) : undefined}>\n ${saved?.description ? html`<p slot=\"facts\" description>${saved.description}</p>` : nothing}\n ${saved ? html`<figure-facts-list slot=\"facts\" .asset=${this.asset} .row=${saved}></figure-facts-list>` : nothing}\n </figure-asset-viewer>` : nothing}\n </div>`\n }\n}\n\n/** The name a board stores: `{namespace}/{type}` (ADR-0114). */\nfunction fullName(figure: Figure): string {\n return figure.namespace ? `${figure.namespace}/${figure.type}` : figure.type\n}\n\nfunction visibleBar(root: Document | ShadowRoot): Element | undefined {\n for (const element of root.querySelectorAll('*')) {\n if (element.tagName === 'OX-CANVAS-BAR' && element.getBoundingClientRect().height > 0) return element\n if (element.shadowRoot) {\n const found = visibleBar(element.shadowRoot)\n if (found) return found\n }\n }\n}\n\nlet dock: FigureDetailDock | undefined\n/** The page the preview was opened on; the preview belongs to that page and closes when the shell leaves it. */\nlet openedOn: string | undefined\nfunction hideDetail() {\n store.dispatch({ type: UPDATE_VIEWPART, name: VIEWPART, override: { show: false } })\n}\nexport function openFigureDetail(figure: Figure) {\n if (!dock) {\n dock = document.createElement('figure-detail-dock') as FigureDetailDock\n }\n openedOn = (store.getState() as { route?: { page?: string } }).route?.page\n store.dispatch({ type: UPDATE_VIEWPART, name: ASSISTANT_DOCK_VIEWPART, override: { show: false } })\n appendViewpart({ name: VIEWPART, position: VIEWPART_POSITION.ASIDEBAR,\n viewpart: { show: true, persistent: true, level: VIEWPART_LEVEL.TOPMOST, template: html`${dock}` } })\n void dock.show(figure.id)\n}\n"]}
|
|
1
|
+
{"version":3,"file":"figure-detail-dock.js","sourceRoot":"","sources":["../../client/viewparts/figure-detail-dock.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport { css, html, LitElement, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { i18next, localize } from '@operato/i18n'\nimport { appendViewpart, UPDATE_VIEWPART, VIEWPART_POSITION, VIEWPART_LEVEL } from '@operato/layout'\nimport { navigate, store } from '@operato/shell'\nimport { ScrollbarStyles } from '@operato/styles'\nimport { ASSISTANT_DOCK_VIEWPART } from '@things-factory/ai-assistant/dist-client/components/assistant-dock.js'\nimport { figureCostOf, parseFigureAsset, type FigureAsset } from '@hatiolab/figure-model'\nimport { capacityFrom } from '../modeller/scene-budget.js'\nimport { SidePanelStyles } from '@things-factory/figure-viewer/dist-client/side-panel-styles.js'\nimport { fetchFigure, fetchFigureTypeNames, fetchReleasedFigure, setFigureDeprecation, setFigurePublic, updateFigure } from '../graphql/index.js'\nimport { hasBeenReleased } from '../figure-released.js'\nimport { modellerPathOf } from '../modeller/modeller-route.js'\nimport { leftOpeningPage } from './detail-route.js'\nimport type { Figure } from '../types.js'\nimport '@things-factory/figure-viewer/dist-client/figure-asset-viewer.js'\nimport '@things-factory/figure-viewer/dist-client/figure-facts-list.js'\n\nconst VIEWPART = 'figure-detail'\n/** Said on window when a figure's card picture was replaced from the preview: `{ detail: { id } }`. */\nexport const FIGURE_THUMBNAIL_CHANGED = 'figure-thumbnail-changed'\n/** Said on window when a figure was retired or brought back: `{ detail: { id } }`. */\nexport const FIGURE_RETIREMENT_CHANGED = 'figure-retirement-changed'\n/** Said on window when a figure was made public or private: `{ detail: { id } }`. */\nexport const FIGURE_PUBLIC_CHANGED = 'figure-public-changed'\nexport interface FigureDetailRecord {\n saved: Figure\n released?: { version: number; source: string }\n /** Another domain's public figure (G2): only its release is shown, and nothing on it is changed from here. */\n foreign?: boolean\n}\n\n/** Catalogue details stay while the catalogue is shown; choosing a size or a pose has no write path. */\n@customElement('figure-detail-dock')\nexport class FigureDetailDock extends localize(i18next)(LitElement) {\n /*\n The modeller's side panel look (SidePanelStyles). What the preview is opened for sits above the model -- name, type,\n which model is shown, how many fit -- as plain text; the controls are borderless icons and quiet text, so the model\n and its numbers are what the eye meets.\n */\n static styles = [ScrollbarStyles, SidePanelStyles, css`\n :host { display: flex; flex-direction: column; width: 400px; max-width: 100vw; height: 100%; min-height: 0;\n background: var(--md-sys-color-surface); border-left: 1px solid var(--md-sys-color-outline-variant); }\n header { display: flex; align-items: flex-start; gap: 2px; padding: 12px 8px 4px 16px; }\n [title-block] { flex: 1; min-width: 0; display: grid; gap: 2px; }\n h2 { margin: 0; font-size: var(--ui-t-title, 16px); font-weight: 600; line-height: 1.3; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n code { font-family: var(--ui-mono, ui-monospace, monospace); color: var(--md-sys-color-on-surface-variant); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n button { font: inherit; color: inherit; cursor: pointer; }\n button:focus-visible { outline: 2px solid var(--md-sys-color-primary); outline-offset: 1px; }\n button[icon] { display: inline-grid; place-items: center; width: 28px; height: 28px; padding: 0; border: none; border-radius: 6px;\n background: transparent; color: var(--md-sys-color-on-surface-variant); }\n button[icon]:hover:not(:disabled) { background: var(--md-sys-color-surface-container-high); color: var(--md-sys-color-on-surface); }\n button[icon]:disabled { opacity: 0.38; cursor: default; }\n button[icon] md-icon { --md-icon-size: 18px; }\n /*\n The figure stays in view under what it is (which model, how many fit) and its tabs; only the tab's body scrolls\n (user 2026-10-15: a clip played at the bottom of a scrolled dock moved a figure no longer seen).\n */\n [body] { flex: 1; display: flex; flex-direction: column; min-height: 0; }\n [body] > * { flex: none; }\n [body] > figure-asset-viewer { flex: 1; }\n [meta] { display: grid; gap: 8px; padding: 0 16px 12px; }\n /* Which model is shown: words that change colour, not boxes. */\n [source] { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 12px; color: var(--md-sys-color-on-surface-variant); }\n [source] button { padding: 0; border: none; background: none; color: var(--md-sys-color-on-surface-variant); }\n [source] button:hover { color: var(--md-sys-color-on-surface); }\n [source] button[aria-pressed='true'] { color: var(--md-sys-color-primary); font-weight: 600; }\n dl { margin: 0; }\n dt { color: var(--md-sys-color-on-surface-variant); }\n dd { margin: 0; color: var(--md-sys-color-on-surface); }\n dl[stats] { display: flex; flex-wrap: wrap; gap: 4px 16px; }\n dl[stats] > div { display: flex; align-items: baseline; gap: 4px; }\n dl[stats] dd { font-weight: 600; font-variant-numeric: tabular-nums; }\n p { margin: 0; line-height: 1.5; }\n [description] { padding: 10px 16px 0; color: var(--md-sys-color-on-surface-variant); }\n [status] { display: grid; gap: 8px; justify-items: start; padding: 12px 16px; }\n /* Retiring: quiet words in the line under what is shown, like the source switch above it. */\n [retire], [publish] { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; color: var(--md-sys-color-on-surface-variant); }\n [retire] button, [publish] button { padding: 0; border: none; background: none; color: var(--md-sys-color-on-surface-variant); }\n [retire] button:hover:not(:disabled), [publish] button:hover:not(:disabled) { color: var(--md-sys-color-on-surface); }\n [publish] [published] { color: var(--md-sys-color-on-surface); font-weight: 600; }\n [retire] button[primary] { color: var(--md-sys-color-primary); font-weight: 600; }\n [retire] input { font: inherit; min-width: 0; flex: 1 1 160px; padding: 2px 6px; border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px; background: var(--md-sys-color-surface); color: var(--md-sys-color-on-surface); }\n [retire] [retired] { color: var(--md-sys-color-on-surface); font-weight: 600; }\n [retire-error] { color: var(--md-sys-color-error); }\n @media (max-width: 1439.98px) {\n :host { position: fixed; inset: var(--figure-detail-top, 44px) 0 0 auto; height: calc(100dvh - var(--figure-detail-top, 44px)); z-index: 11; box-shadow: -4px 0 16px rgb(0 0 0 / 20%); }\n }\n `]\n @property({ attribute: false }) load: (id: string) => Promise<FigureDetailRecord> = async id => {\n const [saved, released] = await Promise.all([fetchFigure(id), fetchReleasedFigure(id)])\n if (!saved?.source) throw Error(i18next.t('figure.text.detail-no-source'))\n return { saved, released }\n }\n /**\n * Keeps the picture taken in the preview as the figure's card picture. A patch of the picture alone does not make a\n * released figure a draft (`updateFigure`). The catalogue is told, and reads its cards again.\n */\n @property({ attribute: false }) keepThumbnail: (id: string, dataUrl: string) => Promise<unknown> = async (id, dataUrl) => {\n await updateFigure(id, { thumbnail: dataUrl })\n window.dispatchEvent(new CustomEvent(FIGURE_THUMBNAIL_CHANGED, { detail: { id } }))\n }\n /** Retires a released figure or brings it back (`setFigureDeprecation`); a check gives its own. */\n @property({ attribute: false }) retire: (id: string, deprecated: boolean, supersededBy: string | null) => Promise<Figure> = setFigureDeprecation\n /** The types a retired figure may name instead: the domain's other figures. */\n @property({ attribute: false }) typeNamesOf: () => Promise<string[]> = fetchFigureTypeNames\n /** Makes a released figure public or private (`setFigurePublic`); a check gives its own. */\n @property({ attribute: false }) publish: (id: string, isPublic: boolean) => Promise<Figure> = setFigurePublic\n /** Another domain's public figure as the catalogue listed it, with its latest release; its draft is not this domain's to open. */\n @property({ attribute: false }) loadPublic: (row: Figure) => Promise<FigureDetailRecord> = async row => ({\n saved: row,\n released: await fetchReleasedFigure(row.id),\n foreign: true\n })\n @state() private publishError = ''\n @state() private figureId = ''\n @state() private record?: FigureDetailRecord\n @state() private asset?: FigureAsset\n @state() private sourceKind: 'saved' | 'released' = 'saved'\n @state() private loading = false\n @state() private error = ''\n @state() private cost?: ReturnType<typeof figureCostOf>\n @state() private retiring = false\n @state() private instead = ''\n @state() private typeNames: string[] = []\n @state() private retireError = ''\n @state() private retireBusy = false\n private ticket = 0\n private unsubscribe?: () => void\n private boundary?: Element\n private boundaryObserver?: ResizeObserver\n\n async show(id: string, publicRow?: Figure) {\n const ticket = ++this.ticket\n this.figureId = id\n this.record = undefined\n this.asset = undefined\n this.cost = undefined\n this.loading = true\n this.error = ''\n this.retiring = false\n this.retireError = ''\n this.publishError = ''\n try {\n const record = publicRow ? await this.loadPublic(publicRow) : await this.load(id)\n if (ticket !== this.ticket) return\n this.record = record\n this.choose(record.released ? 'released' : 'saved')\n } catch {\n if (ticket === this.ticket) this.error = i18next.t('figure.text.detail-load-failed')\n } finally {\n if (ticket === this.ticket) this.loading = false\n }\n }\n private choose(kind: 'saved' | 'released') {\n this.sourceKind = kind\n this.asset = undefined\n this.cost = undefined\n this.error = ''\n try {\n const source = kind === 'released' ? this.record?.released?.source : this.record?.saved.source\n if (!source) throw Error('missing source')\n const asset = parseFigureAsset(source)\n this.cost = figureCostOf(asset)\n this.asset = asset\n } catch {\n this.error = i18next.t('figure.text.detail-invalid-source')\n }\n }\n connectedCallback() {\n super.connectedCallback()\n window.addEventListener('resize', this.place)\n window.addEventListener('popstate', this.place)\n this.unsubscribe = store.subscribe(() => {\n const state = store.getState() as { layout?: { viewparts?: Record<string, { show?: boolean }> }; route?: { page?: string } }\n const parts = state.layout?.viewparts\n if (parts?.[VIEWPART]?.show && (parts?.[ASSISTANT_DOCK_VIEWPART]?.show || leftOpeningPage(openedOn, state.route?.page))) hideDetail()\n else requestAnimationFrame(this.place)\n })\n this.place()\n window.addEventListener('keydown', this.escape)\n }\n disconnectedCallback() {\n super.disconnectedCallback()\n window.removeEventListener('resize', this.place)\n window.removeEventListener('popstate', this.place)\n window.removeEventListener('keydown', this.escape)\n this.unsubscribe?.()\n this.boundaryObserver?.disconnect()\n this.boundary = undefined\n }\n private place = () => {\n const bar = visibleBar(document)\n if (bar !== this.boundary) {\n this.boundaryObserver?.disconnect()\n this.boundary = bar\n if (bar) {\n this.boundaryObserver = new ResizeObserver(this.place)\n this.boundaryObserver.observe(bar)\n }\n }\n this.style.setProperty('--figure-detail-top', `${bar?.getBoundingClientRect().bottom ?? 44}px`)\n }\n private escape = (e: KeyboardEvent) => {\n if (e.key === 'Escape') hideDetail()\n }\n /**\n * Retiring a released figure (it stays released and served; boards already using it keep it), naming the type to use\n * instead, or bringing it back. A figure never released is deleted from the catalogue instead.\n */\n private retireRow(saved: Figure) {\n if (saved.deprecated) {\n return html`<div retire>\n <span retired>${saved.supersededBy\n ? i18next.t('figure.text.retired-instead', { type: saved.supersededBy })\n : i18next.t('figure.label.retired')}</span>\n <button ?disabled=${this.retireBusy} @click=${() => this.setRetired(saved, false)}>${i18next.t('figure.button.unretire')}</button>\n </div>\n ${this.retireError ? html`<p retire-error role=\"alert\">${this.retireError}</p>` : nothing}`\n }\n if (!this.retiring) {\n return html`<div retire><button @click=${() => this.openRetire(saved)}>${i18next.t('figure.button.retire')}</button></div>`\n }\n return html`<div retire>\n <input list=\"figure-retire-instead\" aria-label=${i18next.t('figure.label.superseded-by')}\n placeholder=${i18next.t('figure.label.superseded-by')} .value=${this.instead}\n @input=${(e: Event) => (this.instead = (e.target as HTMLInputElement).value)} />\n <datalist id=\"figure-retire-instead\">${this.typeNames.map(name => html`<option value=${name}></option>`)}</datalist>\n <button primary ?disabled=${this.retireBusy} @click=${() => this.setRetired(saved, true)}>${i18next.t('figure.button.retire')}</button>\n <button ?disabled=${this.retireBusy} @click=${() => { this.retiring = false; this.retireError = '' }}>${i18next.t('figure.button.cancel')}</button>\n </div>\n ${this.retireError ? html`<p retire-error role=\"alert\">${this.retireError}</p>` : nothing}`\n }\n /** Public to other domains, or not (G2): they read its releases and draw it by full name; its draft stays here. */\n private publishRow(saved: Figure) {\n return html`<div publish>\n ${saved.public ? html`<span published>${i18next.t('figure.label.public')}</span>` : nothing}\n <button ?disabled=${this.retireBusy} @click=${() => this.setPublic(saved, !saved.public)}>${i18next.t(saved.public ? 'figure.button.make-private' : 'figure.button.make-public')}</button>\n </div>\n ${this.publishError ? html`<p retire-error role=\"alert\">${this.publishError}</p>` : nothing}`\n }\n private async setPublic(saved: Figure, isPublic: boolean) {\n this.retireBusy = true\n this.publishError = ''\n try {\n const figure = await this.publish(saved.id, isPublic)\n if (this.record?.saved.id === saved.id) this.record = { ...this.record, saved: { ...this.record.saved, ...figure } }\n window.dispatchEvent(new CustomEvent(FIGURE_PUBLIC_CHANGED, { detail: { id: saved.id } }))\n } catch (e) {\n this.publishError = (e as Error).message\n } finally {\n this.retireBusy = false\n }\n }\n private async openRetire(saved: Figure) {\n this.retiring = true\n this.instead = ''\n this.retireError = ''\n try {\n this.typeNames = (await this.typeNamesOf()).filter(name => name !== saved.type)\n } catch {\n this.typeNames = []\n }\n }\n private async setRetired(saved: Figure, deprecated: boolean) {\n this.retireBusy = true\n this.retireError = ''\n try {\n const figure = await this.retire(saved.id, deprecated, deprecated ? this.instead.trim() || null : null)\n if (this.record?.saved.id === saved.id) this.record = { ...this.record, saved: { ...this.record.saved, ...figure } }\n this.retiring = false\n window.dispatchEvent(new CustomEvent(FIGURE_RETIREMENT_CHANGED, { detail: { id: saved.id } }))\n } catch (e) {\n this.retireError = (e as Error).message\n } finally {\n this.retireBusy = false\n }\n }\n render() {\n const saved = this.record?.saved\n const released = this.record?.released\n const cost = this.cost\n const capacity = cost ? capacityFrom(cost.groups, cost.triangles) : undefined\n const savedLabel = saved\n ? `${i18next.t('figure.label.detail-saved')} · ${i18next.t(saved.state === 'released' ? 'figure.label.figure-released' : 'figure.label.draft')}`\n : ''\n return html`<header>\n <div title-block><h2 title=${saved?.name || ''}>${saved?.name || i18next.t('figure.title.detail')}</h2>\n ${saved ? html`<code title=${fullName(saved)}>${fullName(saved)}</code>` : nothing}</div>\n ${this.record?.foreign ? nothing : html`<button icon edit aria-label=${i18next.t('figure.button.detail-edit')} title=${i18next.t('figure.button.detail-edit')} ?disabled=${!saved}\n @click=${() => {\n // The modeller shows the figure itself; a preview of it beside the editor only covers the work.\n hideDetail()\n navigate(modellerPathOf(saved))\n }}><md-icon>edit</md-icon></button>`}\n <button icon aria-label=${i18next.t('figure.button.close')} title=${i18next.t('figure.button.close')} @click=${() => hideDetail()}><md-icon>close</md-icon></button>\n </header>\n <div body>\n ${this.loading ? html`<p status role=\"status\">${i18next.t('figure.text.detail-loading')}</p>` : nothing}\n ${this.error ? html`<div status role=\"alert\"><p>${this.error}</p><button act @click=${() => this.show(this.figureId)}><md-icon>refresh</md-icon>${i18next.t('figure.button.detail-retry')}</button></div>` : nothing}\n ${saved ? html`<section meta>\n ${released && this.record?.foreign\n ? html`<div source><span>${i18next.t('figure.label.detail-released', { version: released.version })}</span>${saved.domain?.name ? html`<span owner>${saved.domain.name}</span>` : nothing}</div>`\n : released\n ? html`<div source role=\"group\" aria-label=${i18next.t('figure.label.detail-source')}>\n <button aria-pressed=${String(this.sourceKind === 'released')} @click=${() => this.choose('released')}>${i18next.t('figure.label.detail-released', { version: released.version })}</button>\n <button aria-pressed=${String(this.sourceKind === 'saved')} @click=${() => this.choose('saved')}>${savedLabel}</button>\n </div>`\n : html`<div source><span title=${i18next.t('figure.text.detail-unreleased')}>${savedLabel}</span></div>`}\n ${this.asset ? html`<dl stats>\n <div><dt>${i18next.t('figure.label.placement-limit')}</dt><dd>${capacity\n ? i18next.t('figure.text.fits-n-instances', { n: capacity.instances.toLocaleString() })\n : i18next.t('figure.text.not-measured-yet')}</dd></div>\n <div><dt>${i18next.t('figure.label.triangles')}</dt><dd>${cost?.triangles.toLocaleString() ?? '—'}</dd></div>\n <div><dt>${i18next.t('figure.label.budget-groups')}</dt><dd>${cost?.groups ?? '—'}</dd></div>\n <div><dt>${i18next.t('figure.label.budget-parts')}</dt><dd>${this.asset.appearance.length}</dd></div>\n </dl>` : nothing}\n ${hasBeenReleased(saved) && !this.record?.foreign ? this.retireRow(saved) : nothing}\n ${hasBeenReleased(saved) && !this.record?.foreign ? this.publishRow(saved) : nothing}\n </section>` : nothing}\n ${this.asset ? html`<figure-asset-viewer .asset=${this.asset}\n .keepShot=${saved?.id && !this.record?.foreign ? (dataUrl: string) => this.keepThumbnail(saved.id, dataUrl) : undefined}>\n ${saved?.description ? html`<p slot=\"facts\" description>${saved.description}</p>` : nothing}\n ${saved ? html`<figure-facts-list slot=\"facts\" .asset=${this.asset} .row=${saved}></figure-facts-list>` : nothing}\n </figure-asset-viewer>` : nothing}\n </div>`\n }\n}\n\n/** The name a board stores: `{namespace}/{type}` (ADR-0114). */\nfunction fullName(figure: Figure): string {\n return figure.namespace ? `${figure.namespace}/${figure.type}` : figure.type\n}\n\nfunction visibleBar(root: Document | ShadowRoot): Element | undefined {\n for (const element of root.querySelectorAll('*')) {\n if (element.tagName === 'OX-CANVAS-BAR' && element.getBoundingClientRect().height > 0) return element\n if (element.shadowRoot) {\n const found = visibleBar(element.shadowRoot)\n if (found) return found\n }\n }\n}\n\nlet dock: FigureDetailDock | undefined\n/** The page the preview was opened on; the preview belongs to that page and closes when the shell leaves it. */\nlet openedOn: string | undefined\nfunction hideDetail() {\n store.dispatch({ type: UPDATE_VIEWPART, name: VIEWPART, override: { show: false } })\n}\n/** Opens the preview of a figure; `publicRow` for another domain's public figure, shown by its release alone. */\nexport function openFigureDetail(figure: Figure, publicRow = false) {\n if (!dock) {\n dock = document.createElement('figure-detail-dock') as FigureDetailDock\n }\n openedOn = (store.getState() as { route?: { page?: string } }).route?.page\n store.dispatch({ type: UPDATE_VIEWPART, name: ASSISTANT_DOCK_VIEWPART, override: { show: false } })\n appendViewpart({ name: VIEWPART, position: VIEWPART_POSITION.ASIDEBAR,\n viewpart: { show: true, persistent: true, level: VIEWPART_LEVEL.TOPMOST, template: html`${dock}` } })\n void dock.show(figure.id, publicRow ? figure : undefined)\n}\n"]}
|
|
@@ -27,6 +27,8 @@ declare const FigureThumb_base: (new (...args: any[]) => LitElement) & typeof Li
|
|
|
27
27
|
export declare class FigureThumb extends FigureThumb_base {
|
|
28
28
|
static styles: import("lit").CSSResult;
|
|
29
29
|
figure: Figure;
|
|
30
|
+
/** Another domain's public figure (G2): its card says whose it is and offers no delete. */
|
|
31
|
+
readonly: boolean;
|
|
30
32
|
render(): import("lit-html").TemplateResult<1>;
|
|
31
33
|
}
|
|
32
34
|
export {};
|
|
@@ -6,6 +6,7 @@ import { i18next, localize } from '@operato/i18n';
|
|
|
6
6
|
import '@operato/data-grist/ox-card-thumb.js';
|
|
7
7
|
import '@things-factory/figure-viewer/dist-client/figure-thumb-mark.js';
|
|
8
8
|
import { getPathInfo } from '@operato/utils';
|
|
9
|
+
import { hasBeenReleased } from '../figure-released.js';
|
|
9
10
|
/**
|
|
10
11
|
* 그림을 부르는 주소.
|
|
11
12
|
*
|
|
@@ -45,6 +46,11 @@ function thumbnailSrc(figure) {
|
|
|
45
46
|
* 위, 손이 갔을 때만 나오는 자리에 둔다.
|
|
46
47
|
*/
|
|
47
48
|
let FigureThumb = class FigureThumb extends localize(i18next)(LitElement) {
|
|
49
|
+
constructor() {
|
|
50
|
+
super(...arguments);
|
|
51
|
+
/** Another domain's public figure (G2): its card says whose it is and offers no delete. */
|
|
52
|
+
this.readonly = false;
|
|
53
|
+
}
|
|
48
54
|
static { this.styles = css `
|
|
49
55
|
:host {
|
|
50
56
|
display: block;
|
|
@@ -150,7 +156,7 @@ let FigureThumb = class FigureThumb extends localize(i18next)(LitElement) {
|
|
|
150
156
|
<ox-card-thumb
|
|
151
157
|
src=${src || ''}
|
|
152
158
|
alt=${figure.name || ''}
|
|
153
|
-
badge=${figure.state === 'released' ? '' : i18next.t('figure.label.draft')}
|
|
159
|
+
badge=${this.readonly ? figure.domain?.name ?? '' : figure.deprecated ? i18next.t('figure.label.retired') : figure.state === 'released' ? '' : i18next.t('figure.label.draft')}
|
|
154
160
|
>
|
|
155
161
|
<figure-thumb-mark slot="empty" .label=${i18next.t('figure.text.thumbnail-not-made-yet')}></figure-thumb-mark>
|
|
156
162
|
${figure.tags?.length
|
|
@@ -169,7 +175,7 @@ let FigureThumb = class FigureThumb extends localize(i18next)(LitElement) {
|
|
|
169
175
|
</div>`
|
|
170
176
|
: ''}
|
|
171
177
|
|
|
172
|
-
|
|
178
|
+
${this.readonly || hasBeenReleased(figure) ? '' : html `<button
|
|
173
179
|
remove
|
|
174
180
|
title=${i18next.t('figure.button.delete-figure')}
|
|
175
181
|
@click=${(e) => {
|
|
@@ -179,7 +185,7 @@ let FigureThumb = class FigureThumb extends localize(i18next)(LitElement) {
|
|
|
179
185
|
}}
|
|
180
186
|
>
|
|
181
187
|
<md-icon>delete</md-icon>
|
|
182
|
-
</button
|
|
188
|
+
</button>`}
|
|
183
189
|
</ox-card-thumb>
|
|
184
190
|
`;
|
|
185
191
|
}
|
|
@@ -188,6 +194,10 @@ __decorate([
|
|
|
188
194
|
property({ type: Object }),
|
|
189
195
|
__metadata("design:type", Object)
|
|
190
196
|
], FigureThumb.prototype, "figure", void 0);
|
|
197
|
+
__decorate([
|
|
198
|
+
property({ type: Boolean }),
|
|
199
|
+
__metadata("design:type", Object)
|
|
200
|
+
], FigureThumb.prototype, "readonly", void 0);
|
|
191
201
|
FigureThumb = __decorate([
|
|
192
202
|
customElement('figure-thumb')
|
|
193
203
|
], FigureThumb);
|