@opensumi/ide-main-layout 2.21.13 → 2.22.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.
Files changed (38) hide show
  1. package/lib/browser/accordion/accordion.service.js +1 -1
  2. package/lib/browser/accordion/accordion.service.js.map +1 -1
  3. package/lib/browser/accordion/styles.module.less +4 -2
  4. package/lib/browser/index.js.map +1 -1
  5. package/lib/browser/layout.service.d.ts.map +1 -1
  6. package/lib/browser/layout.service.js +6 -6
  7. package/lib/browser/layout.service.js.map +1 -1
  8. package/lib/browser/main-layout.contribution.js +1 -1
  9. package/lib/browser/main-layout.contribution.js.map +1 -1
  10. package/lib/browser/quick-open-view.js +3 -2
  11. package/lib/browser/quick-open-view.js.map +1 -1
  12. package/lib/browser/tabbar/tabbar.service.js.map +1 -1
  13. package/lib/browser/tabbar-handler.js.map +1 -1
  14. package/lib/browser/views-registry.js +7 -7
  15. package/lib/browser/views-registry.js.map +1 -1
  16. package/package.json +10 -9
  17. package/src/browser/accordion/accordion.service.ts +635 -0
  18. package/src/browser/accordion/accordion.view.tsx +102 -0
  19. package/src/browser/accordion/section.view.tsx +181 -0
  20. package/src/browser/accordion/styles.module.less +214 -0
  21. package/src/browser/accordion/titlebar.view.tsx +17 -0
  22. package/src/browser/default-config.ts +35 -0
  23. package/src/browser/index.ts +40 -0
  24. package/src/browser/input/index.tsx +32 -0
  25. package/src/browser/layout.service.ts +515 -0
  26. package/src/browser/main-layout.contribution.ts +447 -0
  27. package/src/browser/quick-open-view.ts +138 -0
  28. package/src/browser/tabbar/bar.view.tsx +289 -0
  29. package/src/browser/tabbar/panel.view.tsx +190 -0
  30. package/src/browser/tabbar/renderer.view.tsx +133 -0
  31. package/src/browser/tabbar/styles.module.less +408 -0
  32. package/src/browser/tabbar/tabbar.service.ts +845 -0
  33. package/src/browser/tabbar-handler.ts +200 -0
  34. package/src/browser/views-registry.ts +157 -0
  35. package/src/browser/welcome.view.tsx +152 -0
  36. package/src/common/index.ts +2 -0
  37. package/src/common/main-layout.definition.ts +122 -0
  38. package/src/index.ts +1 -0
@@ -0,0 +1,102 @@
1
+ import { observer } from 'mobx-react-lite';
2
+ import React from 'react';
3
+
4
+ import { View, useInjectable } from '@opensumi/ide-core-browser';
5
+ import { Layout, SplitPanel } from '@opensumi/ide-core-browser/lib/components';
6
+ import { replaceLocalizePlaceholder } from '@opensumi/ide-core-common';
7
+
8
+ import { AccordionServiceFactory, AccordionService, SectionState } from './accordion.service';
9
+ import { AccordionSection } from './section.view';
10
+
11
+ interface AccordionContainerProps {
12
+ alignment?: Layout.alignment;
13
+ views: View[];
14
+ initState?: Map<string, SectionState>;
15
+ containerId: string;
16
+ headerSize?: number;
17
+ minSize?: number;
18
+ noRestore?: boolean;
19
+ className?: string;
20
+ style?: React.CSSProperties;
21
+ }
22
+
23
+ export const AccordionContainer = observer(
24
+ ({
25
+ alignment = 'vertical',
26
+ views,
27
+ containerId,
28
+ headerSize = 24,
29
+ minSize = 120,
30
+ className,
31
+ noRestore,
32
+ style,
33
+ }: AccordionContainerProps) => {
34
+ const accordionService: AccordionService = useInjectable(AccordionServiceFactory)(containerId, noRestore);
35
+ React.useEffect(() => {
36
+ // 解决视图在渲染前注册的问题
37
+ if (!views.length) {
38
+ return;
39
+ }
40
+ for (const view of views) {
41
+ accordionService.appendView(view);
42
+ }
43
+ }, [views]);
44
+ React.useEffect(() => {
45
+ accordionService.initConfig({ headerSize, minSize });
46
+ }, []);
47
+ const allCollapsed = !accordionService.visibleViews.find((view) => {
48
+ const viewState: SectionState = accordionService.getViewState(view.id);
49
+ return !viewState.collapsed;
50
+ });
51
+
52
+ return (
53
+ <SplitPanel
54
+ className={className}
55
+ style={style}
56
+ dynamicTarget={true}
57
+ id={containerId}
58
+ resizeKeep={false}
59
+ useDomSize={allCollapsed}
60
+ direction={alignment === 'horizontal' ? 'left-to-right' : 'top-to-bottom'}
61
+ >
62
+ {accordionService.visibleViews.map((view, index) => {
63
+ const viewState: SectionState = accordionService.getViewState(view.id);
64
+ const titleMenu = view.titleMenu || accordionService.getSectionToolbarMenu(view.id);
65
+ const { collapsed, nextSize } = viewState;
66
+ return (
67
+ <AccordionSection
68
+ noHeader={accordionService.visibleViews.length === 1}
69
+ onItemClick={() => accordionService.handleSectionClick(view.id, !collapsed, index)}
70
+ onContextMenuHandler={accordionService.handleContextMenu}
71
+ alignment={alignment as Layout.alignment}
72
+ header={(view.name && replaceLocalizePlaceholder(view.name)) || view.id}
73
+ viewId={view.id}
74
+ key={view.id}
75
+ expanded={!collapsed}
76
+ accordionService={accordionService}
77
+ index={index}
78
+ headerSize={headerSize}
79
+ minSize={headerSize}
80
+ initialProps={view.initialProps}
81
+ titleMenu={titleMenu}
82
+ titleMenuContext={view.titleMenuContext}
83
+ savedSize={collapsed ? headerSize : nextSize}
84
+ flex={view.weight || 1}
85
+ >
86
+ {view.component}
87
+ </AccordionSection>
88
+ );
89
+ })}
90
+ </SplitPanel>
91
+ );
92
+ },
93
+ );
94
+
95
+ AccordionContainer.displayName = 'AccordionContainer';
96
+
97
+ export interface PanelProps extends React.PropsWithChildren<any> {
98
+ flex: number;
99
+ }
100
+
101
+ export const Panel: React.FC<PanelProps> = ({ children }) => <div>{children}</div>;
102
+ Panel.displayName = 'Panel';
@@ -0,0 +1,181 @@
1
+ import cls from 'classnames';
2
+ import React from 'react';
3
+
4
+ import { getIcon, ErrorBoundary, useViewState } from '@opensumi/ide-core-browser';
5
+ import { useInjectable } from '@opensumi/ide-core-browser';
6
+ import { Layout, PanelContext } from '@opensumi/ide-core-browser/lib/components';
7
+ import { InlineActionBar, InlineMenuBar } from '@opensumi/ide-core-browser/lib/components/actions';
8
+ import { isIMenu, IMenu, IContextMenu } from '@opensumi/ide-core-browser/lib/menu/next';
9
+ import { IProgressService } from '@opensumi/ide-core-browser/lib/progress';
10
+ import { ProgressBar } from '@opensumi/ide-core-browser/lib/progress/progress-bar';
11
+ import { transformLabelWithCodicon } from '@opensumi/ide-core-browser/lib/utils/label';
12
+ import { IIconService } from '@opensumi/ide-theme';
13
+
14
+ import { AccordionService } from './accordion.service';
15
+ import styles from './styles.module.less';
16
+
17
+ export interface CollapsePanelProps extends React.PropsWithChildren<any> {
18
+ // panel 头部标题
19
+ header: string;
20
+ // panel 头部描述
21
+ description?: string;
22
+ // panel 信息
23
+ message?: string;
24
+ // 头部样式名
25
+ headerClass?: string;
26
+ // panel 点击事件监听
27
+ onItemClick?: any;
28
+ onContextMenuHandler: any;
29
+ // 计算宽度时的优先级
30
+ weight?: number;
31
+ // 排序优先级
32
+ priority?: number;
33
+ // panel宽高
34
+ size?: {
35
+ width: number;
36
+ height: number;
37
+ };
38
+ headerSize?: number;
39
+ viewId: string;
40
+ alignment?: Layout.alignment;
41
+ index: number;
42
+ initialProps?: any;
43
+ noHeader?: boolean;
44
+ titleMenu: IMenu | IContextMenu;
45
+ accordionService: AccordionService;
46
+ }
47
+
48
+ const attrs = {
49
+ tabIndex: 0,
50
+ };
51
+
52
+ export const AccordionSection = ({
53
+ header,
54
+ description,
55
+ message,
56
+ headerClass,
57
+ onItemClick,
58
+ noHeader,
59
+ children,
60
+ expanded,
61
+ onResize,
62
+ size,
63
+ headerSize,
64
+ viewId,
65
+ initialProps,
66
+ titleMenu,
67
+ titleMenuContext,
68
+ accordionService,
69
+ onContextMenuHandler,
70
+ }: CollapsePanelProps) => {
71
+ const iconService = useInjectable<IIconService>(IIconService);
72
+ const contentRef = React.useRef<HTMLDivElement | null>(null);
73
+
74
+ const [headerFocused, setHeaderFocused] = React.useState(false);
75
+ const [headerLabel, setHeaderLabel] = React.useState(header);
76
+ const [headerDescription, setheaderDescription] = React.useState(description);
77
+ const [panelMessage, setPanelMessage] = React.useState(message);
78
+
79
+ const { getSize, setSize } = React.useContext(PanelContext);
80
+
81
+ React.useEffect(() => {
82
+ const disposable = accordionService.onDidChangeViewTiele(({ id, title, description, message: msg }) => {
83
+ if (viewId === id && title && title !== headerLabel) {
84
+ setHeaderLabel(title);
85
+ }
86
+
87
+ if (viewId === id && description && description !== headerDescription) {
88
+ setheaderDescription(description);
89
+ }
90
+
91
+ if (viewId === id && msg && msg !== panelMessage) {
92
+ setPanelMessage(msg);
93
+ }
94
+ });
95
+
96
+ return () => {
97
+ disposable.dispose();
98
+ };
99
+ }, []);
100
+
101
+ const clickHandler = React.useCallback(() => {
102
+ const currentSize = getSize(false);
103
+ onItemClick((targetSize) => setSize(targetSize, false), currentSize);
104
+ }, [getSize, setSize]);
105
+
106
+ const bodyStyle = React.useMemo<React.CSSProperties>(
107
+ () => ({
108
+ overflow: expanded ? 'auto' : 'hidden',
109
+ }),
110
+ [expanded],
111
+ );
112
+
113
+ React.useEffect(() => {
114
+ if (onResize) {
115
+ onResize(size);
116
+ }
117
+ }, [size]);
118
+
119
+ const headerFocusHandler = React.useCallback(() => {
120
+ setHeaderFocused(true);
121
+ }, []);
122
+
123
+ const headerBlurHandler = React.useCallback(() => {
124
+ setHeaderFocused(false);
125
+ }, []);
126
+
127
+ const viewState = useViewState(viewId, contentRef, true);
128
+ const progressService: IProgressService = useInjectable(IProgressService);
129
+ const indicator = progressService.getIndicator(viewId);
130
+ const Component: any = children;
131
+ return (
132
+ <div className={styles.kt_split_panel} data-view-id={viewId}>
133
+ {!noHeader && (
134
+ <div
135
+ onFocus={headerFocusHandler}
136
+ onBlur={headerBlurHandler}
137
+ {...attrs}
138
+ className={cls(styles.kt_split_panel_header, headerFocused ? styles.kt_panel_focused : '', headerClass)}
139
+ onClick={clickHandler}
140
+ onContextMenu={(e) => onContextMenuHandler(e, viewId)}
141
+ style={{ height: headerSize + 'px', lineHeight: headerSize + 'px' }}
142
+ >
143
+ <div className={styles.label_wrap}>
144
+ <i className={cls(getIcon('arrow-down'), styles.arrow_icon, expanded ? '' : styles.kt_mod_collapsed)}></i>
145
+ <div className={styles.section_label} style={{ lineHeight: headerSize + 'px' }}>
146
+ {headerLabel}
147
+ </div>
148
+ {headerDescription && (
149
+ <div className={styles.section_description} style={{ lineHeight: headerSize + 'px' }}>
150
+ {transformLabelWithCodicon(headerDescription, {}, iconService.fromString.bind(iconService))}
151
+ </div>
152
+ )}
153
+ </div>
154
+ {expanded && titleMenu && (
155
+ <div className={styles.actions_wrap}>
156
+ {isIMenu(titleMenu) ? (
157
+ <InlineActionBar menus={titleMenu} context={titleMenuContext} />
158
+ ) : (
159
+ <InlineMenuBar menus={titleMenu} context={titleMenuContext} />
160
+ )}
161
+ </div>
162
+ )}
163
+ </div>
164
+ )}
165
+ <div
166
+ className={cls([styles.kt_split_panel_body, { [styles.hide]: !expanded }])}
167
+ style={bodyStyle}
168
+ ref={contentRef}
169
+ >
170
+ <ProgressBar className={styles.progressBar} progressModel={indicator!.progressModel} />
171
+ <ErrorBoundary>
172
+ {panelMessage && <span className={styles.kt_split_panel_message}>{panelMessage}</span>}
173
+ <Component
174
+ {...initialProps}
175
+ viewState={{ height: viewState.height - (panelMessage ? 22 : 0), width: viewState.width }}
176
+ />
177
+ </ErrorBoundary>
178
+ </div>
179
+ </div>
180
+ );
181
+ };
@@ -0,0 +1,214 @@
1
+ :global {
2
+ .resize-ease {
3
+ transition: height 0.1s ease-out;
4
+ }
5
+ }
6
+
7
+ .kt_split_panel_container {
8
+ display: flex;
9
+ width: 100%;
10
+ height: 100%;
11
+ white-space: nowrap;
12
+ flex-direction: column;
13
+ }
14
+
15
+ .kt_split_panel {
16
+ overflow: hidden;
17
+ width: 100%;
18
+ height: 100%;
19
+ display: flex;
20
+ flex-direction: column;
21
+ position: relative;
22
+ &:hover .actions_wrap {
23
+ display: block;
24
+ }
25
+ }
26
+
27
+ .kt_split_panel_header {
28
+ background-color: var(--sideBarSectionHeader-background);
29
+ font-size: 12px;
30
+ text-transform: uppercase;
31
+ display: flex;
32
+ cursor: pointer;
33
+ color: var(--sideBarSectionHeader-foreground);
34
+ justify-content: space-between;
35
+ border-top: 1px solid var(--sideBar-border);
36
+ border-bottom: 1px solid transparent;
37
+ &:focus {
38
+ outline-color: rgba(14, 99, 156, 0.8);
39
+ outline-width: 1px;
40
+ outline-style: solid;
41
+ outline-offset: -1px;
42
+ }
43
+ }
44
+
45
+ .label_wrap {
46
+ display: flex;
47
+ flex-direction: row;
48
+ flex-shrink: 1;
49
+ overflow: hidden;
50
+ }
51
+
52
+ .kt_split_panel_body {
53
+ overflow: hidden;
54
+ flex: 1;
55
+ font-size: 12px;
56
+ position: relative;
57
+ user-select: none;
58
+ .progressBar {
59
+ top: -1px;
60
+ }
61
+
62
+ .kt_split_panel_message {
63
+ display: flex;
64
+ padding: 4px 12px 4px 18px;
65
+ height: 14px;
66
+ line-height: 14px;
67
+ user-select: text;
68
+ box-sizing: content-box;
69
+ }
70
+ }
71
+
72
+ .kt_split_overlay {
73
+ width: 100%;
74
+ white-space: normal;
75
+ flex: none;
76
+ position: relative;
77
+ transition: height 0.2s ease;
78
+ }
79
+
80
+ .kt_panel_toolbar {
81
+ position: absolute;
82
+ right: 0;
83
+ top: 0;
84
+ height: 22px;
85
+ display: none;
86
+ .kt_panel_toolbar_container {
87
+ margin: 0 auto;
88
+ padding: 0;
89
+ width: 100%;
90
+ height: 22px;
91
+ }
92
+ .kt_panel_toolbar_item {
93
+ cursor: pointer;
94
+ display: inline-block;
95
+ position: relative;
96
+ width: 28px;
97
+ height: 22px;
98
+ background-size: 16px;
99
+ background-position: 50%;
100
+ background-repeat: no-repeat;
101
+ margin-right: 0;
102
+ &:active {
103
+ transform: scale(1.1);
104
+ }
105
+ }
106
+ }
107
+
108
+ .kt_panel_focused {
109
+ outline-color: var(--focusBorder);
110
+ }
111
+
112
+ .section_label {
113
+ font-weight: 400;
114
+ color: var(--sideBarSectionHeader-foreground);
115
+ // font-size: 80%;
116
+ text-transform: uppercase;
117
+ cursor: pointer;
118
+ user-select: none;
119
+ }
120
+
121
+ .section_description {
122
+ font-weight: 400;
123
+ margin-left: 10px;
124
+ opacity: 0.6;
125
+ overflow: hidden;
126
+ text-overflow: ellipsis;
127
+ text-transform: none;
128
+ white-space: nowrap;
129
+ flex-shrink: 100000;
130
+ display: flex;
131
+ align-items: center;
132
+ font-size: 12px;
133
+
134
+ :global(.kt-icon),
135
+ :global(.codicon) {
136
+ font-size: 14px !important;
137
+ }
138
+ }
139
+
140
+ i.arrow_icon {
141
+ font-size: 16px;
142
+ margin: 0 4px;
143
+ color: var(--descriptionForeground);
144
+ &.kt_mod_collapsed {
145
+ transform: rotate(-90deg);
146
+ }
147
+ }
148
+
149
+ .actions_wrap {
150
+ margin-right: 8px;
151
+ display: none;
152
+ }
153
+
154
+ .titlebar {
155
+ background-color: var(--sideBar-background);
156
+ display: flex;
157
+ flex-direction: row;
158
+ user-select: none;
159
+ justify-content: space-between;
160
+ align-items: center;
161
+ padding: 0 8px;
162
+ box-sizing: border-box;
163
+ line-height: 100%;
164
+ h1 {
165
+ color: var(--sideBarTitle-foreground);
166
+ font-weight: 400;
167
+ margin: 0;
168
+ text-transform: uppercase;
169
+ font-size: 12px;
170
+ overflow: hidden;
171
+ text-overflow: ellipsis;
172
+ white-space: nowrap;
173
+ }
174
+ }
175
+
176
+ .hide {
177
+ display: none;
178
+ }
179
+ .welcome {
180
+ height: 100%;
181
+ display: flex;
182
+ flex-direction: column;
183
+ padding: 0 10px 1em;
184
+ box-sizing: border-box;
185
+ align-items: center;
186
+ & > * {
187
+ margin-block-start: 1em;
188
+ margin-block-end: 0;
189
+ margin-inline-start: 0;
190
+ margin-inline-end: 0;
191
+ }
192
+ p {
193
+ width: 100%;
194
+ text-align: left;
195
+ }
196
+ :global(.button-container) {
197
+ width: 100%;
198
+ max-width: 300px;
199
+ transition: max-width 0.2s ease-out;
200
+ :global(.kt-button) {
201
+ box-sizing: border-box;
202
+ width: 100%;
203
+ padding: 4px;
204
+ text-align: center;
205
+ cursor: pointer;
206
+ justify-content: center;
207
+ align-items: center;
208
+ max-width: 300px;
209
+ overflow: hidden;
210
+ text-overflow: ellipsis;
211
+ display: inline-block;
212
+ }
213
+ }
214
+ }
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+
3
+ import { LAYOUT_VIEW_SIZE } from '@opensumi/ide-core-browser/lib/layout/constants';
4
+
5
+ import styles from './styles.module.less';
6
+
7
+ export const TitleBar: React.FC<{
8
+ title: string;
9
+ menubar?: React.ReactNode;
10
+ }> = React.memo((props) => (
11
+ <div className={styles.titlebar} style={{ height: LAYOUT_VIEW_SIZE.PANEL_TITLEBAR_HEIGHT }}>
12
+ <h1>{props.title}</h1>
13
+ {props.menubar || null}
14
+ </div>
15
+ ));
16
+
17
+ TitleBar.displayName = 'TitleBar';
@@ -0,0 +1,35 @@
1
+ /* istanbul ignore file */
2
+ import { LayoutConfig, SlotLocation } from '@opensumi/ide-core-browser';
3
+
4
+ export const defaultConfig: LayoutConfig = {
5
+ [SlotLocation.top]: {
6
+ modules: ['@opensumi/ide-menu-bar'],
7
+ },
8
+ [SlotLocation.action]: {
9
+ modules: ['@opensumi/ide-toolbar-action'],
10
+ },
11
+ [SlotLocation.left]: {
12
+ modules: [
13
+ '@opensumi/ide-explorer',
14
+ '@opensumi/ide-search',
15
+ '@opensumi/ide-scm',
16
+ '@opensumi/ide-extension-manager',
17
+ '@opensumi/ide-debug',
18
+ ],
19
+ },
20
+ [SlotLocation.right]: {
21
+ modules: [],
22
+ },
23
+ [SlotLocation.main]: {
24
+ modules: ['@opensumi/ide-editor'],
25
+ },
26
+ [SlotLocation.bottom]: {
27
+ modules: ['@opensumi/ide-terminal-next', '@opensumi/ide-output', 'debug-console', '@opensumi/ide-markers'],
28
+ },
29
+ [SlotLocation.statusBar]: {
30
+ modules: ['@opensumi/ide-status-bar'],
31
+ },
32
+ [SlotLocation.extra]: {
33
+ modules: ['breadcrumb-menu'],
34
+ },
35
+ };
@@ -0,0 +1,40 @@
1
+ import { Provider, Injectable, Injector } from '@opensumi/di';
2
+ import { BrowserModule } from '@opensumi/ide-core-browser';
3
+
4
+ import { IMainLayoutService, IViewsRegistry, MainLayoutContribution } from '../common';
5
+
6
+ import { AccordionServiceFactory } from './accordion/accordion.service';
7
+ import { LayoutService } from './layout.service';
8
+ import { MainLayoutModuleContribution } from './main-layout.contribution';
9
+ import { TabbarServiceFactory } from './tabbar/tabbar.service';
10
+ import { ViewsRegistry } from './views-registry';
11
+
12
+ @Injectable()
13
+ export class MainLayoutModule extends BrowserModule {
14
+ providers: Provider[] = [
15
+ MainLayoutModuleContribution,
16
+ {
17
+ token: IMainLayoutService,
18
+ useClass: LayoutService,
19
+ },
20
+ {
21
+ token: IViewsRegistry,
22
+ useClass: ViewsRegistry,
23
+ },
24
+ {
25
+ token: TabbarServiceFactory,
26
+ useFactory: (injector: Injector) => (location: string) => {
27
+ const manager: IMainLayoutService = injector.get(IMainLayoutService);
28
+ return manager.getTabbarService(location);
29
+ },
30
+ },
31
+ {
32
+ token: AccordionServiceFactory,
33
+ useFactory: (injector: Injector) => (containerId: string, noRestore?: boolean) => {
34
+ const manager: IMainLayoutService = injector.get(IMainLayoutService);
35
+ return manager.getAccordionService(containerId, noRestore);
36
+ },
37
+ },
38
+ ];
39
+ contributionProvider = MainLayoutContribution;
40
+ }
@@ -0,0 +1,32 @@
1
+ import React, { PropsWithChildren, useRef, useEffect } from 'react';
2
+
3
+ import { IInputBaseProps, Input } from '@opensumi/ide-components';
4
+ import { useInjectable } from '@opensumi/ide-core-browser/lib/react-hooks';
5
+
6
+ import { IMainLayoutService } from '../../common';
7
+
8
+ export const AutoFocusedInput = ({
9
+ containerId,
10
+ ...inputProps
11
+ }: PropsWithChildren<{ containerId: string } & IInputBaseProps>) => {
12
+ const layoutService = useInjectable<IMainLayoutService>(IMainLayoutService);
13
+ const inputRef = useRef<HTMLInputElement | null>(null);
14
+
15
+ const doFocus = React.useCallback(() => {
16
+ if (inputRef && inputRef.current) {
17
+ queueMicrotask(() => inputRef.current?.focus());
18
+ }
19
+ }, [inputRef.current]);
20
+
21
+ useEffect(() => {
22
+ doFocus();
23
+
24
+ const handler = layoutService.getTabbarHandler(containerId);
25
+ const disposable = handler?.onActivate(doFocus);
26
+
27
+ return () => {
28
+ disposable?.dispose();
29
+ };
30
+ }, [layoutService]);
31
+ return <Input ref={inputRef} {...inputProps} />;
32
+ };