|
|
@@ -21,197 +21,199 @@ export default App.directive('layout', [
|
|
|
'$parse',
|
|
|
'$compile',
|
|
|
'ide',
|
|
|
- ($parse, $compile, ide) => ({
|
|
|
- compile() {
|
|
|
- return {
|
|
|
- pre(scope, element, attrs) {
|
|
|
- let customTogglerEl, spacingClosed, spacingOpen, state
|
|
|
- const name = attrs.layout
|
|
|
+ function ($parse, $compile, ide) {
|
|
|
+ return {
|
|
|
+ compile() {
|
|
|
+ return {
|
|
|
+ pre(scope, element, attrs) {
|
|
|
+ let customTogglerEl, spacingClosed, spacingOpen, state
|
|
|
+ const name = attrs.layout
|
|
|
|
|
|
- const { customTogglerPane } = attrs
|
|
|
- const { customTogglerMsgWhenOpen } = attrs
|
|
|
- const { customTogglerMsgWhenClosed } = attrs
|
|
|
- const hasCustomToggler =
|
|
|
- customTogglerPane != null &&
|
|
|
- customTogglerMsgWhenOpen != null &&
|
|
|
- customTogglerMsgWhenClosed != null
|
|
|
+ const { customTogglerPane } = attrs
|
|
|
+ const { customTogglerMsgWhenOpen } = attrs
|
|
|
+ const { customTogglerMsgWhenClosed } = attrs
|
|
|
+ const hasCustomToggler =
|
|
|
+ customTogglerPane != null &&
|
|
|
+ customTogglerMsgWhenOpen != null &&
|
|
|
+ customTogglerMsgWhenClosed != null
|
|
|
|
|
|
- if (attrs.spacingOpen != null) {
|
|
|
- spacingOpen = parseInt(attrs.spacingOpen, 10)
|
|
|
- } else {
|
|
|
- spacingOpen = 7
|
|
|
- }
|
|
|
+ if (attrs.spacingOpen != null) {
|
|
|
+ spacingOpen = parseInt(attrs.spacingOpen, 10)
|
|
|
+ } else {
|
|
|
+ spacingOpen = 7
|
|
|
+ }
|
|
|
|
|
|
- if (attrs.spacingClosed != null) {
|
|
|
- spacingClosed = parseInt(attrs.spacingClosed, 10)
|
|
|
- } else {
|
|
|
- spacingClosed = 7
|
|
|
- }
|
|
|
+ if (attrs.spacingClosed != null) {
|
|
|
+ spacingClosed = parseInt(attrs.spacingClosed, 10)
|
|
|
+ } else {
|
|
|
+ spacingClosed = 7
|
|
|
+ }
|
|
|
|
|
|
- const options = {
|
|
|
- spacing_open: spacingOpen,
|
|
|
- spacing_closed: spacingClosed,
|
|
|
- slidable: false,
|
|
|
- enableCursorHotkey: false,
|
|
|
- onopen: pane => {
|
|
|
- return onPaneOpen(pane)
|
|
|
- },
|
|
|
- onclose: pane => {
|
|
|
- return onPaneClose(pane)
|
|
|
- },
|
|
|
- onresize: () => {
|
|
|
- return onInternalResize()
|
|
|
- },
|
|
|
- maskIframesOnResize: scope.$eval(
|
|
|
- attrs.maskIframesOnResize || 'false'
|
|
|
- ),
|
|
|
- east: {
|
|
|
- size: scope.$eval(attrs.initialSizeEast),
|
|
|
- initClosed: scope.$eval(attrs.initClosedEast),
|
|
|
- },
|
|
|
- west: {
|
|
|
- size: scope.$eval(attrs.initialSizeWest),
|
|
|
- initClosed: scope.$eval(attrs.initClosedWest),
|
|
|
- },
|
|
|
- }
|
|
|
+ const options = {
|
|
|
+ spacing_open: spacingOpen,
|
|
|
+ spacing_closed: spacingClosed,
|
|
|
+ slidable: false,
|
|
|
+ enableCursorHotkey: false,
|
|
|
+ onopen: pane => {
|
|
|
+ return onPaneOpen(pane)
|
|
|
+ },
|
|
|
+ onclose: pane => {
|
|
|
+ return onPaneClose(pane)
|
|
|
+ },
|
|
|
+ onresize: () => {
|
|
|
+ return onInternalResize()
|
|
|
+ },
|
|
|
+ maskIframesOnResize: scope.$eval(
|
|
|
+ attrs.maskIframesOnResize || 'false'
|
|
|
+ ),
|
|
|
+ east: {
|
|
|
+ size: scope.$eval(attrs.initialSizeEast),
|
|
|
+ initClosed: scope.$eval(attrs.initClosedEast),
|
|
|
+ },
|
|
|
+ west: {
|
|
|
+ size: scope.$eval(attrs.initialSizeWest),
|
|
|
+ initClosed: scope.$eval(attrs.initClosedWest),
|
|
|
+ },
|
|
|
+ }
|
|
|
|
|
|
- // Restore previously recorded state
|
|
|
- if ((state = ide.localStorage(`layout.${name}`)) != null) {
|
|
|
- if (state.east != null) {
|
|
|
- if (
|
|
|
- attrs.minimumRestoreSizeEast == null ||
|
|
|
- (state.east.size >= attrs.minimumRestoreSizeEast &&
|
|
|
- !state.east.initClosed)
|
|
|
- ) {
|
|
|
- options.east = state.east
|
|
|
+ // Restore previously recorded state
|
|
|
+ if ((state = ide.localStorage(`layout.${name}`)) != null) {
|
|
|
+ if (state.east != null) {
|
|
|
+ if (
|
|
|
+ attrs.minimumRestoreSizeEast == null ||
|
|
|
+ (state.east.size >= attrs.minimumRestoreSizeEast &&
|
|
|
+ !state.east.initClosed)
|
|
|
+ ) {
|
|
|
+ options.east = state.east
|
|
|
+ }
|
|
|
+ options.east.initClosed = state.east.initClosed
|
|
|
}
|
|
|
- options.east.initClosed = state.east.initClosed
|
|
|
- }
|
|
|
- if (state.west != null) {
|
|
|
- if (
|
|
|
- attrs.minimumRestoreSizeWest == null ||
|
|
|
- (state.west.size >= attrs.minimumRestoreSizeWest &&
|
|
|
- !state.west.initClosed)
|
|
|
- ) {
|
|
|
- options.west = state.west
|
|
|
+ if (state.west != null) {
|
|
|
+ if (
|
|
|
+ attrs.minimumRestoreSizeWest == null ||
|
|
|
+ (state.west.size >= attrs.minimumRestoreSizeWest &&
|
|
|
+ !state.west.initClosed)
|
|
|
+ ) {
|
|
|
+ options.west = state.west
|
|
|
+ }
|
|
|
+ // NOTE: disabled so that the file tree re-opens on page load
|
|
|
+ // options.west.initClosed = state.west.initClosed
|
|
|
}
|
|
|
- // NOTE: disabled so that the file tree re-opens on page load
|
|
|
- // options.west.initClosed = state.west.initClosed
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- options.east.resizerCursor = 'ew-resize'
|
|
|
- options.west.resizerCursor = 'ew-resize'
|
|
|
+ options.east.resizerCursor = 'ew-resize'
|
|
|
+ options.west.resizerCursor = 'ew-resize'
|
|
|
|
|
|
- function repositionControls() {
|
|
|
- state = layout.readState()
|
|
|
- if (state.east != null) {
|
|
|
- const controls = element.find('> .ui-layout-resizer-controls')
|
|
|
- if (state.east.initClosed) {
|
|
|
- return controls.hide()
|
|
|
- } else {
|
|
|
- controls.show()
|
|
|
- return controls.css({
|
|
|
- right: state.east.size,
|
|
|
- })
|
|
|
+ function repositionControls() {
|
|
|
+ state = layout.readState()
|
|
|
+ if (state.east != null) {
|
|
|
+ const controls = element.find('> .ui-layout-resizer-controls')
|
|
|
+ if (state.east.initClosed) {
|
|
|
+ return controls.hide()
|
|
|
+ } else {
|
|
|
+ controls.show()
|
|
|
+ return controls.css({
|
|
|
+ right: state.east.size,
|
|
|
+ })
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- function repositionCustomToggler() {
|
|
|
- if (customTogglerEl == null) {
|
|
|
- return
|
|
|
- }
|
|
|
- state = layout.readState()
|
|
|
- const positionAnchor =
|
|
|
- customTogglerPane === 'east' ? 'right' : 'left'
|
|
|
- const paneState = state[customTogglerPane]
|
|
|
- if (paneState != null) {
|
|
|
- return customTogglerEl.css(
|
|
|
- positionAnchor,
|
|
|
- paneState.initClosed ? 0 : paneState.size
|
|
|
- )
|
|
|
+ function repositionCustomToggler() {
|
|
|
+ if (customTogglerEl == null) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ state = layout.readState()
|
|
|
+ const positionAnchor =
|
|
|
+ customTogglerPane === 'east' ? 'right' : 'left'
|
|
|
+ const paneState = state[customTogglerPane]
|
|
|
+ if (paneState != null) {
|
|
|
+ return customTogglerEl.css(
|
|
|
+ positionAnchor,
|
|
|
+ paneState.initClosed ? 0 : paneState.size
|
|
|
+ )
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- function resetOpenStates() {
|
|
|
- state = layout.readState()
|
|
|
- if (attrs.openEast != null && state.east != null) {
|
|
|
- const openEast = $parse(attrs.openEast)
|
|
|
- return openEast.assign(scope, !state.east.initClosed)
|
|
|
+ function resetOpenStates() {
|
|
|
+ state = layout.readState()
|
|
|
+ if (attrs.openEast != null && state.east != null) {
|
|
|
+ const openEast = $parse(attrs.openEast)
|
|
|
+ return openEast.assign(scope, !state.east.initClosed)
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- // Someone moved the resizer
|
|
|
- function onInternalResize() {
|
|
|
- state = layout.readState()
|
|
|
- scope.$broadcast(`layout:${name}:resize`, state)
|
|
|
- repositionControls()
|
|
|
- if (hasCustomToggler) {
|
|
|
- repositionCustomToggler()
|
|
|
+ // Someone moved the resizer
|
|
|
+ function onInternalResize() {
|
|
|
+ state = layout.readState()
|
|
|
+ scope.$broadcast(`layout:${name}:resize`, state)
|
|
|
+ repositionControls()
|
|
|
+ if (hasCustomToggler) {
|
|
|
+ repositionCustomToggler()
|
|
|
+ }
|
|
|
+ return resetOpenStates()
|
|
|
}
|
|
|
- return resetOpenStates()
|
|
|
- }
|
|
|
|
|
|
- let oldWidth = element.width()
|
|
|
- // Something resized our parent element
|
|
|
- const onExternalResize = function () {
|
|
|
- if (
|
|
|
- attrs.resizeProportionally != null &&
|
|
|
- scope.$eval(attrs.resizeProportionally)
|
|
|
- ) {
|
|
|
- const eastState = layout.readState().east
|
|
|
- if (eastState != null) {
|
|
|
- const currentWidth = element.width()
|
|
|
- if (currentWidth > 0) {
|
|
|
- const newInternalWidth =
|
|
|
- (eastState.size / oldWidth) * currentWidth
|
|
|
- oldWidth = currentWidth
|
|
|
- layout.sizePane('east', newInternalWidth)
|
|
|
+ let oldWidth = element.width()
|
|
|
+ // Something resized our parent element
|
|
|
+ const onExternalResize = function () {
|
|
|
+ if (
|
|
|
+ attrs.resizeProportionally != null &&
|
|
|
+ scope.$eval(attrs.resizeProportionally)
|
|
|
+ ) {
|
|
|
+ const eastState = layout.readState().east
|
|
|
+ if (eastState != null) {
|
|
|
+ const currentWidth = element.width()
|
|
|
+ if (currentWidth > 0) {
|
|
|
+ const newInternalWidth =
|
|
|
+ (eastState.size / oldWidth) * currentWidth
|
|
|
+ oldWidth = currentWidth
|
|
|
+ layout.sizePane('east', newInternalWidth)
|
|
|
+ }
|
|
|
+ return
|
|
|
}
|
|
|
- return
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- ide.$timeout(() => {
|
|
|
- layout.resizeAll()
|
|
|
- })
|
|
|
- }
|
|
|
+ ide.$timeout(() => {
|
|
|
+ layout.resizeAll()
|
|
|
+ })
|
|
|
+ }
|
|
|
|
|
|
- const layout = element.layout(options)
|
|
|
- layout.resizeAll()
|
|
|
+ const layout = element.layout(options)
|
|
|
+ layout.resizeAll()
|
|
|
|
|
|
- if (attrs.resizeOn != null) {
|
|
|
- for (const event of Array.from(attrs.resizeOn.split(','))) {
|
|
|
- scope.$on(event, () => onExternalResize())
|
|
|
+ if (attrs.resizeOn != null) {
|
|
|
+ for (const event of Array.from(attrs.resizeOn.split(','))) {
|
|
|
+ scope.$on(event, () => onExternalResize())
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- if (hasCustomToggler) {
|
|
|
- state = layout.readState()
|
|
|
- const customTogglerScope = scope.$new()
|
|
|
+ if (hasCustomToggler) {
|
|
|
+ state = layout.readState()
|
|
|
+ const customTogglerScope = scope.$new()
|
|
|
|
|
|
- customTogglerScope.isOpen = true
|
|
|
- customTogglerScope.isVisible = true
|
|
|
+ customTogglerScope.isOpen = true
|
|
|
+ customTogglerScope.isVisible = true
|
|
|
|
|
|
- if (
|
|
|
- (state[customTogglerPane] != null
|
|
|
- ? state[customTogglerPane].initClosed
|
|
|
- : undefined) === true
|
|
|
- ) {
|
|
|
- customTogglerScope.isOpen = false
|
|
|
- }
|
|
|
+ if (
|
|
|
+ (state[customTogglerPane] != null
|
|
|
+ ? state[customTogglerPane].initClosed
|
|
|
+ : undefined) === true
|
|
|
+ ) {
|
|
|
+ customTogglerScope.isOpen = false
|
|
|
+ }
|
|
|
|
|
|
- customTogglerScope.tooltipMsgWhenOpen = customTogglerMsgWhenOpen
|
|
|
- customTogglerScope.tooltipMsgWhenClosed = customTogglerMsgWhenClosed
|
|
|
+ customTogglerScope.tooltipMsgWhenOpen = customTogglerMsgWhenOpen
|
|
|
+ customTogglerScope.tooltipMsgWhenClosed =
|
|
|
+ customTogglerMsgWhenClosed
|
|
|
|
|
|
- customTogglerScope.tooltipPlacement =
|
|
|
- customTogglerPane === 'east' ? 'left' : 'right'
|
|
|
- customTogglerScope.handleClick = function () {
|
|
|
- layout.toggle(customTogglerPane)
|
|
|
- return repositionCustomToggler()
|
|
|
- }
|
|
|
- customTogglerEl = $compile(`\
|
|
|
+ customTogglerScope.tooltipPlacement =
|
|
|
+ customTogglerPane === 'east' ? 'left' : 'right'
|
|
|
+ customTogglerScope.handleClick = function () {
|
|
|
+ layout.toggle(customTogglerPane)
|
|
|
+ return repositionCustomToggler()
|
|
|
+ }
|
|
|
+ customTogglerEl = $compile(`\
|
|
|
<a href \
|
|
|
ng-show=\"isVisible\" \
|
|
|
class=\"custom-toggler ${`custom-toggler-${customTogglerPane}`}\" \
|
|
|
@@ -221,100 +223,101 @@ tooltip-placement=\"{{ tooltipPlacement }}\" \
|
|
|
ng-click=\"handleClick()\" \
|
|
|
aria-label=\"{{ isOpen ? tooltipMsgWhenOpen : tooltipMsgWhenClosed }}\">\
|
|
|
`)(customTogglerScope)
|
|
|
- element.append(customTogglerEl)
|
|
|
- }
|
|
|
+ element.append(customTogglerEl)
|
|
|
+ }
|
|
|
|
|
|
- function onPaneOpen(pane) {
|
|
|
- if (!hasCustomToggler || pane !== customTogglerPane) {
|
|
|
- return
|
|
|
+ function onPaneOpen(pane) {
|
|
|
+ if (!hasCustomToggler || pane !== customTogglerPane) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ return customTogglerEl
|
|
|
+ .scope()
|
|
|
+ .$applyAsync(() => (customTogglerEl.scope().isOpen = true))
|
|
|
}
|
|
|
- return customTogglerEl
|
|
|
- .scope()
|
|
|
- .$applyAsync(() => (customTogglerEl.scope().isOpen = true))
|
|
|
- }
|
|
|
|
|
|
- function onPaneClose(pane) {
|
|
|
- if (!hasCustomToggler || pane !== customTogglerPane) {
|
|
|
- return
|
|
|
+ function onPaneClose(pane) {
|
|
|
+ if (!hasCustomToggler || pane !== customTogglerPane) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ return customTogglerEl
|
|
|
+ .scope()
|
|
|
+ .$applyAsync(() => (customTogglerEl.scope().isOpen = false))
|
|
|
+ }
|
|
|
+
|
|
|
+ // Ensure editor resizes after loading. This is to handle the case where
|
|
|
+ // the window has been resized while the editor is loading
|
|
|
+ scope.$on('editor:loaded', () => {
|
|
|
+ ide.$timeout(() => layout.resizeAll())
|
|
|
+ })
|
|
|
+
|
|
|
+ // Save state when exiting
|
|
|
+ $(window).unload(() => {
|
|
|
+ // Save only the state properties for the current layout, ignoring sublayouts inside it.
|
|
|
+ // If we save sublayouts state (`children`), the layout library will use it when
|
|
|
+ // initializing. This raises errors when the sublayout elements aren't available (due to
|
|
|
+ // being loaded at init or just not existing for the current project/user).
|
|
|
+ const stateToSave = _.mapValues(layout.readState(), pane =>
|
|
|
+ _.omit(pane, 'children')
|
|
|
+ )
|
|
|
+ ide.localStorage(`layout.${name}`, stateToSave)
|
|
|
+ })
|
|
|
+
|
|
|
+ if (attrs.openEast != null) {
|
|
|
+ scope.$watch(attrs.openEast, function (value, oldValue) {
|
|
|
+ if (value != null && value !== oldValue) {
|
|
|
+ if (value) {
|
|
|
+ layout.open('east')
|
|
|
+ } else {
|
|
|
+ layout.close('east')
|
|
|
+ }
|
|
|
+ if (hasCustomToggler && customTogglerPane === 'east') {
|
|
|
+ repositionCustomToggler()
|
|
|
+ customTogglerEl.scope().$applyAsync(function () {
|
|
|
+ customTogglerEl.scope().isOpen = value
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return setTimeout(() => scope.$digest(), 0)
|
|
|
+ })
|
|
|
}
|
|
|
- return customTogglerEl
|
|
|
- .scope()
|
|
|
- .$applyAsync(() => (customTogglerEl.scope().isOpen = false))
|
|
|
- }
|
|
|
|
|
|
- // Ensure editor resizes after loading. This is to handle the case where
|
|
|
- // the window has been resized while the editor is loading
|
|
|
- scope.$on('editor:loaded', () => {
|
|
|
- ide.$timeout(() => layout.resizeAll())
|
|
|
- })
|
|
|
+ if (attrs.allowOverflowOn != null) {
|
|
|
+ const overflowPane = scope.$eval(attrs.allowOverflowOn)
|
|
|
+ const overflowPaneEl = layout.panes[overflowPane]
|
|
|
+ // Set the panel as overflowing (gives it higher z-index and sets overflow rules)
|
|
|
+ layout.allowOverflow(overflowPane)
|
|
|
+ // Read the given z-index value and increment it, so that it's higher than synctex controls.
|
|
|
+ const overflowPaneZVal = overflowPaneEl.zIndex()
|
|
|
+ overflowPaneEl.css('z-index', overflowPaneZVal + 1)
|
|
|
+ }
|
|
|
|
|
|
- // Save state when exiting
|
|
|
- $(window).unload(() => {
|
|
|
- // Save only the state properties for the current layout, ignoring sublayouts inside it.
|
|
|
- // If we save sublayouts state (`children`), the layout library will use it when
|
|
|
- // initializing. This raises errors when the sublayout elements aren't available (due to
|
|
|
- // being loaded at init or just not existing for the current project/user).
|
|
|
- const stateToSave = _.mapValues(layout.readState(), pane =>
|
|
|
- _.omit(pane, 'children')
|
|
|
- )
|
|
|
- ide.localStorage(`layout.${name}`, stateToSave)
|
|
|
- })
|
|
|
+ resetOpenStates()
|
|
|
+ onInternalResize()
|
|
|
|
|
|
- if (attrs.openEast != null) {
|
|
|
- scope.$watch(attrs.openEast, function (value, oldValue) {
|
|
|
- if (value != null && value !== oldValue) {
|
|
|
+ if (attrs.layoutDisabled != null) {
|
|
|
+ return scope.$watch(attrs.layoutDisabled, function (value) {
|
|
|
if (value) {
|
|
|
- layout.open('east')
|
|
|
+ layout.hide('east')
|
|
|
} else {
|
|
|
- layout.close('east')
|
|
|
+ layout.show('east')
|
|
|
}
|
|
|
- if (hasCustomToggler && customTogglerPane === 'east') {
|
|
|
- repositionCustomToggler()
|
|
|
- customTogglerEl.scope().$applyAsync(function () {
|
|
|
- customTogglerEl.scope().isOpen = value
|
|
|
+ if (hasCustomToggler) {
|
|
|
+ return customTogglerEl.scope().$applyAsync(function () {
|
|
|
+ customTogglerEl.scope().isOpen = !value
|
|
|
+ return (customTogglerEl.scope().isVisible = !value)
|
|
|
})
|
|
|
}
|
|
|
- }
|
|
|
- return setTimeout(() => scope.$digest(), 0)
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- if (attrs.allowOverflowOn != null) {
|
|
|
- const overflowPane = scope.$eval(attrs.allowOverflowOn)
|
|
|
- const overflowPaneEl = layout.panes[overflowPane]
|
|
|
- // Set the panel as overflowing (gives it higher z-index and sets overflow rules)
|
|
|
- layout.allowOverflow(overflowPane)
|
|
|
- // Read the given z-index value and increment it, so that it's higher than synctex controls.
|
|
|
- const overflowPaneZVal = overflowPaneEl.zIndex()
|
|
|
- overflowPaneEl.css('z-index', overflowPaneZVal + 1)
|
|
|
- }
|
|
|
-
|
|
|
- resetOpenStates()
|
|
|
- onInternalResize()
|
|
|
-
|
|
|
- if (attrs.layoutDisabled != null) {
|
|
|
- return scope.$watch(attrs.layoutDisabled, function (value) {
|
|
|
- if (value) {
|
|
|
- layout.hide('east')
|
|
|
- } else {
|
|
|
- layout.show('east')
|
|
|
- }
|
|
|
- if (hasCustomToggler) {
|
|
|
- return customTogglerEl.scope().$applyAsync(function () {
|
|
|
- customTogglerEl.scope().isOpen = !value
|
|
|
- return (customTogglerEl.scope().isVisible = !value)
|
|
|
- })
|
|
|
- }
|
|
|
- })
|
|
|
- }
|
|
|
- },
|
|
|
+ })
|
|
|
+ }
|
|
|
+ },
|
|
|
|
|
|
- post(scope, element, attrs) {
|
|
|
- const name = attrs.layout
|
|
|
- const state = element.layout().readState()
|
|
|
- return scope.$broadcast(`layout:${name}:linked`, state)
|
|
|
- },
|
|
|
- }
|
|
|
- },
|
|
|
- }),
|
|
|
+ post(scope, element, attrs) {
|
|
|
+ const name = attrs.layout
|
|
|
+ const state = element.layout().readState()
|
|
|
+ return scope.$broadcast(`layout:${name}:linked`, state)
|
|
|
+ },
|
|
|
+ }
|
|
|
+ },
|
|
|
+ }
|
|
|
+ },
|
|
|
])
|