@geniee-inc/sfa-kit 1.6.0
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/GUIDE.md +585 -0
- package/LICENSE.md +21 -0
- package/README.md +31 -0
- package/package.json +36 -0
- package/src/index.d.ts +29 -0
- package/src/index.js +21 -0
- package/src/lib/auth/DevToolbar.d.ts +38 -0
- package/src/lib/auth/DevToolbar.js +271 -0
- package/src/lib/auth/SfaLoginGate.d.ts +53 -0
- package/src/lib/auth/SfaLoginGate.js +462 -0
- package/src/lib/auth/StandalonePreviewContext.d.ts +27 -0
- package/src/lib/auth/StandalonePreviewContext.js +14 -0
- package/src/lib/auth/embed-protocol.d.ts +40 -0
- package/src/lib/auth/embed-protocol.js +56 -0
- package/src/lib/auth/exchange-code.d.ts +14 -0
- package/src/lib/auth/exchange-code.js +45 -0
- package/src/lib/auth/keystone-auth.d.ts +16 -0
- package/src/lib/auth/keystone-auth.js +84 -0
- package/src/lib/auth/session-store.d.ts +19 -0
- package/src/lib/auth/session-store.js +73 -0
- package/src/lib/auth/types.d.ts +33 -0
- package/src/lib/auth/types.js +1 -0
- package/src/lib/auth/useEmbedContext.d.ts +22 -0
- package/src/lib/auth/useEmbedContext.js +31 -0
- package/src/lib/components/EmptyState.d.ts +18 -0
- package/src/lib/components/EmptyState.js +36 -0
- package/src/lib/components/ErrorBoundary.d.ts +31 -0
- package/src/lib/components/ErrorBoundary.js +100 -0
- package/src/lib/components/FieldRenderer.d.ts +11 -0
- package/src/lib/components/FieldRenderer.js +57 -0
- package/src/lib/components/RecordList.d.ts +20 -0
- package/src/lib/components/RecordList.js +139 -0
- package/src/lib/components/RecordView.d.ts +14 -0
- package/src/lib/components/RecordView.js +153 -0
- package/src/lib/errors.d.ts +15 -0
- package/src/lib/errors.js +15 -0
- package/src/lib/hooks/useCreateRecord.d.ts +17 -0
- package/src/lib/hooks/useCreateRecord.js +48 -0
- package/src/lib/hooks/useDeleteRecord.d.ts +16 -0
- package/src/lib/hooks/useDeleteRecord.js +48 -0
- package/src/lib/hooks/useDescribe.d.ts +16 -0
- package/src/lib/hooks/useDescribe.js +40 -0
- package/src/lib/hooks/useFieldTypes.d.ts +15 -0
- package/src/lib/hooks/useFieldTypes.js +39 -0
- package/src/lib/hooks/useObjectCatalog.d.ts +15 -0
- package/src/lib/hooks/useObjectCatalog.js +39 -0
- package/src/lib/hooks/useRecord.d.ts +15 -0
- package/src/lib/hooks/useRecord.js +49 -0
- package/src/lib/hooks/useRecordList.d.ts +19 -0
- package/src/lib/hooks/useRecordList.js +82 -0
- package/src/lib/hooks/useRelatedRecords.d.ts +65 -0
- package/src/lib/hooks/useRelatedRecords.js +227 -0
- package/src/lib/hooks/useUpdateRecord.d.ts +23 -0
- package/src/lib/hooks/useUpdateRecord.js +55 -0
- package/src/lib/provider/SfaClient.d.ts +98 -0
- package/src/lib/provider/SfaClient.js +688 -0
- package/src/lib/provider/SfaProvider.d.ts +38 -0
- package/src/lib/provider/SfaProvider.js +44 -0
- package/src/lib/provider/types.d.ts +184 -0
- package/src/lib/provider/types.js +1 -0
- package/src/lib/provider/useSfaClient.d.ts +11 -0
- package/src/lib/provider/useSfaClient.js +24 -0
- package/src/lib/theme.d.ts +55 -0
- package/src/lib/theme.js +68 -0
- package/src/lib/version.d.ts +1 -0
- package/src/lib/version.js +1 -0
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export { SfaApiError, SfaConflictError, SfaValidationError, } from './lib/provider/SfaClient';
|
|
2
|
+
export { SfaProvider } from './lib/provider/SfaProvider';
|
|
3
|
+
export { useSfaClient } from './lib/provider/useSfaClient';
|
|
4
|
+
export { SfaLoginGate } from './lib/auth/SfaLoginGate';
|
|
5
|
+
export { ExchangeError } from './lib/auth/exchange-code';
|
|
6
|
+
export { useEmbedContext } from './lib/auth/useEmbedContext';
|
|
7
|
+
export { useStandalonePreview } from './lib/auth/StandalonePreviewContext';
|
|
8
|
+
export type { EmbedContext } from './lib/auth/embed-protocol';
|
|
9
|
+
export type { StandalonePreviewState, StandalonePreviewControls, } from './lib/auth/StandalonePreviewContext';
|
|
10
|
+
export { useRecord } from './lib/hooks/useRecord';
|
|
11
|
+
export { useRecordList } from './lib/hooks/useRecordList';
|
|
12
|
+
export { useDescribe } from './lib/hooks/useDescribe';
|
|
13
|
+
export { useObjectCatalog } from './lib/hooks/useObjectCatalog';
|
|
14
|
+
export { useFieldTypes } from './lib/hooks/useFieldTypes';
|
|
15
|
+
export { useCreateRecord } from './lib/hooks/useCreateRecord';
|
|
16
|
+
export type { UseCreateRecordResult } from './lib/hooks/useCreateRecord';
|
|
17
|
+
export { useUpdateRecord } from './lib/hooks/useUpdateRecord';
|
|
18
|
+
export type { UseUpdateRecordResult, SfaConflictDetail, } from './lib/hooks/useUpdateRecord';
|
|
19
|
+
export { useDeleteRecord } from './lib/hooks/useDeleteRecord';
|
|
20
|
+
export type { UseDeleteRecordResult } from './lib/hooks/useDeleteRecord';
|
|
21
|
+
export { useRelatedRecords, RelationNotFoundError, resolveRelation, buildRelationFilter, } from './lib/hooks/useRelatedRecords';
|
|
22
|
+
export type { UseRelatedRecordsParams, UseRelatedRecordsOptions, UseRelatedRecordsResult, } from './lib/hooks/useRelatedRecords';
|
|
23
|
+
export { FieldRenderer } from './lib/components/FieldRenderer';
|
|
24
|
+
export { RecordView } from './lib/components/RecordView';
|
|
25
|
+
export { RecordList } from './lib/components/RecordList';
|
|
26
|
+
export { ErrorBoundary } from './lib/components/ErrorBoundary';
|
|
27
|
+
export { EmptyState } from './lib/components/EmptyState';
|
|
28
|
+
export type { SfaAuth, SfaApiMode, SfaClientConfig, SfaRecord, SfaListResponse, SfaListParams, SfaFieldValue, SfaFieldKind, SfaFieldValueMapping, FieldGroupSpec, FieldGroupLayout, SfaFieldDescriptor, SfaFieldOption, SfaRelationDescriptor, SfaObjectDetail, SfaObjectSummary, SfaObjectCatalog, SfaObjectPermissions, SfaObjectOrigin, SfaRelationshipKind, SfaCardinality, SfaDescribeResult, SfaFieldTypeDescriptor, SfaFieldTypeCatalog, SfaFilterOperator, SfaCreateOptions, SfaFieldError, } from './lib/provider/types';
|
|
29
|
+
export type { SfaSession, AuthResult, OrganizationChoice, } from './lib/auth/types';
|
package/src/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export { SfaApiError, SfaConflictError, SfaValidationError, } from './lib/provider/SfaClient';
|
|
2
|
+
export { SfaProvider } from './lib/provider/SfaProvider';
|
|
3
|
+
export { useSfaClient } from './lib/provider/useSfaClient';
|
|
4
|
+
export { SfaLoginGate } from './lib/auth/SfaLoginGate';
|
|
5
|
+
export { ExchangeError } from './lib/auth/exchange-code';
|
|
6
|
+
export { useEmbedContext } from './lib/auth/useEmbedContext';
|
|
7
|
+
export { useStandalonePreview } from './lib/auth/StandalonePreviewContext';
|
|
8
|
+
export { useRecord } from './lib/hooks/useRecord';
|
|
9
|
+
export { useRecordList } from './lib/hooks/useRecordList';
|
|
10
|
+
export { useDescribe } from './lib/hooks/useDescribe';
|
|
11
|
+
export { useObjectCatalog } from './lib/hooks/useObjectCatalog';
|
|
12
|
+
export { useFieldTypes } from './lib/hooks/useFieldTypes';
|
|
13
|
+
export { useCreateRecord } from './lib/hooks/useCreateRecord';
|
|
14
|
+
export { useUpdateRecord } from './lib/hooks/useUpdateRecord';
|
|
15
|
+
export { useDeleteRecord } from './lib/hooks/useDeleteRecord';
|
|
16
|
+
export { useRelatedRecords, RelationNotFoundError, resolveRelation, buildRelationFilter, } from './lib/hooks/useRelatedRecords';
|
|
17
|
+
export { FieldRenderer } from './lib/components/FieldRenderer';
|
|
18
|
+
export { RecordView } from './lib/components/RecordView';
|
|
19
|
+
export { RecordList } from './lib/components/RecordList';
|
|
20
|
+
export { ErrorBoundary } from './lib/components/ErrorBoundary';
|
|
21
|
+
export { EmptyState } from './lib/components/EmptyState';
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { StandalonePreviewControls } from './StandalonePreviewContext';
|
|
2
|
+
/** Which Chikyu tab slot the page is embedded into. */
|
|
3
|
+
export type StudioPageLevel = 'record' | 'object';
|
|
4
|
+
interface DevToolbarProps {
|
|
5
|
+
readonly organizationId: string;
|
|
6
|
+
readonly preview: StandalonePreviewControls;
|
|
7
|
+
/**
|
|
8
|
+
* Which tab slot the page is built for.
|
|
9
|
+
*
|
|
10
|
+
* - `'record'` — a record tab: the record ID input and fetch button are shown.
|
|
11
|
+
* - `'object'` — an object tab (list/dashboard): no record to pick, so the
|
|
12
|
+
* input, the fetch button and the separator are hidden.
|
|
13
|
+
*
|
|
14
|
+
* This is deliberately separate from `objectKey`: an object-level page still
|
|
15
|
+
* wants to declare which object it is for, and must not get a record input
|
|
16
|
+
* as a side effect of doing so.
|
|
17
|
+
*/
|
|
18
|
+
readonly level: StudioPageLevel;
|
|
19
|
+
/**
|
|
20
|
+
* The object this page is built for (e.g. `"companies"`).
|
|
21
|
+
*
|
|
22
|
+
* Used for display only — the page-info tooltip and the record ID
|
|
23
|
+
* placeholder — at both levels. Omit only when the page truly has no object.
|
|
24
|
+
*/
|
|
25
|
+
readonly objectKey?: string;
|
|
26
|
+
readonly onLogout: () => void;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* @description Persistent dev toolbar rendered only in standalone (non-embedded) mode.
|
|
30
|
+
* A solid black bar, visually separate from the page content. It does not appear
|
|
31
|
+
* when the page is embedded in GENIEE SFA/CRM.
|
|
32
|
+
*
|
|
33
|
+
* Features:
|
|
34
|
+
* - Record ID input with a fetch button, on record-level pages only.
|
|
35
|
+
* - Page info (object, org ID, sfa-kit version) behind a hover tooltip, logout.
|
|
36
|
+
*/
|
|
37
|
+
export declare function DevToolbar({ organizationId, preview, level, objectKey, onLogout, }: DevToolbarProps): JSX.Element;
|
|
38
|
+
export {};
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment, useCallback, useEffect, useId, useState } from 'react';
|
|
3
|
+
import { useSfaClient } from '../provider/useSfaClient';
|
|
4
|
+
import { font, baseButtonStyle } from '../theme';
|
|
5
|
+
import { SFA_KIT_VERSION } from '../version';
|
|
6
|
+
const TOOLBAR_BLACK = '#1a1a1a';
|
|
7
|
+
const SEPARATOR_GREY = '#666666';
|
|
8
|
+
const PLACEHOLDER_COLOR = 'rgba(0, 0, 0, 0.4)';
|
|
9
|
+
/** style 属性では書けない指定(疑似要素・疑似クラス)のために付けるクラス名 */
|
|
10
|
+
const RECORD_ID_INPUT_CLASS = 'sfa-kit-dev-toolbar-record-id';
|
|
11
|
+
/** ホバーで薄くする要素に付ける(取得ボタンとページ情報の両方) */
|
|
12
|
+
const HOVER_DIM_CLASS = 'sfa-kit-dev-toolbar-hover-dim';
|
|
13
|
+
const PAGE_INFO_CLASS = 'sfa-kit-dev-toolbar-page-info';
|
|
14
|
+
const PAGE_INFO_TOOLTIP_CLASS = 'sfa-kit-dev-toolbar-page-info-tip';
|
|
15
|
+
/**
|
|
16
|
+
* NOTE: 疑似要素・疑似クラス(::placeholder / :hover / :focus-within)は style
|
|
17
|
+
* 属性で指定できないため、上のクラスにだけ効く最小限の CSS を差し込む。
|
|
18
|
+
* ホバーの遷移は baseButtonStyle の transition が効く
|
|
19
|
+
*/
|
|
20
|
+
const DEV_TOOLBAR_CSS = `
|
|
21
|
+
.${RECORD_ID_INPUT_CLASS}::placeholder { color: ${PLACEHOLDER_COLOR}; opacity: 1; }
|
|
22
|
+
.${HOVER_DIM_CLASS} { opacity: 1; }
|
|
23
|
+
.${HOVER_DIM_CLASS}:hover { opacity: 0.8; }
|
|
24
|
+
.${PAGE_INFO_CLASS} .${PAGE_INFO_TOOLTIP_CLASS} { visibility: hidden; opacity: 0; }
|
|
25
|
+
.${PAGE_INFO_CLASS}:hover .${PAGE_INFO_TOOLTIP_CLASS},
|
|
26
|
+
.${PAGE_INFO_CLASS}:focus-within .${PAGE_INFO_TOOLTIP_CLASS} { visibility: visible; opacity: 1; }
|
|
27
|
+
`;
|
|
28
|
+
/**
|
|
29
|
+
* @description Persistent dev toolbar rendered only in standalone (non-embedded) mode.
|
|
30
|
+
* A solid black bar, visually separate from the page content. It does not appear
|
|
31
|
+
* when the page is embedded in GENIEE SFA/CRM.
|
|
32
|
+
*
|
|
33
|
+
* Features:
|
|
34
|
+
* - Record ID input with a fetch button, on record-level pages only.
|
|
35
|
+
* - Page info (object, org ID, sfa-kit version) behind a hover tooltip, logout.
|
|
36
|
+
*/
|
|
37
|
+
export function DevToolbar({ organizationId, preview, level, objectKey, onLogout, }) {
|
|
38
|
+
var _a, _b, _c, _d;
|
|
39
|
+
// NOTE: 入力欄を出すかは level だけで決める。objectKey の有無で決めていた頃は、
|
|
40
|
+
// 一覧ページが自分のオブジェクトを名乗るとレコード ID の入力欄まで付いてきた
|
|
41
|
+
const isRecordLevel = level === 'record';
|
|
42
|
+
const hasObjectKey = objectKey != null && objectKey !== '';
|
|
43
|
+
// NOTE: 1 ページに複数のツールバーが載ることがあるため id は useId で作る
|
|
44
|
+
const pageInfoTooltipId = useId();
|
|
45
|
+
// Inline catalog fetch. The useObjectCatalog hook lives in the data-hooks
|
|
46
|
+
// layer (next PR); DevToolbar only needs it to resolve the object's display
|
|
47
|
+
// name, so we call the client directly to avoid a cross-layer dependency.
|
|
48
|
+
// NOTE: 使うのは表示名だけなので、objectKey が無いページでは取得しない
|
|
49
|
+
const client = useSfaClient();
|
|
50
|
+
const [catalog, setCatalog] = useState([]);
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (!hasObjectKey)
|
|
53
|
+
return undefined;
|
|
54
|
+
let cancelled = false;
|
|
55
|
+
client.listObjects().then(result => {
|
|
56
|
+
if (!cancelled)
|
|
57
|
+
setCatalog(result.objects);
|
|
58
|
+
}, () => {
|
|
59
|
+
/* toolbar is best-effort; swallow errors */
|
|
60
|
+
});
|
|
61
|
+
return () => {
|
|
62
|
+
cancelled = true;
|
|
63
|
+
};
|
|
64
|
+
}, [client, hasObjectKey]);
|
|
65
|
+
const [localRecordId, setLocalRecordId] = useState((_b = (_a = preview.state) === null || _a === void 0 ? void 0 : _a.record_id) !== null && _b !== void 0 ? _b : '');
|
|
66
|
+
// NOTE: フォームは record レベル かつ objectKey があるときだけ描くため、
|
|
67
|
+
// ここに来る時点で objectKey はある。型を絞るためのガードとして残す
|
|
68
|
+
const handleSubmit = useCallback((e) => {
|
|
69
|
+
e.preventDefault();
|
|
70
|
+
if (!hasObjectKey || objectKey == null)
|
|
71
|
+
return;
|
|
72
|
+
preview.setObjectKey(objectKey);
|
|
73
|
+
preview.setRecordId(localRecordId);
|
|
74
|
+
preview.triggerFetch();
|
|
75
|
+
}, [hasObjectKey, objectKey, localRecordId, preview]);
|
|
76
|
+
// NOTE: カタログ取得前は key をそのまま表示名に使う。objectKey が無いページは
|
|
77
|
+
// undefined のままにして、表示側で出し分ける
|
|
78
|
+
const objectLabel = hasObjectKey
|
|
79
|
+
? (_d = (_c = catalog.find(o => o.key === objectKey)) === null || _c === void 0 ? void 0 : _c.label) !== null && _d !== void 0 ? _d : objectKey
|
|
80
|
+
: undefined;
|
|
81
|
+
/** ページ情報の行。並びは設計(Figma)に合わせる */
|
|
82
|
+
const pageInfoRows = [
|
|
83
|
+
[
|
|
84
|
+
'オブジェクト:',
|
|
85
|
+
objectLabel == null ? '未指定' : `${objectLabel} (${objectKey})`,
|
|
86
|
+
],
|
|
87
|
+
['組織ID:', organizationId],
|
|
88
|
+
['バージョン:', `v${SFA_KIT_VERSION}`],
|
|
89
|
+
];
|
|
90
|
+
const recordIdPlaceholder = objectLabel == null
|
|
91
|
+
? 'レコードIDを入力'
|
|
92
|
+
: `${objectLabel}のレコードIDを入力`;
|
|
93
|
+
return (_jsxs("div", { style: toolbarStyles.wrapper, children: [_jsx("style", { children: DEV_TOOLBAR_CSS }), _jsxs("div", { style: toolbarStyles.main, children: [_jsxs("div", { style: toolbarStyles.left, children: [_jsx("span", { style: toolbarStyles.previewBadge, children: "\u30AB\u30B9\u30BF\u30E0\u30DA\u30FC\u30B8\u30D7\u30EC\u30D3\u30E5\u30FC" }), _jsx("span", { style: toolbarStyles.inlineNotice, children: "\u203B\u3053\u306E\u30C4\u30FC\u30EB\u30D0\u30FC\u306F GENIEE SFA/CRM \u306E\u753B\u9762\u306B\u306F\u8868\u793A\u3055\u308C\u307E\u305B\u3093" })] }), isRecordLevel && hasObjectKey && (_jsxs(_Fragment, { children: [_jsxs("form", { onSubmit: handleSubmit, style: toolbarStyles.center, children: [_jsx("input", { className: RECORD_ID_INPUT_CLASS, style: toolbarStyles.input, type: "text", "aria-label": "\u30EC\u30B3\u30FC\u30C9 ID", value: localRecordId, onChange: e => setLocalRecordId(e.target.value), placeholder: recordIdPlaceholder }), _jsx("button", { type: "submit", className: HOVER_DIM_CLASS, style: toolbarStyles.fetchBtn, children: "\u30C7\u30FC\u30BF\u53D6\u5F97" })] }), _jsx("span", { style: toolbarStyles.separator })] })), _jsxs("div", { style: toolbarStyles.right, children: [_jsxs("div", { className: PAGE_INFO_CLASS, style: toolbarStyles.pageInfo, children: [_jsxs("button", { type: "button", className: HOVER_DIM_CLASS, "aria-describedby": pageInfoTooltipId, style: toolbarStyles.pageInfoTrigger, children: [_jsx("span", { "aria-hidden": "true", style: toolbarStyles.infoIcon, children: _jsx("span", { style: toolbarStyles.infoIconGlyph, children: "i" }) }), "\u30DA\u30FC\u30B8\u60C5\u5831"] }), _jsx("div", { id: pageInfoTooltipId, className: PAGE_INFO_TOOLTIP_CLASS, role: "tooltip", style: toolbarStyles.pageInfoTooltip, children: pageInfoRows.map(([label, value]) => (_jsxs(Fragment, { children: [_jsx("span", { style: toolbarStyles.tooltipLabel, children: label }), _jsx("span", { style: toolbarStyles.tooltipValue, children: value })] }, label))) })] }), _jsxs("button", { type: "button", className: HOVER_DIM_CLASS, style: toolbarStyles.logoutBtn, onClick: onLogout, children: [_jsx("svg", { width: "16", height: "16", viewBox: "0 -960 960 960", fill: "currentColor", "aria-hidden": "true", style: toolbarStyles.logoutIcon, children: _jsx("path", { d: "M806-440H320v-80h486l-62-62 56-58 160 160-160 160-56-58 62-62ZM600-600v-160H200v560h400v-160h80v160q0 33-23.5 56.5T600-120H200q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h400q33 0 56.5 23.5T680-760v160h-80Z" }) }), "\u30ED\u30B0\u30A2\u30A6\u30C8"] })] })] })] }));
|
|
94
|
+
}
|
|
95
|
+
const toolbarStyles = {
|
|
96
|
+
wrapper: {
|
|
97
|
+
position: 'sticky',
|
|
98
|
+
top: 0,
|
|
99
|
+
zIndex: 9999,
|
|
100
|
+
fontFamily: font.family,
|
|
101
|
+
boxShadow: '0 4px 16px rgba(0,0,0,0.25), 0 2px 4px rgba(0,0,0,0.15)',
|
|
102
|
+
},
|
|
103
|
+
main: {
|
|
104
|
+
display: 'flex',
|
|
105
|
+
alignItems: 'center',
|
|
106
|
+
justifyContent: 'space-between',
|
|
107
|
+
// NOTE: 高さは 56px 固定。中身は 30px の入力・ボタン 1 行なので、
|
|
108
|
+
// 縦の padding は持たせず alignItems: center で中央に置く
|
|
109
|
+
height: 56,
|
|
110
|
+
boxSizing: 'border-box',
|
|
111
|
+
padding: '0 16px',
|
|
112
|
+
gap: '12px',
|
|
113
|
+
flexWrap: 'nowrap',
|
|
114
|
+
background: TOOLBAR_BLACK,
|
|
115
|
+
},
|
|
116
|
+
left: {
|
|
117
|
+
display: 'flex',
|
|
118
|
+
alignItems: 'center',
|
|
119
|
+
gap: '12px',
|
|
120
|
+
// NOTE: 幅が足りないときはここが先に縮む。flexShrink: 0 にすると
|
|
121
|
+
// 行末の Logout が画面外に押し出されて押せなくなる
|
|
122
|
+
minWidth: 0,
|
|
123
|
+
},
|
|
124
|
+
inlineNotice: {
|
|
125
|
+
fontSize: 12,
|
|
126
|
+
color: 'rgba(255,255,255,0.7)',
|
|
127
|
+
whiteSpace: 'nowrap',
|
|
128
|
+
// NOTE: 幅が足りないときは折り返さず省略する(行の高さは 56px 固定のため)
|
|
129
|
+
minWidth: 0,
|
|
130
|
+
overflow: 'hidden',
|
|
131
|
+
textOverflow: 'ellipsis',
|
|
132
|
+
},
|
|
133
|
+
previewBadge: {
|
|
134
|
+
fontWeight: 700,
|
|
135
|
+
fontSize: 16,
|
|
136
|
+
color: '#ffffff',
|
|
137
|
+
whiteSpace: 'nowrap',
|
|
138
|
+
// NOTE: 見出しは削らない。縮むのは隣の注意書きだけ
|
|
139
|
+
flexShrink: 0,
|
|
140
|
+
},
|
|
141
|
+
center: {
|
|
142
|
+
display: 'flex',
|
|
143
|
+
alignItems: 'center',
|
|
144
|
+
gap: '8px',
|
|
145
|
+
// NOTE: flex: 1 で伸ばしたうえで中身を右端に寄せる。center だと
|
|
146
|
+
// ボタンの右側に余白が残り、区切り線との間が開いて見える
|
|
147
|
+
flex: 1,
|
|
148
|
+
justifyContent: 'flex-end',
|
|
149
|
+
minWidth: 0,
|
|
150
|
+
},
|
|
151
|
+
input: {
|
|
152
|
+
padding: '5px 10px',
|
|
153
|
+
borderRadius: 3,
|
|
154
|
+
fontSize: 14,
|
|
155
|
+
fontFamily: font.family,
|
|
156
|
+
border: '1px solid #D6D9DD',
|
|
157
|
+
backgroundColor: '#ffffff',
|
|
158
|
+
color: '#313542',
|
|
159
|
+
// NOTE: 幅は 206px 固定。center は flex コンテナなので、
|
|
160
|
+
// flexShrink: 0 を付けないと幅が縮んでしまう
|
|
161
|
+
width: 206,
|
|
162
|
+
flexShrink: 0,
|
|
163
|
+
height: 30,
|
|
164
|
+
outline: 'none',
|
|
165
|
+
},
|
|
166
|
+
fetchBtn: Object.assign(Object.assign({}, baseButtonStyle), {
|
|
167
|
+
// NOTE: 74x30 固定。横 padding を残すと文字が入らないため 0 にする。
|
|
168
|
+
// 中央寄せは UA 既定に頼らず flex で明示する(baseButtonStyle の
|
|
169
|
+
// lineHeight: 20px と height: 30 がずれても中央に来る)
|
|
170
|
+
display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: 74, height: 30, boxSizing: 'border-box', padding: 0, flexShrink: 0, backgroundColor: TOOLBAR_BLACK, color: '#ffffff',
|
|
171
|
+
// NOTE: 隣の入力欄と角の丸みを揃える
|
|
172
|
+
border: '1px solid #ffffff', borderRadius: 3, fontWeight: 700, fontSize: 12, whiteSpace: 'nowrap' }),
|
|
173
|
+
separator: {
|
|
174
|
+
width: 1,
|
|
175
|
+
height: 23,
|
|
176
|
+
backgroundColor: SEPARATOR_GREY,
|
|
177
|
+
flexShrink: 0,
|
|
178
|
+
// NOTE: main の gap は 12px で全体に効くため、区切り線の左右だけ広げたい分を
|
|
179
|
+
// ここで足す(どちら側も 12 + 12 = 24px)
|
|
180
|
+
marginLeft: 12,
|
|
181
|
+
marginRight: 12,
|
|
182
|
+
},
|
|
183
|
+
pageInfo: {
|
|
184
|
+
position: 'relative',
|
|
185
|
+
display: 'flex',
|
|
186
|
+
alignItems: 'center',
|
|
187
|
+
},
|
|
188
|
+
pageInfoTrigger: Object.assign(Object.assign({}, baseButtonStyle), { display: 'flex', alignItems: 'center', gap: '6px', padding: 0, border: 'none', background: 'transparent', color: '#ffffff', fontSize: 13, fontWeight: 600, whiteSpace: 'nowrap' }),
|
|
189
|
+
infoIcon: {
|
|
190
|
+
display: 'inline-flex',
|
|
191
|
+
alignItems: 'center',
|
|
192
|
+
justifyContent: 'center',
|
|
193
|
+
width: 14,
|
|
194
|
+
height: 14,
|
|
195
|
+
borderRadius: '50%',
|
|
196
|
+
backgroundColor: '#ffffff',
|
|
197
|
+
color: TOOLBAR_BLACK,
|
|
198
|
+
fontSize: 10,
|
|
199
|
+
fontWeight: 700,
|
|
200
|
+
lineHeight: '14px',
|
|
201
|
+
flexShrink: 0,
|
|
202
|
+
},
|
|
203
|
+
infoIconGlyph: {
|
|
204
|
+
// NOTE: テーマの font.family はサンセリフのみで、その italic の "i" は
|
|
205
|
+
// 直線の棒にしかならない。足元が丸まった(セリフのある)字形が欲しいので、
|
|
206
|
+
// このグリフだけセリフ体を指定する
|
|
207
|
+
fontFamily: "Georgia, 'Times New Roman', 'Noto Serif JP', serif",
|
|
208
|
+
// NOTE: italic だけだと傾きが足りないため skewX を重ねる。円が歪まないよう、
|
|
209
|
+
// 変形はグリフを包む内側の span にだけ掛ける(transform は inline に効かない
|
|
210
|
+
// ので inline-block にする)
|
|
211
|
+
display: 'inline-block',
|
|
212
|
+
fontStyle: 'italic',
|
|
213
|
+
transform: 'skewX(-10deg)',
|
|
214
|
+
lineHeight: 1,
|
|
215
|
+
},
|
|
216
|
+
pageInfoTooltip: {
|
|
217
|
+
position: 'absolute',
|
|
218
|
+
// NOTE: 起点は pageInfo(アイコン + 文字)の左端。left: 0 でアイコンの真下に
|
|
219
|
+
// 左端を合わせ、右へ広げる。right: 0 だと「ページ情報」の文字の右端に揃って
|
|
220
|
+
// しまい、アイコンより右にずれる
|
|
221
|
+
top: 'calc(100% + 8px)',
|
|
222
|
+
left: 0,
|
|
223
|
+
display: 'grid',
|
|
224
|
+
gridTemplateColumns: 'auto minmax(0, 1fr)',
|
|
225
|
+
columnGap: '8px',
|
|
226
|
+
rowGap: '6px',
|
|
227
|
+
// NOTE: 幅は 200px 固定。中身の量で広がらないようにし、長い値は折り返す。
|
|
228
|
+
// padding を内側に含めるため boxSizing を border-box にする
|
|
229
|
+
width: 200,
|
|
230
|
+
boxSizing: 'border-box',
|
|
231
|
+
padding: '12px 16px',
|
|
232
|
+
borderRadius: 6,
|
|
233
|
+
backgroundColor: '#ffffff',
|
|
234
|
+
color: TOOLBAR_BLACK,
|
|
235
|
+
fontSize: 12,
|
|
236
|
+
lineHeight: '18px',
|
|
237
|
+
boxShadow: '0 4px 16px rgba(0,0,0,0.25)',
|
|
238
|
+
// NOTE: 開閉は CSS 側(DEV_TOOLBAR_CSS)で切り替える
|
|
239
|
+
transition: '0.2s',
|
|
240
|
+
textAlign: 'left',
|
|
241
|
+
},
|
|
242
|
+
tooltipLabel: {
|
|
243
|
+
textAlign: 'right',
|
|
244
|
+
whiteSpace: 'nowrap',
|
|
245
|
+
// NOTE: 値が複数行になっても、ラベルは 1 行目の位置に留める
|
|
246
|
+
alignSelf: 'start',
|
|
247
|
+
},
|
|
248
|
+
tooltipValue: {
|
|
249
|
+
// NOTE: anywhere は語の途中でも切るため、2 行目が語の断片になって
|
|
250
|
+
// 揃っていないように見える。単語境界を優先させる
|
|
251
|
+
overflowWrap: 'break-word',
|
|
252
|
+
// NOTE: 全角の閉じ括弧は行頭に置けない(禁則処理)ため、ぶら下がって
|
|
253
|
+
// padding からはみ出して見えることがある。禁則を厳格に適用させる
|
|
254
|
+
lineBreak: 'strict',
|
|
255
|
+
alignSelf: 'start',
|
|
256
|
+
},
|
|
257
|
+
right: {
|
|
258
|
+
display: 'flex',
|
|
259
|
+
alignItems: 'center',
|
|
260
|
+
// NOTE: 子は ページ情報 と ログアウト の 2 つだけなので、この gap は
|
|
261
|
+
// その間隔そのものになる
|
|
262
|
+
gap: '24px',
|
|
263
|
+
flexShrink: 0,
|
|
264
|
+
},
|
|
265
|
+
logoutBtn: Object.assign(Object.assign({}, baseButtonStyle), {
|
|
266
|
+
// NOTE: 枠線は持たず、隣の ページ情報 と同じ見た目に揃える
|
|
267
|
+
display: 'flex', alignItems: 'center', gap: '4px', padding: 0, border: 'none', backgroundColor: 'transparent', color: '#ffffff', fontSize: 13, fontWeight: 600, whiteSpace: 'nowrap' }),
|
|
268
|
+
logoutIcon: {
|
|
269
|
+
flexShrink: 0,
|
|
270
|
+
},
|
|
271
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { StudioPageLevel } from './DevToolbar';
|
|
3
|
+
interface SfaLoginGateProps {
|
|
4
|
+
readonly apiBase?: string;
|
|
5
|
+
/**
|
|
6
|
+
* The origin of the SFA/CRM host page (e.g. "https://app.chikyu.net").
|
|
7
|
+
* When provided and the page is running inside an iframe, the component
|
|
8
|
+
* uses the postMessage code-exchange flow instead of a login form.
|
|
9
|
+
* Must come from Keystone server-side config, never from a URL query parameter.
|
|
10
|
+
*/
|
|
11
|
+
readonly trustedOrigin?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Default organization ID to auto-select when the user's account spans
|
|
14
|
+
* multiple organizations. Set this to the org that generated the page's
|
|
15
|
+
* metadata so users don't have to pick it manually.
|
|
16
|
+
*/
|
|
17
|
+
readonly defaultOrganizationId?: number;
|
|
18
|
+
/**
|
|
19
|
+
* The object this page is built for (e.g. `"companies"`).
|
|
20
|
+
*
|
|
21
|
+
* Used by the standalone DevToolbar to show the object in the page-info
|
|
22
|
+
* tooltip and in the record ID placeholder, and to pre-populate the
|
|
23
|
+
* standalone preview context. Pass it at both levels.
|
|
24
|
+
*/
|
|
25
|
+
readonly objectKey?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Which Chikyu tab slot this page is for — a record tab or an object
|
|
28
|
+
* (list/dashboard) tab. Only the record level gets a record ID input in the
|
|
29
|
+
* standalone DevToolbar.
|
|
30
|
+
*
|
|
31
|
+
* Defaults to `'record'` when `objectKey` is set and `'object'` otherwise,
|
|
32
|
+
* which preserves the behavior of pages written before this prop existed.
|
|
33
|
+
* Object-level pages that declare `objectKey` must set `level="object"`
|
|
34
|
+
* explicitly, or they will get a record ID input they cannot use.
|
|
35
|
+
*/
|
|
36
|
+
readonly level?: StudioPageLevel;
|
|
37
|
+
readonly children: ReactNode;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* @description Universal auth wrapper for sfa-kit pages.
|
|
41
|
+
*
|
|
42
|
+
* Auto-detects its context:
|
|
43
|
+
* - **Embedded (iframe with `_t`)**: when the page is inside an iframe and
|
|
44
|
+
* was served with a content token (`_t` query parameter), uses the
|
|
45
|
+
* postMessage code-exchange flow. No login form. The trusted origin for
|
|
46
|
+
* postMessage validation is derived from `document.referrer` (or the
|
|
47
|
+
* explicit `trustedOrigin` prop if provided).
|
|
48
|
+
* - **Standalone**: shows a GENIEE SFA/CRM-branded login form. Org is resolved
|
|
49
|
+
* automatically from the API (auto-selected for multi-org accounts when
|
|
50
|
+
* defaultOrganizationId is set, or the first org is used by default).
|
|
51
|
+
*/
|
|
52
|
+
export declare function SfaLoginGate({ apiBase, trustedOrigin, defaultOrganizationId, objectKey, level, children, }: SfaLoginGateProps): JSX.Element;
|
|
53
|
+
export {};
|