@mindlogic-ai/logician-ui 3.2.0-alpha.5 → 4.0.0-alpha.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Tree/Tree.d.ts +5 -1
- package/dist/components/Tree/Tree.d.ts.map +1 -1
- package/dist/components/Tree/Tree.types.d.ts +29 -1
- package/dist/components/Tree/Tree.types.d.ts.map +1 -1
- package/dist/components/Tree/TreeBranchIndentGuide.d.ts +5 -1
- package/dist/components/Tree/TreeBranchIndentGuide.d.ts.map +1 -1
- package/dist/components/Tree/TreeBranchIndentGuide.js +107 -8
- package/dist/components/Tree/TreeBranchIndentGuide.js.map +1 -1
- package/dist/components/Tree/TreeBranchIndentGuide.mjs +107 -8
- package/dist/components/Tree/TreeBranchIndentGuide.mjs.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +0 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -2
- package/src/components/Tree/Tree.GuideElbow.stories.tsx +220 -0
- package/src/components/Tree/Tree.types.ts +29 -1
- package/src/components/Tree/TreeBranchIndentGuide.test.tsx +213 -0
- package/src/components/Tree/TreeBranchIndentGuide.tsx +137 -11
- package/src/index.ts +0 -1
- package/dist/components/MDXEditor/MDXEditor.d.ts +0 -6
- package/dist/components/MDXEditor/MDXEditor.d.ts.map +0 -1
- package/dist/components/MDXEditor/MDXEditor.js +0 -237
- package/dist/components/MDXEditor/MDXEditor.js.map +0 -1
- package/dist/components/MDXEditor/MDXEditor.mjs +0 -235
- package/dist/components/MDXEditor/MDXEditor.mjs.map +0 -1
- package/dist/components/MDXEditor/MDXEditor.types.d.ts +0 -7
- package/dist/components/MDXEditor/MDXEditor.types.d.ts.map +0 -1
- package/dist/components/MDXEditor/index.d.ts +0 -3
- package/dist/components/MDXEditor/index.d.ts.map +0 -1
- package/src/components/MDXEditor/MDXEditor.css +0 -40
- package/src/components/MDXEditor/MDXEditor.stories.tsx +0 -56
- package/src/components/MDXEditor/MDXEditor.tsx +0 -337
- package/src/components/MDXEditor/MDXEditor.types.ts +0 -7
- package/src/components/MDXEditor/index.ts +0 -2
|
@@ -23,7 +23,35 @@ export type TreeBranchTriggerProps = TreeViewBranchTriggerProps;
|
|
|
23
23
|
export type TreeBranchIndicatorProps = TreeViewBranchIndicatorProps;
|
|
24
24
|
export type TreeBranchTextProps = TreeViewBranchTextProps;
|
|
25
25
|
export type TreeBranchContentProps = TreeViewBranchContentProps;
|
|
26
|
-
export type TreeBranchIndentGuideProps = TreeViewBranchIndentGuideProps
|
|
26
|
+
export type TreeBranchIndentGuideProps = TreeViewBranchIndentGuideProps & {
|
|
27
|
+
/**
|
|
28
|
+
* Draw `├` / `└` guide lines instead of a plain vertical rail: every
|
|
29
|
+
* row gets a horizontal foot joining it to its parent rail, middle
|
|
30
|
+
* rows keep the vertical (`├`), and the last row of each group stops
|
|
31
|
+
* at its centre (`└`). Off by default to preserve the existing
|
|
32
|
+
* plain-vertical-rail look. See `TreeBranchIndentGuide`.
|
|
33
|
+
* @default false
|
|
34
|
+
*/
|
|
35
|
+
elbow?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Length of the elbow foot — the horizontal cross-stroke that joins
|
|
38
|
+
* the rail to the row content, drawn from the parent rail column
|
|
39
|
+
* toward the text. In elbow mode each level indents 4px further than
|
|
40
|
+
* the plain-rail default, and since the default foot length doesn't
|
|
41
|
+
* grow with it, the foot ends 8px short of the row text. Any Chakra
|
|
42
|
+
* width value. Only applies when `elbow` is set.
|
|
43
|
+
* @default 'calc(var(--tree-indentation) - 4px)'
|
|
44
|
+
*/
|
|
45
|
+
footLength?: string | number;
|
|
46
|
+
/**
|
|
47
|
+
* Color of the guide lines (rail, elbow foot and vertical segments).
|
|
48
|
+
* Any Chakra color token. The default is a mode-flipping semantic
|
|
49
|
+
* token, so it darkens in light mode and lightens in dark mode
|
|
50
|
+
* automatically.
|
|
51
|
+
* @default 'border.default'
|
|
52
|
+
*/
|
|
53
|
+
guideColor?: string;
|
|
54
|
+
};
|
|
27
55
|
export type TreeItemProps = TreeViewItemProps;
|
|
28
56
|
export type TreeItemTextProps = TreeViewItemTextProps;
|
|
29
57
|
export type TreeItemIndicatorProps = TreeViewItemIndicatorProps;
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { ChakraProvider } from '@chakra-ui/react';
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
|
+
|
|
4
|
+
import { system } from '../../theme';
|
|
5
|
+
import { createTreeCollection } from './index';
|
|
6
|
+
import { Tree } from './Tree';
|
|
7
|
+
|
|
8
|
+
interface N {
|
|
9
|
+
id: string;
|
|
10
|
+
name: string;
|
|
11
|
+
children?: N[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const sample: N = {
|
|
15
|
+
id: 'ROOT',
|
|
16
|
+
name: '',
|
|
17
|
+
children: [{ id: 'a', name: 'A', children: [{ id: 'a1', name: 'A1' }] }],
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const collection = createTreeCollection<N>({
|
|
21
|
+
rootNode: sample,
|
|
22
|
+
nodeToValue: (n) => n.id,
|
|
23
|
+
nodeToString: (n) => n.name,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const renderNode = ({
|
|
27
|
+
node,
|
|
28
|
+
nodeState,
|
|
29
|
+
}: {
|
|
30
|
+
node: N;
|
|
31
|
+
nodeState: { isBranch: boolean };
|
|
32
|
+
}) =>
|
|
33
|
+
nodeState.isBranch ? (
|
|
34
|
+
<Tree.BranchControl>
|
|
35
|
+
<Tree.BranchText>{node.name}</Tree.BranchText>
|
|
36
|
+
</Tree.BranchControl>
|
|
37
|
+
) : (
|
|
38
|
+
<Tree.Item>
|
|
39
|
+
<Tree.ItemText>{node.name}</Tree.ItemText>
|
|
40
|
+
</Tree.Item>
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
function renderGuide(elbow: boolean, extraProps: { guideColor?: string } = {}) {
|
|
44
|
+
const { container } = render(
|
|
45
|
+
<ChakraProvider value={system}>
|
|
46
|
+
<Tree.Root
|
|
47
|
+
collection={collection}
|
|
48
|
+
aria-label="t"
|
|
49
|
+
defaultExpandedValue={['a']}
|
|
50
|
+
>
|
|
51
|
+
<Tree.Tree>
|
|
52
|
+
<Tree.Node
|
|
53
|
+
indentGuide={
|
|
54
|
+
elbow ? (
|
|
55
|
+
<Tree.BranchIndentGuide elbow {...extraProps} />
|
|
56
|
+
) : (
|
|
57
|
+
<Tree.BranchIndentGuide {...extraProps} />
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
render={renderNode}
|
|
61
|
+
/>
|
|
62
|
+
</Tree.Tree>
|
|
63
|
+
</Tree.Root>
|
|
64
|
+
</ChakraProvider>
|
|
65
|
+
);
|
|
66
|
+
const guide = container.querySelector<HTMLElement>(
|
|
67
|
+
'[data-part="branch-indent-guide"]'
|
|
68
|
+
);
|
|
69
|
+
if (!guide) throw new Error('no indent guide rendered');
|
|
70
|
+
return guide.className;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// All Chakra/emotion-injected CSS, concatenated.
|
|
74
|
+
const sheetText = () =>
|
|
75
|
+
Array.from(document.querySelectorAll('style'))
|
|
76
|
+
.map((s) => s.textContent ?? '')
|
|
77
|
+
.join('\n');
|
|
78
|
+
|
|
79
|
+
// The foot rule is anchored on the emotion-generated `css-*` class
|
|
80
|
+
// (not the static `chakra-*` slot class), so target that one.
|
|
81
|
+
const styleAnchor = (className: string) => {
|
|
82
|
+
const emotion = className
|
|
83
|
+
.trim()
|
|
84
|
+
.split(/\s+/)
|
|
85
|
+
.find((c) => /^css-/.test(c));
|
|
86
|
+
if (!emotion) throw new Error(`no emotion class in: ${className}`);
|
|
87
|
+
return '\\.' + emotion;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Regression guard for the `elbow` foot. Chakra renders ONE indent guide
|
|
92
|
+
* per `branchContent` (its first child), followed by that branch's rows
|
|
93
|
+
* as siblings — so the foot must be drawn on those sibling rows, scoped
|
|
94
|
+
* to the elbow guide's own class. An earlier `:last-of-type::after`
|
|
95
|
+
* attempt silently matched nothing (the guide is never the last sibling
|
|
96
|
+
* of its type), which is exactly what these tests pin down.
|
|
97
|
+
*/
|
|
98
|
+
describe('Tree.BranchIndentGuide elbow', () => {
|
|
99
|
+
it('draws a ::before foot on the guide’s sibling rows when elbow is set', () => {
|
|
100
|
+
const cls = renderGuide(true);
|
|
101
|
+
const anchor = styleAnchor(cls);
|
|
102
|
+
const css = sheetText();
|
|
103
|
+
|
|
104
|
+
// Foot rule is scoped to THIS guide's class via the sibling combinator,
|
|
105
|
+
// for both leaf rows and branch rows — never applied globally.
|
|
106
|
+
expect(css).toMatch(
|
|
107
|
+
new RegExp(`${anchor}\\s*~\\s*\\[data-part="item"\\]::before`)
|
|
108
|
+
);
|
|
109
|
+
expect(css).toMatch(
|
|
110
|
+
new RegExp(
|
|
111
|
+
`${anchor}\\s*~\\s*\\[data-part="branch"\\]\\s*>\\s*\\[data-part="branch-control"\\]::before`
|
|
112
|
+
)
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
// The foot paints a 1px cross-stroke pinned to the row's vertical
|
|
116
|
+
// centre, starting at the parent rail column.
|
|
117
|
+
const footRule =
|
|
118
|
+
css.match(
|
|
119
|
+
new RegExp(
|
|
120
|
+
`${anchor}\\s*~\\s*\\[data-part="item"\\]::before\\s*\\{[^}]*\\}`
|
|
121
|
+
)
|
|
122
|
+
)?.[0] ?? '';
|
|
123
|
+
expect(footRule).toContain('content:""');
|
|
124
|
+
expect(footRule).toContain('position:absolute');
|
|
125
|
+
expect(footRule).toContain('inset-block-start:50%');
|
|
126
|
+
expect(footRule).toContain('height:1px');
|
|
127
|
+
expect(footRule).toMatch(
|
|
128
|
+
/inset-inline-start:calc\(var\(--tree-offset\) - var\(--tree-elbow-indentation\)\)/
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
// Elbow mode widens per-level indentation: the sibling rows redefine
|
|
132
|
+
// the recipe's indentation-offset from the widened value, pushing the
|
|
133
|
+
// row text right, away from the elbow foot.
|
|
134
|
+
const rowVarsRule =
|
|
135
|
+
css.match(
|
|
136
|
+
new RegExp(`${anchor}\\s*~\\s*\\[data-part="item"\\]\\s*\\{[^}]*\\}`)
|
|
137
|
+
)?.[0] ?? '';
|
|
138
|
+
expect(rowVarsRule).toContain(
|
|
139
|
+
'--tree-elbow-indentation:calc(var(--tree-indentation) + 4px)'
|
|
140
|
+
);
|
|
141
|
+
expect(rowVarsRule).toContain(
|
|
142
|
+
'--tree-indentation-offset:calc(var(--tree-elbow-indentation) * var(--tree-depth))'
|
|
143
|
+
);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('draws no foot for a plain guide (no elbow)', () => {
|
|
147
|
+
const cls = renderGuide(false);
|
|
148
|
+
const anchor = styleAnchor(cls);
|
|
149
|
+
// This specific guide's class must not anchor any ::before foot rule.
|
|
150
|
+
expect(sheetText()).not.toMatch(new RegExp(`${anchor}\\s*~[^{]*::before`));
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('stops the vertical segment at the centre of the last row (└)', () => {
|
|
154
|
+
const cls = renderGuide(true);
|
|
155
|
+
const anchor = styleAnchor(cls);
|
|
156
|
+
const css = sheetText();
|
|
157
|
+
|
|
158
|
+
// Middle rows: full-height vertical (├) on the row itself.
|
|
159
|
+
const middleRule =
|
|
160
|
+
css.match(
|
|
161
|
+
new RegExp(
|
|
162
|
+
`${anchor}\\s*~\\s*\\[data-part="item"\\]:not\\(:last-child\\)::after\\s*\\{[^}]*\\}`
|
|
163
|
+
)
|
|
164
|
+
)?.[0] ?? '';
|
|
165
|
+
expect(middleRule).toContain('inset-block-start:0');
|
|
166
|
+
expect(middleRule).toContain('inset-block-end:0');
|
|
167
|
+
expect(middleRule).toContain('width:1px');
|
|
168
|
+
|
|
169
|
+
// Last leaf row: segment ends at 50% so the foot forms a └.
|
|
170
|
+
const lastItemRule =
|
|
171
|
+
css.match(
|
|
172
|
+
new RegExp(
|
|
173
|
+
`${anchor}\\s*~\\s*\\[data-part="item"\\]:last-child::after\\s*\\{[^}]*\\}`
|
|
174
|
+
)
|
|
175
|
+
)?.[0] ?? '';
|
|
176
|
+
expect(lastItemRule).toContain('inset-block-start:0');
|
|
177
|
+
expect(lastItemRule).toContain('height:50%');
|
|
178
|
+
|
|
179
|
+
// Last branch row: half-height segment on its control only, so an
|
|
180
|
+
// expanded last branch has no rail running past its elbow.
|
|
181
|
+
const lastBranchRule =
|
|
182
|
+
css.match(
|
|
183
|
+
new RegExp(
|
|
184
|
+
`${anchor}\\s*~\\s*\\[data-part="branch"\\]:last-child\\s*>\\s*\\[data-part="branch-control"\\]::after\\s*\\{[^}]*\\}`
|
|
185
|
+
)
|
|
186
|
+
)?.[0] ?? '';
|
|
187
|
+
expect(lastBranchRule).toContain('height:50%');
|
|
188
|
+
|
|
189
|
+
// Non-last branch: pass-through rail spans the whole expanded subtree,
|
|
190
|
+
// painted above row hover backgrounds like the original slot rail.
|
|
191
|
+
const passThroughRule =
|
|
192
|
+
css.match(
|
|
193
|
+
new RegExp(
|
|
194
|
+
`${anchor}\\s*~\\s*\\[data-part="branch"\\]:not\\(:last-child\\)::before\\s*\\{[^}]*\\}`
|
|
195
|
+
)
|
|
196
|
+
)?.[0] ?? '';
|
|
197
|
+
expect(passThroughRule).toContain('inset-block-end:0');
|
|
198
|
+
expect(passThroughRule).toContain('z-index:1');
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('paints the guide with the custom guideColor token', () => {
|
|
202
|
+
const cls = renderGuide(true, { guideColor: 'red.500' });
|
|
203
|
+
const anchor = styleAnchor(cls);
|
|
204
|
+
const css = sheetText();
|
|
205
|
+
const footRule =
|
|
206
|
+
css.match(
|
|
207
|
+
new RegExp(
|
|
208
|
+
`${anchor}\\s*~\\s*\\[data-part="item"\\]::before\\s*\\{[^}]*\\}`
|
|
209
|
+
)
|
|
210
|
+
)?.[0] ?? '';
|
|
211
|
+
expect(footRule).toMatch(/--chakra-colors-red-500|red\.500/);
|
|
212
|
+
});
|
|
213
|
+
});
|
|
@@ -3,18 +3,144 @@ import { TreeView as ChakraTreeView } from '@chakra-ui/react';
|
|
|
3
3
|
|
|
4
4
|
import { TreeBranchIndentGuideProps } from './Tree.types';
|
|
5
5
|
|
|
6
|
+
// Extra per-level indentation in elbow mode. Guide-line trees read better
|
|
7
|
+
// with a little more horizontal air between the elbow and the row text,
|
|
8
|
+
// so each level indents this much further than the recipe default. The
|
|
9
|
+
// foot length does NOT grow with it (its default is derived from the
|
|
10
|
+
// original `--tree-indentation`), so the foot-to-text gap widens by the
|
|
11
|
+
// same amount.
|
|
12
|
+
const ELBOW_EXTRA_INDENT = '4px';
|
|
13
|
+
|
|
14
|
+
// The widened per-level indentation, defined on the guide's sibling rows
|
|
15
|
+
// so both the recipe's offset math and our pseudo-element math can share
|
|
16
|
+
// it. Root rows (depth 1) have no guide sibling, but their offset
|
|
17
|
+
// multiplies indentation by zero, so every level stays consistent.
|
|
18
|
+
const ELBOW_INDENT_VARS = {
|
|
19
|
+
'--tree-elbow-indentation': `calc(var(--tree-indentation) + ${ELBOW_EXTRA_INDENT})`,
|
|
20
|
+
} as const;
|
|
21
|
+
|
|
22
|
+
// Re-derive the recipe's `--tree-indentation-offset` (which feeds
|
|
23
|
+
// `--tree-offset`, the row's padding-start) from the widened indentation.
|
|
24
|
+
// Must be set on the same elements the recipe defines it on
|
|
25
|
+
// (item / branch-control) — inheritance can't override those.
|
|
26
|
+
const ELBOW_OFFSET_VARS = {
|
|
27
|
+
'--tree-indentation-offset':
|
|
28
|
+
'calc(var(--tree-elbow-indentation) * var(--tree-depth))',
|
|
29
|
+
} as const;
|
|
30
|
+
|
|
31
|
+
// The parent rail column, measured from a child row (item / branch-control).
|
|
32
|
+
// A row's content starts at its own `--tree-offset`, and the parent rail
|
|
33
|
+
// sits exactly one (widened) indentation to its left (verified against the
|
|
34
|
+
// recipe's offset math for both the guide and the row slots).
|
|
35
|
+
const ROW_RAIL_COLUMN =
|
|
36
|
+
'calc(var(--tree-offset) - var(--tree-elbow-indentation))';
|
|
37
|
+
|
|
38
|
+
// The same column, computed from the `branch` element itself. A branch
|
|
39
|
+
// only carries Ark's inline `--depth` (the recipe's `--tree-offset` is
|
|
40
|
+
// defined on item/branch-control, not here), so expand the recipe math:
|
|
41
|
+
// rail(x) = padding + indentation * (depth - 2) + icon * 0.5 * (depth - 1)
|
|
42
|
+
// — algebraically identical to ROW_RAIL_COLUMN for a row at `--depth`.
|
|
43
|
+
const BRANCH_RAIL_COLUMN =
|
|
44
|
+
'calc(var(--tree-padding-inline) + var(--tree-elbow-indentation) * (var(--depth) - 2) + var(--tree-icon-size) * 0.5 * (var(--depth) - 1))';
|
|
45
|
+
|
|
6
46
|
export const TreeBranchIndentGuide = forwardRef<
|
|
7
47
|
HTMLDivElement,
|
|
8
48
|
TreeBranchIndentGuideProps
|
|
9
|
-
>(
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
)
|
|
19
|
-
|
|
49
|
+
>(
|
|
50
|
+
(
|
|
51
|
+
{
|
|
52
|
+
elbow = false,
|
|
53
|
+
footLength = 'calc(var(--tree-indentation) - 4px)',
|
|
54
|
+
guideColor = 'border.default',
|
|
55
|
+
...props
|
|
56
|
+
},
|
|
57
|
+
ref
|
|
58
|
+
) => {
|
|
59
|
+
// Chakra v3's `branchIndentGuide` slot renders ONE absolutely-positioned
|
|
60
|
+
// vertical line per `branchContent` (its first child), `height: 100%`,
|
|
61
|
+
// with `insetInlineStart` auto-calculated from tree depth. Don't add
|
|
62
|
+
// `ms`/`ps` (breaks the depth math) or `borderInlineStartWidth` (stacks
|
|
63
|
+
// a second 1px stroke on the slot's own bg-painted 1px) — only `bg`.
|
|
64
|
+
//
|
|
65
|
+
// That single full-height rail cannot form a `└` on the last row: it
|
|
66
|
+
// always paints past the last row's centre down to the content bottom.
|
|
67
|
+
// So in `elbow` mode the slot's own rail is hidden (`bg: transparent`,
|
|
68
|
+
// the element stays as the CSS sibling anchor) and the guide lines are
|
|
69
|
+
// re-drawn per row on the guide's sibling rows (`[data-part="item"]`
|
|
70
|
+
// leaves and `[data-part="branch"]` subtrees):
|
|
71
|
+
//
|
|
72
|
+
// - every row gets a `::before` foot — the horizontal cross-stroke
|
|
73
|
+
// from the rail column toward the row content, pinned to the row's
|
|
74
|
+
// vertical centre;
|
|
75
|
+
// - non-last rows get a full-height vertical segment (`├`); for a
|
|
76
|
+
// branch it is drawn on the `[data-part="branch"]` element so it
|
|
77
|
+
// passes through the whole expanded subtree, like the original rail
|
|
78
|
+
// (zIndex 1 for the same paint-above-row-hover reason);
|
|
79
|
+
// - the LAST row's segment stops at the row's vertical centre where
|
|
80
|
+
// the foot meets it (`└`); for a branch that means half the
|
|
81
|
+
// branch-control only, so an expanded last branch hangs below the
|
|
82
|
+
// elbow with no rail running past it.
|
|
83
|
+
const foot = {
|
|
84
|
+
content: '""',
|
|
85
|
+
position: 'absolute',
|
|
86
|
+
insetBlockStart: '50%',
|
|
87
|
+
insetInlineStart: ROW_RAIL_COLUMN,
|
|
88
|
+
width: footLength,
|
|
89
|
+
height: '1px',
|
|
90
|
+
bg: guideColor,
|
|
91
|
+
} as const;
|
|
92
|
+
const rail = {
|
|
93
|
+
content: '""',
|
|
94
|
+
position: 'absolute',
|
|
95
|
+
insetBlockStart: '0',
|
|
96
|
+
insetInlineStart: ROW_RAIL_COLUMN,
|
|
97
|
+
width: '1px',
|
|
98
|
+
bg: guideColor,
|
|
99
|
+
} as const;
|
|
100
|
+
const elbowCss = elbow
|
|
101
|
+
? {
|
|
102
|
+
'& ~ [data-part="item"]': {
|
|
103
|
+
...ELBOW_INDENT_VARS,
|
|
104
|
+
...ELBOW_OFFSET_VARS,
|
|
105
|
+
},
|
|
106
|
+
// branch-control reads --tree-elbow-indentation by inheritance,
|
|
107
|
+
// but the offset override must sit on the control itself.
|
|
108
|
+
'& ~ [data-part="branch"]': ELBOW_INDENT_VARS,
|
|
109
|
+
'& ~ [data-part="branch"] > [data-part="branch-control"]':
|
|
110
|
+
ELBOW_OFFSET_VARS,
|
|
111
|
+
'& ~ [data-part="item"]::before': foot,
|
|
112
|
+
'& ~ [data-part="branch"] > [data-part="branch-control"]::before':
|
|
113
|
+
foot,
|
|
114
|
+
'& ~ [data-part="item"]:not(:last-child)::after': {
|
|
115
|
+
...rail,
|
|
116
|
+
insetBlockEnd: '0',
|
|
117
|
+
},
|
|
118
|
+
'& ~ [data-part="item"]:last-child::after': {
|
|
119
|
+
...rail,
|
|
120
|
+
height: '50%',
|
|
121
|
+
},
|
|
122
|
+
'& ~ [data-part="branch"]:not(:last-child)::before': {
|
|
123
|
+
...rail,
|
|
124
|
+
insetBlockEnd: '0',
|
|
125
|
+
insetInlineStart: BRANCH_RAIL_COLUMN,
|
|
126
|
+
zIndex: 1,
|
|
127
|
+
},
|
|
128
|
+
'& ~ [data-part="branch"]:last-child > [data-part="branch-control"]::after':
|
|
129
|
+
{
|
|
130
|
+
...rail,
|
|
131
|
+
height: '50%',
|
|
132
|
+
},
|
|
133
|
+
}
|
|
134
|
+
: undefined;
|
|
135
|
+
|
|
136
|
+
return (
|
|
137
|
+
<ChakraTreeView.BranchIndentGuide
|
|
138
|
+
ref={ref}
|
|
139
|
+
bg={elbow ? 'transparent' : guideColor}
|
|
140
|
+
css={elbowCss}
|
|
141
|
+
{...props}
|
|
142
|
+
/>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
);
|
|
20
146
|
TreeBranchIndentGuide.displayName = 'TreeBranchIndentGuide';
|
package/src/index.ts
CHANGED
|
@@ -53,7 +53,6 @@ export * from './components/LogicianProvider';
|
|
|
53
53
|
export * from './components/Markdown';
|
|
54
54
|
export * from './components/Masonry';
|
|
55
55
|
export * from './components/MaxLengthIndicator';
|
|
56
|
-
export * from './components/MDXEditor';
|
|
57
56
|
export * from './components/Menu';
|
|
58
57
|
export * from './components/Modal';
|
|
59
58
|
export * from './components/MonthPicker';
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { MDXEditorMethods, MDXEditorProps as BaseEditorProps } from '@mdxeditor/editor';
|
|
2
|
-
import '@mdxeditor/editor/style.css';
|
|
3
|
-
import './MDXEditor.css';
|
|
4
|
-
import type { MDXEditorProps } from './MDXEditor.types';
|
|
5
|
-
export declare const MDXEditor: import("react").ForwardRefExoticComponent<MDXEditorProps & import("react").RefAttributes<BaseEditorProps & MDXEditorMethods>>;
|
|
6
|
-
//# sourceMappingURL=MDXEditor.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"MDXEditor.d.ts","sourceRoot":"","sources":["../../../src/components/MDXEditor/MDXEditor.tsx"],"names":[],"mappings":"AAIA,OAAO,EAeL,gBAAgB,EAChB,cAAc,IAAI,eAAe,EAOlC,MAAM,mBAAmB,CAAC;AAE3B,OAAO,6BAA6B,CAAC;AACrC,OAAO,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,eAAO,MAAM,SAAS,+HA4SpB,CAAC"}
|
|
@@ -1,237 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
var jsxRuntime = require('react/jsx-runtime');
|
|
5
|
-
var React = require('react');
|
|
6
|
-
var react = require('@chakra-ui/react');
|
|
7
|
-
var editor = require('@mdxeditor/editor');
|
|
8
|
-
require('@mdxeditor/editor/style.css');
|
|
9
|
-
|
|
10
|
-
const MDXEditor = React.forwardRef(({ containerProps, autoFocus = true, onError, ...rest }, ref) => {
|
|
11
|
-
const [error, setError] = React.useState(null);
|
|
12
|
-
// Resolve theme tokens to actual values
|
|
13
|
-
const [space1, space2, space4] = react.useToken('spacing', ['1', '2', '4']);
|
|
14
|
-
const [radiusSm] = react.useToken('radii', ['sm']);
|
|
15
|
-
// 내부적으로 Editor 인스턴스를 참조하기 위한 로컬 ref
|
|
16
|
-
const editorRef = React.useRef(null);
|
|
17
|
-
// 부모 컴포넌트로 전달된 ref가 editorRef를 바라보도록 연결
|
|
18
|
-
React.useImperativeHandle(ref, () => editorRef.current, []);
|
|
19
|
-
const handleContainerClick = (e) => {
|
|
20
|
-
// Only focus if clicking on the container itself, not on toolbar and table elements
|
|
21
|
-
const target = e.target;
|
|
22
|
-
const isToolbarClick = target.closest('.mdxeditor-toolbar');
|
|
23
|
-
const isTableClick = target.closest('table');
|
|
24
|
-
const isPopupClick = target.closest('.mdxeditor-popup-container');
|
|
25
|
-
if (!isToolbarClick && !isTableClick && !isPopupClick) {
|
|
26
|
-
editorRef.current?.focus();
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
return (jsxRuntime.jsx(react.Box, { width: "100%", height: "100%", borderWidth: "1px", borderRadius: "lg", overflow: "hidden", onClick: handleContainerClick, cursor: "text", ...containerProps, css: {
|
|
30
|
-
/**
|
|
31
|
-
* Chakra v3 css prop styling notes:
|
|
32
|
-
*
|
|
33
|
-
* 1. All nested selectors require '&' prefix (e.g., '& .class', '& h1')
|
|
34
|
-
* 2. Use full CSS property names (background, padding) instead of Chakra
|
|
35
|
-
* shorthand props (bg, p) in nested selectors
|
|
36
|
-
* 3. Color values must be resolved via useToken(), not token strings
|
|
37
|
-
* 4. Font sizes use responsive objects: { base: '2.4em', md: '3em' }
|
|
38
|
-
* 5. List styles must be explicitly defined (listStyleType, etc.)
|
|
39
|
-
*
|
|
40
|
-
* These requirements differ from v2's sx prop, which was more implicit.
|
|
41
|
-
* v3 prioritizes performance and explicitness over automatic handling.
|
|
42
|
-
*/
|
|
43
|
-
...containerProps?.css,
|
|
44
|
-
'& .mdxeditor': {
|
|
45
|
-
width: '100%',
|
|
46
|
-
minHeight: '300px',
|
|
47
|
-
height: '100%',
|
|
48
|
-
background: 'var(--chakra-colors-bg-surface)',
|
|
49
|
-
display: 'flex',
|
|
50
|
-
flexDirection: 'column',
|
|
51
|
-
},
|
|
52
|
-
'& .mdxeditor-toolbar': {
|
|
53
|
-
display: 'flex',
|
|
54
|
-
gap: space2,
|
|
55
|
-
padding: space2,
|
|
56
|
-
borderBottomWidth: '1px',
|
|
57
|
-
background: 'var(--chakra-colors-bg-subtle)',
|
|
58
|
-
flexShrink: 0,
|
|
59
|
-
cursor: 'default',
|
|
60
|
-
},
|
|
61
|
-
// Toolbar icons. mdxeditor colors its toolbar svgs with its own
|
|
62
|
-
// `--baseTextContrast` (its internal slate scale), which doesn't follow
|
|
63
|
-
// our color mode — in dark mode they stay near-black (#1c2024) and all
|
|
64
|
-
// but vanish against the dark toolbar. Flip them onto the semantic
|
|
65
|
-
// foreground token so they track light/dark like the editor body text.
|
|
66
|
-
'& .mdxeditor-toolbar svg': {
|
|
67
|
-
color: 'var(--chakra-colors-fg-default)',
|
|
68
|
-
},
|
|
69
|
-
// Disabled toolbar buttons (e.g. undo/redo with nothing to undo) — keep
|
|
70
|
-
// them dimmed but still mode-aware, instead of mdxeditor's light-only
|
|
71
|
-
// border color.
|
|
72
|
-
'& .mdxeditor-toolbar [data-disabled] svg': {
|
|
73
|
-
color: 'var(--chakra-colors-fg-subtle)',
|
|
74
|
-
},
|
|
75
|
-
// Hover / pressed / toggled-on backgrounds. mdxeditor paints these with
|
|
76
|
-
// `--baseBgActive` (its slate scale), which also doesn't track the color
|
|
77
|
-
// mode — in dark mode it goes light-grey, so the now-light icons sit on a
|
|
78
|
-
// light fill and disappear on hover and when a format is active. Flip the
|
|
79
|
-
// fill onto the semantic hover token so it stays mode-aware.
|
|
80
|
-
'& .mdxeditor-toolbar button:hover, & .mdxeditor-toolbar button[data-state="on"], & .mdxeditor-toolbar button:active': {
|
|
81
|
-
background: 'var(--chakra-colors-bg-muted)',
|
|
82
|
-
},
|
|
83
|
-
// Block-type select trigger in the toolbar — mdxeditor themes it with
|
|
84
|
-
// its own vars (left white), so flip it onto the semantic surface.
|
|
85
|
-
'& [class*="_selectTrigger_"]': {
|
|
86
|
-
background: 'var(--chakra-colors-bg-surface)',
|
|
87
|
-
color: 'var(--chakra-colors-fg-default)',
|
|
88
|
-
},
|
|
89
|
-
// Target the root contenteditable wrapper
|
|
90
|
-
'& [class*="_rootContentEditableWrapper_"]': {
|
|
91
|
-
flex: 1,
|
|
92
|
-
display: 'flex',
|
|
93
|
-
flexDirection: 'column',
|
|
94
|
-
minHeight: 0,
|
|
95
|
-
height: '100%',
|
|
96
|
-
overflow: 'auto',
|
|
97
|
-
},
|
|
98
|
-
// Target the actual contenteditable element
|
|
99
|
-
'& [contenteditable="true"]': {
|
|
100
|
-
flex: 1,
|
|
101
|
-
minHeight: '100%',
|
|
102
|
-
outline: 'none',
|
|
103
|
-
},
|
|
104
|
-
// Target any intermediate wrapper elements
|
|
105
|
-
'& [class*="_contentEditable_"]': {
|
|
106
|
-
flex: 1,
|
|
107
|
-
display: 'flex',
|
|
108
|
-
flexDirection: 'column',
|
|
109
|
-
},
|
|
110
|
-
'& .content-editable': {
|
|
111
|
-
padding: space4,
|
|
112
|
-
color: 'var(--chakra-colors-fg-default)',
|
|
113
|
-
display: 'flex',
|
|
114
|
-
flexDirection: 'column',
|
|
115
|
-
gap: radiusSm,
|
|
116
|
-
flex: 1,
|
|
117
|
-
height: '100%',
|
|
118
|
-
'& h1, & h2, & h3, & h4, & h5': {
|
|
119
|
-
marginBottom: '2px',
|
|
120
|
-
fontWeight: 'bold',
|
|
121
|
-
},
|
|
122
|
-
'& h1:not(:first-child), & h2:not(:first-child), & h3:not(:first-child), & h4:not(:first-child)': {
|
|
123
|
-
marginTop: space1,
|
|
124
|
-
},
|
|
125
|
-
/**
|
|
126
|
-
* Font sizes - Cannot be tokenized due to responsive objects
|
|
127
|
-
*
|
|
128
|
-
* Unlike spacing/radii/colors, fontSize values here are responsive objects
|
|
129
|
-
* { base: '2.4em', md: '3em' } which cannot be stored as regular tokens
|
|
130
|
-
* in Chakra v3. Only textStyles support responsive values, but we cannot
|
|
131
|
-
* extract just the fontSize from textStyles using useToken().
|
|
132
|
-
*
|
|
133
|
-
* These values match theme/index.ts textStyles exactly and must be
|
|
134
|
-
* manually synchronized when updating the theme:
|
|
135
|
-
* - h1: { base: '2.4em', md: '3em' }
|
|
136
|
-
* - h2: { base: '2em', md: '2.5em' }
|
|
137
|
-
* - h3: { base: '1.5em', md: '1.75em' }
|
|
138
|
-
* - h4: { base: '1.25em', md: '1.44em' }
|
|
139
|
-
* - h5: { base: '1.1em', md: '1.2em' }
|
|
140
|
-
*/
|
|
141
|
-
'& h1': {
|
|
142
|
-
fontSize: { base: '2.4em', md: '3em' },
|
|
143
|
-
},
|
|
144
|
-
'& h2': {
|
|
145
|
-
fontSize: { base: '2em', md: '2.5em' },
|
|
146
|
-
},
|
|
147
|
-
'& h3': {
|
|
148
|
-
fontSize: { base: '1.5em', md: '1.75em' },
|
|
149
|
-
},
|
|
150
|
-
'& h4': {
|
|
151
|
-
fontSize: { base: '1.25em', md: '1.44em' },
|
|
152
|
-
},
|
|
153
|
-
'& h5': {
|
|
154
|
-
fontSize: { base: '1.1em', md: '1.2em' },
|
|
155
|
-
},
|
|
156
|
-
/**
|
|
157
|
-
* List styling - IMPORTANT: v3 requires explicit list-style properties
|
|
158
|
-
*
|
|
159
|
-
* In Chakra v2, the `sx` prop automatically preserved browser default list styles.
|
|
160
|
-
* In Chakra v3, the `css` prop is more explicit and performant, but requires
|
|
161
|
-
* manual specification of list styles to override MDXEditor's CSS resets.
|
|
162
|
-
*
|
|
163
|
-
* Required properties:
|
|
164
|
-
* - listStyleType: 'disc' (ul) or 'decimal' (ol) - Shows bullets/numbers
|
|
165
|
-
* - listStylePosition: 'outside' - Places markers outside content box
|
|
166
|
-
* - display: 'list-item' (on li) - Ensures proper list item rendering
|
|
167
|
-
*
|
|
168
|
-
* Why this changed: v3 optimized for speed by removing automatic style inference,
|
|
169
|
-
* requiring more explicit style declarations.
|
|
170
|
-
*/
|
|
171
|
-
'& ul': {
|
|
172
|
-
marginTop: space2,
|
|
173
|
-
paddingInlineStart: space4,
|
|
174
|
-
listStyleType: 'disc',
|
|
175
|
-
listStylePosition: 'outside',
|
|
176
|
-
},
|
|
177
|
-
'& ol': {
|
|
178
|
-
marginTop: space2,
|
|
179
|
-
paddingInlineStart: space4,
|
|
180
|
-
listStyleType: 'decimal',
|
|
181
|
-
listStylePosition: 'outside',
|
|
182
|
-
},
|
|
183
|
-
'& li': {
|
|
184
|
-
lineHeight: '1.5',
|
|
185
|
-
marginBottom: space2,
|
|
186
|
-
display: 'list-item',
|
|
187
|
-
},
|
|
188
|
-
'& blockquote': {
|
|
189
|
-
borderLeftWidth: '4px',
|
|
190
|
-
borderLeftColor: 'var(--chakra-colors-primary-light)',
|
|
191
|
-
background: 'var(--chakra-colors-primary-lighter)',
|
|
192
|
-
paddingLeft: space4,
|
|
193
|
-
paddingBlock: space2,
|
|
194
|
-
marginBlock: space4,
|
|
195
|
-
color: 'var(--chakra-colors-fg-muted)',
|
|
196
|
-
},
|
|
197
|
-
'& a': {
|
|
198
|
-
color: 'var(--chakra-colors-primary-main)',
|
|
199
|
-
textDecoration: 'underline',
|
|
200
|
-
},
|
|
201
|
-
'& code': {
|
|
202
|
-
fontFamily: 'mono',
|
|
203
|
-
background: 'var(--chakra-colors-bg-muted)',
|
|
204
|
-
paddingInline: space1,
|
|
205
|
-
borderRadius: radiusSm,
|
|
206
|
-
'& span': {
|
|
207
|
-
background: 'transparent',
|
|
208
|
-
},
|
|
209
|
-
},
|
|
210
|
-
},
|
|
211
|
-
'& .mdxeditor-diff-source-wrapper': {
|
|
212
|
-
overflow: 'auto',
|
|
213
|
-
},
|
|
214
|
-
}, children: jsxRuntime.jsx(editor.MDXEditor, { ref: editorRef, contentEditableClassName: "content-editable", overlayContainer: typeof document !== 'undefined' ? document.body : undefined, autoFocus: autoFocus, onError: ({ error }) => {
|
|
215
|
-
setError(error);
|
|
216
|
-
onError?.(error);
|
|
217
|
-
}, plugins: [
|
|
218
|
-
editor.headingsPlugin({
|
|
219
|
-
allowedHeadingLevels: [2, 3, 4, 5],
|
|
220
|
-
}),
|
|
221
|
-
editor.linkPlugin(),
|
|
222
|
-
editor.listsPlugin(),
|
|
223
|
-
editor.imagePlugin(),
|
|
224
|
-
editor.quotePlugin(),
|
|
225
|
-
editor.thematicBreakPlugin(),
|
|
226
|
-
editor.markdownShortcutPlugin(),
|
|
227
|
-
editor.tablePlugin(),
|
|
228
|
-
editor.diffSourcePlugin(),
|
|
229
|
-
editor.toolbarPlugin({
|
|
230
|
-
toolbarContents: () => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(editor.UndoRedo, {}), jsxRuntime.jsx(editor.Separator, {}), jsxRuntime.jsx(editor.BoldItalicUnderlineToggles, {}), jsxRuntime.jsx(editor.CodeToggle, {}), jsxRuntime.jsx(editor.Separator, {}), jsxRuntime.jsx(editor.ListsToggle, {}), jsxRuntime.jsx(editor.Separator, {}), jsxRuntime.jsx(editor.BlockTypeSelect, {}), jsxRuntime.jsx(editor.Separator, {}), jsxRuntime.jsx(editor.InsertTable, {}), jsxRuntime.jsx(editor.InsertThematicBreak, {}), error && (jsxRuntime.jsx(editor.DiffSourceToggleWrapper, { options: ['rich-text', 'source'], children: jsxRuntime.jsx(jsxRuntime.Fragment, {}) }))] })),
|
|
231
|
-
}),
|
|
232
|
-
], ...rest }) }));
|
|
233
|
-
});
|
|
234
|
-
MDXEditor.displayName = 'MDXEditor';
|
|
235
|
-
|
|
236
|
-
exports.MDXEditor = MDXEditor;
|
|
237
|
-
//# sourceMappingURL=MDXEditor.js.map
|