@atlaskit/jql-editor 6.4.7 → 6.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/CHANGELOG.md +22 -0
- package/dist/cjs/analytics/util.js +1 -1
- package/dist/cjs/plugins/autocomplete/constants.js +3 -0
- package/dist/cjs/plugins/rich-inline-nodes/nodes/index.js +2 -0
- package/dist/cjs/plugins/rich-inline-nodes/nodes/lozenge-with-avatar/index.js +41 -0
- package/dist/cjs/plugins/rich-inline-nodes/nodes/lozenge-with-avatar/styled.js +105 -0
- package/dist/cjs/plugins/rich-inline-nodes/util/replace-nodes-transaction.js +86 -33
- package/dist/cjs/state/hydration/util.js +6 -6
- package/dist/cjs/state/index.js +65 -39
- package/dist/es2019/analytics/util.js +1 -1
- package/dist/es2019/plugins/autocomplete/constants.js +2 -1
- package/dist/es2019/plugins/rich-inline-nodes/nodes/index.js +2 -0
- package/dist/es2019/plugins/rich-inline-nodes/nodes/lozenge-with-avatar/index.js +32 -0
- package/dist/es2019/plugins/rich-inline-nodes/nodes/lozenge-with-avatar/styled.js +97 -0
- package/dist/es2019/plugins/rich-inline-nodes/util/replace-nodes-transaction.js +63 -15
- package/dist/es2019/state/hydration/util.js +6 -6
- package/dist/es2019/state/index.js +32 -4
- package/dist/esm/analytics/util.js +1 -1
- package/dist/esm/plugins/autocomplete/constants.js +3 -0
- package/dist/esm/plugins/rich-inline-nodes/nodes/index.js +2 -0
- package/dist/esm/plugins/rich-inline-nodes/nodes/lozenge-with-avatar/index.js +34 -0
- package/dist/esm/plugins/rich-inline-nodes/nodes/lozenge-with-avatar/styled.js +96 -0
- package/dist/esm/plugins/rich-inline-nodes/util/replace-nodes-transaction.js +86 -33
- package/dist/esm/state/hydration/util.js +6 -6
- package/dist/esm/state/index.js +64 -38
- package/dist/types/plugins/rich-inline-nodes/nodes/index.d.ts +2 -0
- package/dist/types/plugins/rich-inline-nodes/nodes/lozenge-with-avatar/index.d.ts +7 -0
- package/dist/types/plugins/rich-inline-nodes/nodes/lozenge-with-avatar/styled.d.ts +18 -0
- package/dist/types/state/index.d.ts +5 -1
- package/dist/types/types.d.ts +1 -1
- package/dist/types/ui/jql-editor/types.d.ts +7 -1
- package/dist/types/ui/types.d.ts +1 -1
- package/dist/types-ts4.5/plugins/rich-inline-nodes/nodes/index.d.ts +2 -0
- package/dist/types-ts4.5/plugins/rich-inline-nodes/nodes/lozenge-with-avatar/index.d.ts +7 -0
- package/dist/types-ts4.5/plugins/rich-inline-nodes/nodes/lozenge-with-avatar/styled.d.ts +18 -0
- package/dist/types-ts4.5/state/index.d.ts +5 -1
- package/dist/types-ts4.5/types.d.ts +1 -1
- package/dist/types-ts4.5/ui/jql-editor/types.d.ts +7 -1
- package/dist/types-ts4.5/ui/types.d.ts +1 -1
- package/package.json +17 -11
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# @atlaskit/jql-editor
|
|
2
2
|
|
|
3
|
+
## 6.6.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`506238c0247fd`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/506238c0247fd) -
|
|
8
|
+
PTC-16709: Added changes to show autocomplete for membersOf function to fetch Teams
|
|
9
|
+
|
|
10
|
+
### Patch Changes
|
|
11
|
+
|
|
12
|
+
- Updated dependencies
|
|
13
|
+
|
|
14
|
+
## 6.5.0
|
|
15
|
+
|
|
16
|
+
### Minor Changes
|
|
17
|
+
|
|
18
|
+
- [`9ead91bedc94b`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/9ead91bedc94b) -
|
|
19
|
+
[ux] Add function argument hydration support behind feature flag
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- Updated dependencies
|
|
24
|
+
|
|
3
25
|
## 6.4.7
|
|
4
26
|
|
|
5
27
|
### Patch Changes
|
|
@@ -6,5 +6,5 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.useJqlEditorAnalytics = void 0;
|
|
7
7
|
var _jqlEditorCommon = require("@atlaskit/jql-editor-common");
|
|
8
8
|
var useJqlEditorAnalytics = exports.useJqlEditorAnalytics = function useJqlEditorAnalytics(analyticsSource) {
|
|
9
|
-
return (0, _jqlEditorCommon.useJqlPackageAnalytics)(analyticsSource, "@atlaskit/jql-editor", "6.
|
|
9
|
+
return (0, _jqlEditorCommon.useJqlPackageAnalytics)(analyticsSource, "@atlaskit/jql-editor", "6.5.0", _jqlEditorCommon.ANALYTICS_CHANNEL);
|
|
10
10
|
};
|
|
@@ -30,6 +30,9 @@ var defaultAutocompleteProvider = exports.defaultAutocompleteProvider = {
|
|
|
30
30
|
},
|
|
31
31
|
onFunctions: function onFunctions() {
|
|
32
32
|
return (0, _empty.empty)();
|
|
33
|
+
},
|
|
34
|
+
onFunctionArguments: function onFunctionArguments() {
|
|
35
|
+
return (0, _empty.empty)();
|
|
33
36
|
}
|
|
34
37
|
};
|
|
35
38
|
var JQLAutocompletePluginKey = exports.JQLAutocompletePluginKey = new _state.PluginKey(AUTOCOMPLETE_PLUGIN_NAME);
|
|
@@ -5,10 +5,12 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.richInlineNodes = void 0;
|
|
7
7
|
var _goal = require("./goal");
|
|
8
|
+
var _lozengeWithAvatar = require("./lozenge-with-avatar");
|
|
8
9
|
var _project = require("./project");
|
|
9
10
|
var _team = require("./team");
|
|
10
11
|
var _user = require("./user");
|
|
11
12
|
var richInlineNodes = exports.richInlineNodes = {
|
|
13
|
+
lozengeWithAvatar: _lozengeWithAvatar.lozengeWithAvatar,
|
|
12
14
|
user: _user.user,
|
|
13
15
|
team: _team.team,
|
|
14
16
|
project: _project.project,
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.lozengeWithAvatar = void 0;
|
|
8
|
+
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
9
|
+
var _react = _interopRequireDefault(require("react"));
|
|
10
|
+
var _avatar = _interopRequireDefault(require("@atlaskit/avatar"));
|
|
11
|
+
var _state = require("../../../../state");
|
|
12
|
+
var _styled = require("./styled");
|
|
13
|
+
var LozengeWithAvatar = function LozengeWithAvatar(_ref) {
|
|
14
|
+
var id = _ref.id,
|
|
15
|
+
name = _ref.name,
|
|
16
|
+
fieldName = _ref.fieldName,
|
|
17
|
+
selected = _ref.selected,
|
|
18
|
+
error = _ref.error;
|
|
19
|
+
var _useHydratedLozengeWi = (0, _state.useHydratedLozengeWithAvatar)({
|
|
20
|
+
id: id,
|
|
21
|
+
fieldName: fieldName
|
|
22
|
+
}),
|
|
23
|
+
_useHydratedLozengeWi2 = (0, _slicedToArray2.default)(_useHydratedLozengeWi, 1),
|
|
24
|
+
lozengeWithAvatar = _useHydratedLozengeWi2[0];
|
|
25
|
+
return /*#__PURE__*/_react.default.createElement(_styled.LozengeWithAvatarContainer, {
|
|
26
|
+
selected: selected,
|
|
27
|
+
error: error
|
|
28
|
+
}, /*#__PURE__*/_react.default.createElement(_styled.AvatarWrapper, null, /*#__PURE__*/_react.default.createElement(_avatar.default, {
|
|
29
|
+
src: lozengeWithAvatar === null || lozengeWithAvatar === void 0 ? void 0 : lozengeWithAvatar.avatarUrl,
|
|
30
|
+
borderColor: "transparent",
|
|
31
|
+
size: "xsmall"
|
|
32
|
+
})), /*#__PURE__*/_react.default.createElement(_styled.NameContainer, null, name));
|
|
33
|
+
};
|
|
34
|
+
var lozengeWithAvatar = exports.lozengeWithAvatar = {
|
|
35
|
+
component: LozengeWithAvatar,
|
|
36
|
+
attrs: {
|
|
37
|
+
id: {},
|
|
38
|
+
name: {},
|
|
39
|
+
fieldName: {}
|
|
40
|
+
}
|
|
41
|
+
};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.NameContainer = exports.LozengeWithAvatarContainer = exports.AvatarWrapper = void 0;
|
|
8
|
+
var _react = require("@emotion/react");
|
|
9
|
+
var _styled = _interopRequireDefault(require("@emotion/styled"));
|
|
10
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
|
|
11
|
+
|
|
12
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
|
|
13
|
+
|
|
14
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
|
|
15
|
+
|
|
16
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-exported-styles -- Ignored via go/DSP-18766
|
|
17
|
+
var LozengeWithAvatarContainer = exports.LozengeWithAvatarContainer =
|
|
18
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-styled -- Ignored via go/DSP-18766
|
|
19
|
+
_styled.default.span({
|
|
20
|
+
display: 'inline-flex',
|
|
21
|
+
alignItems: 'baseline',
|
|
22
|
+
paddingLeft: "var(--ds-space-025, 2px)",
|
|
23
|
+
borderRadius: "var(--ds-radius-xlarge, 12px)",
|
|
24
|
+
cursor: 'pointer',
|
|
25
|
+
userSelect: 'none'
|
|
26
|
+
},
|
|
27
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-dynamic-styles -- Ignored via go/DSP-18766
|
|
28
|
+
function (_ref) {
|
|
29
|
+
var selected = _ref.selected,
|
|
30
|
+
error = _ref.error;
|
|
31
|
+
if (selected) {
|
|
32
|
+
if (error) {
|
|
33
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766
|
|
34
|
+
return (0, _react.css)({
|
|
35
|
+
color: "var(--ds-text-inverse, #FFFFFF)",
|
|
36
|
+
backgroundColor: "var(--ds-background-danger-bold, #C9372C)",
|
|
37
|
+
textDecoration: 'wavy underline',
|
|
38
|
+
textDecorationThickness: '1px',
|
|
39
|
+
textDecorationSkipInk: 'none',
|
|
40
|
+
textDecorationColor: "var(--ds-text-inverse, #FFFFFF)"
|
|
41
|
+
});
|
|
42
|
+
} else {
|
|
43
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766
|
|
44
|
+
return (0, _react.css)({
|
|
45
|
+
color: "var(--ds-text, #292A2E)",
|
|
46
|
+
backgroundColor: "var(--ds-background-selected, #E9F2FE)",
|
|
47
|
+
boxShadow: "0 0 0 1px ".concat("var(--ds-border-selected, #1868DB)")
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
} else {
|
|
51
|
+
if (error) {
|
|
52
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766
|
|
53
|
+
return (0, _react.css)({
|
|
54
|
+
color: "var(--ds-text-subtle, #505258)",
|
|
55
|
+
backgroundColor: "var(--ds-background-neutral, #0515240F)",
|
|
56
|
+
textDecoration: 'wavy underline',
|
|
57
|
+
textDecorationThickness: '1px',
|
|
58
|
+
textDecorationSkipInk: 'none',
|
|
59
|
+
textDecorationColor: "var(--ds-text-danger, #AE2E24)",
|
|
60
|
+
'&:hover': {
|
|
61
|
+
backgroundColor: "var(--ds-background-neutral-hovered, #0B120E24)"
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
} else {
|
|
65
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766
|
|
66
|
+
return (0, _react.css)({
|
|
67
|
+
color: "var(--ds-text-subtle, #505258)",
|
|
68
|
+
backgroundColor: "var(--ds-background-neutral, #0515240F)",
|
|
69
|
+
'&:hover': {
|
|
70
|
+
backgroundColor: "var(--ds-background-neutral-hovered, #0B120E24)"
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-exported-styles -- Ignored via go/DSP-18766
|
|
78
|
+
var NameContainer = exports.NameContainer =
|
|
79
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-styled -- Ignored via go/DSP-18766
|
|
80
|
+
_styled.default.span({
|
|
81
|
+
marginLeft: "var(--ds-space-075, 6px)",
|
|
82
|
+
marginRight: "var(--ds-space-100, 8px)",
|
|
83
|
+
// eslint-disable-next-line -- Ignored via go/DSP-18766
|
|
84
|
+
lineHeight: "var(--ds-space-250, 20px)"
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
/* Override Avatar styles to match design spec */
|
|
88
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-exported-styles -- Ignored via go/DSP-18766
|
|
89
|
+
var AvatarWrapper = exports.AvatarWrapper =
|
|
90
|
+
// eslint-disable-next-line @atlaskit/design-system/ensure-design-token-usage, @atlaskit/ui-styling-standard/no-styled -- Ignored via go/DSP-18766
|
|
91
|
+
_styled.default.div({
|
|
92
|
+
height: "var(--ds-space-200, 16px)",
|
|
93
|
+
width: "var(--ds-space-200, 16px)",
|
|
94
|
+
alignSelf: 'center',
|
|
95
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors -- Ignored via go/DSP-18766
|
|
96
|
+
'> div span': {
|
|
97
|
+
margin: "var(--ds-space-0, 0px)"
|
|
98
|
+
},
|
|
99
|
+
// Fix fallback avatar icon vertical alignment.
|
|
100
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors, @atlaskit/ui-styling-standard/no-unsafe-selectors -- Ignored via go/DSP-18766
|
|
101
|
+
'> div > span:has(> span)': {
|
|
102
|
+
position: 'relative',
|
|
103
|
+
top: '-2px'
|
|
104
|
+
}
|
|
105
|
+
});
|
|
@@ -5,13 +5,13 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
value: true
|
|
6
6
|
});
|
|
7
7
|
exports.replaceRichInlineNodes = void 0;
|
|
8
|
-
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
|
|
9
8
|
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
|
|
10
9
|
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
|
|
11
10
|
var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
|
|
12
11
|
var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
|
|
13
12
|
var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
|
|
14
13
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
14
|
+
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
|
|
15
15
|
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
16
16
|
var _featureGateJsClient = _interopRequireDefault(require("@atlaskit/feature-gate-js-client"));
|
|
17
17
|
var _jqlAst = require("@atlaskit/jql-ast");
|
|
@@ -34,32 +34,42 @@ var replaceRichInlineNodes = exports.replaceRichInlineNodes = function replaceRi
|
|
|
34
34
|
fieldName = _ref2[0],
|
|
35
35
|
values = _ref2[1];
|
|
36
36
|
values.forEach(function (value) {
|
|
37
|
-
if (
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
// If not found as direct value and it's a team, try to find in membersOf function arguments
|
|
42
|
-
if (astNodes.length === 0 && value.type === 'team' && (0, _platformFeatureFlags.fg)('jira-membersof-team-support')) {
|
|
43
|
-
astNodes = getMembersOfArgumentNodes(ast, value.id);
|
|
37
|
+
if ((0, _platformFeatureFlags.fg)('jql-function-arg-hydration')) {
|
|
38
|
+
// Skip deprecated fields
|
|
39
|
+
if (value.type === 'deprecated-field') {
|
|
40
|
+
return;
|
|
44
41
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
});
|
|
42
|
+
// When the gate is on, collect both direct value operands and function argument
|
|
43
|
+
// operands for all node types
|
|
44
|
+
var astNodes = [].concat((0, _toConsumableArray2.default)(getValueNodes(ast, fieldName, value.id)), (0, _toConsumableArray2.default)(getFunctionArgumentNodes(ast, fieldName, value.id)));
|
|
45
|
+
replaceAstNodesWithRichInlineNodes(transaction, astNodes, fieldName, value);
|
|
46
|
+
} else if (value.type === 'user' || value.type === 'team' || value.type === 'goal' && _featureGateJsClient.default.getExperimentValue('anip-1095-goals-in-harmonised-filter', 'isEnabled', false) || value.type === 'project' && _featureGateJsClient.default.getExperimentValue('atlassian_projects_-_native_integration', 'releaseVersion', -1) >= 1) {
|
|
47
|
+
// Legacy path: direct value operands only, with membersOf fallback for teams.
|
|
48
|
+
var _astNodes = getValueNodes(ast, fieldName, value.id);
|
|
49
|
+
if (_astNodes.length === 0 && value.type === 'team' && (0, _platformFeatureFlags.fg)('jira-membersof-team-support')) {
|
|
50
|
+
_astNodes = getMembersOfArgumentNodes(ast, value.id);
|
|
51
|
+
}
|
|
52
|
+
replaceAstNodesWithRichInlineNodes(transaction, _astNodes, fieldName, value);
|
|
58
53
|
}
|
|
59
54
|
});
|
|
60
55
|
});
|
|
61
56
|
return transaction;
|
|
62
57
|
};
|
|
58
|
+
var replaceAstNodesWithRichInlineNodes = function replaceAstNodesWithRichInlineNodes(transaction, astNodes, fieldName, value) {
|
|
59
|
+
astNodes.forEach(function (astNode) {
|
|
60
|
+
if (astNode.position) {
|
|
61
|
+
var _astNode$position = (0, _slicedToArray2.default)(astNode.position, 2),
|
|
62
|
+
from = _astNode$position[0],
|
|
63
|
+
to = _astNode$position[1];
|
|
64
|
+
var documentFrom = (0, _getDocumentPosition.default)(transaction.doc, from);
|
|
65
|
+
if (!isRichInlineNode(transaction.doc, documentFrom)) {
|
|
66
|
+
var documentTo = (0, _getDocumentPosition.default)(transaction.doc, to);
|
|
67
|
+
var node = getRichInlineNode(fieldName, value, astNode.text);
|
|
68
|
+
transaction.replaceWith(documentFrom, documentTo, node);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
};
|
|
63
73
|
var getRichInlineNode = function getRichInlineNode(fieldName, value, text) {
|
|
64
74
|
switch (value.type) {
|
|
65
75
|
case 'user':
|
|
@@ -90,6 +100,13 @@ var getRichInlineNode = function getRichInlineNode(fieldName, value, text) {
|
|
|
90
100
|
fieldName: fieldName
|
|
91
101
|
}), _textContent3);
|
|
92
102
|
}
|
|
103
|
+
case 'lozengeWithAvatar':
|
|
104
|
+
{
|
|
105
|
+
var _textContent4 = _schema.JQLEditorSchema.text(text);
|
|
106
|
+
return _schema.JQLEditorSchema.nodes.lozengeWithAvatar.create(_objectSpread(_objectSpread({}, value), {}, {
|
|
107
|
+
fieldName: fieldName
|
|
108
|
+
}), _textContent4);
|
|
109
|
+
}
|
|
93
110
|
default:
|
|
94
111
|
{
|
|
95
112
|
throw new Error("Unsupported hydrated value type ".concat(value.type));
|
|
@@ -112,6 +129,12 @@ var getMembersOfArgumentNodes = function getMembersOfArgumentNodes(ast, teamId)
|
|
|
112
129
|
}
|
|
113
130
|
return ast.query.accept(new FindMembersOfArgumentsVisitor(teamId));
|
|
114
131
|
};
|
|
132
|
+
var getFunctionArgumentNodes = function getFunctionArgumentNodes(ast, fieldName, valueId) {
|
|
133
|
+
if (!ast.query) {
|
|
134
|
+
return [];
|
|
135
|
+
}
|
|
136
|
+
return ast.query.accept(new FindFunctionArgumentsVisitor(fieldName, valueId));
|
|
137
|
+
};
|
|
115
138
|
|
|
116
139
|
/**
|
|
117
140
|
* Base visitor class for traversing JQL AST to find specific nodes.
|
|
@@ -198,21 +221,51 @@ var FindValuesVisitor = /*#__PURE__*/function (_BaseAstNodeFinder2) {
|
|
|
198
221
|
return (0, _createClass2.default)(FindValuesVisitor);
|
|
199
222
|
}(BaseAstNodeFinder);
|
|
200
223
|
/**
|
|
201
|
-
* Visitor that finds
|
|
202
|
-
*
|
|
224
|
+
* Visitor that finds function arguments for a specific field matching a target value id.
|
|
225
|
+
* This visitor is field-aware: it processes clauses for the given field and matches arguments by their raw value.
|
|
203
226
|
*/
|
|
204
|
-
var
|
|
205
|
-
function
|
|
227
|
+
var FindFunctionArgumentsVisitor = /*#__PURE__*/function (_BaseAstNodeFinder3) {
|
|
228
|
+
function FindFunctionArgumentsVisitor(fieldName, targetValueId) {
|
|
206
229
|
var _this3;
|
|
207
|
-
(0, _classCallCheck2.default)(this,
|
|
208
|
-
_this3 = _callSuper(this,
|
|
230
|
+
(0, _classCallCheck2.default)(this, FindFunctionArgumentsVisitor);
|
|
231
|
+
_this3 = _callSuper(this, FindFunctionArgumentsVisitor);
|
|
209
232
|
(0, _defineProperty2.default)(_this3, "visitTerminalClause", function (terminalClause) {
|
|
233
|
+
if (!_this3.equalsIgnoreCase(terminalClause.field.value, _this3.fieldName)) {
|
|
234
|
+
return [];
|
|
235
|
+
}
|
|
210
236
|
if (terminalClause.operand === undefined) {
|
|
211
237
|
return [];
|
|
212
238
|
}
|
|
213
239
|
return terminalClause.operand.accept(_this3);
|
|
214
240
|
});
|
|
215
241
|
(0, _defineProperty2.default)(_this3, "visitFunctionOperand", function (functionOperand) {
|
|
242
|
+
return functionOperand.arguments.filter(function (arg) {
|
|
243
|
+
return _this3.equalsIgnoreCase(arg.value.trim(), _this3.targetValueId);
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
_this3.fieldName = fieldName;
|
|
247
|
+
_this3.targetValueId = targetValueId.trim();
|
|
248
|
+
return _this3;
|
|
249
|
+
}
|
|
250
|
+
(0, _inherits2.default)(FindFunctionArgumentsVisitor, _BaseAstNodeFinder3);
|
|
251
|
+
return (0, _createClass2.default)(FindFunctionArgumentsVisitor);
|
|
252
|
+
}(BaseAstNodeFinder);
|
|
253
|
+
/**
|
|
254
|
+
* Visitor that finds membersOf function arguments matching a specific team ID.
|
|
255
|
+
* Used for queries like "assignee in membersOf("id: <uuid>")".
|
|
256
|
+
*/
|
|
257
|
+
var FindMembersOfArgumentsVisitor = /*#__PURE__*/function (_BaseAstNodeFinder4) {
|
|
258
|
+
function FindMembersOfArgumentsVisitor(teamId) {
|
|
259
|
+
var _this4;
|
|
260
|
+
(0, _classCallCheck2.default)(this, FindMembersOfArgumentsVisitor);
|
|
261
|
+
_this4 = _callSuper(this, FindMembersOfArgumentsVisitor);
|
|
262
|
+
(0, _defineProperty2.default)(_this4, "visitTerminalClause", function (terminalClause) {
|
|
263
|
+
if (terminalClause.operand === undefined) {
|
|
264
|
+
return [];
|
|
265
|
+
}
|
|
266
|
+
return terminalClause.operand.accept(_this4);
|
|
267
|
+
});
|
|
268
|
+
(0, _defineProperty2.default)(_this4, "visitFunctionOperand", function (functionOperand) {
|
|
216
269
|
var functionName = functionOperand.function.value.toLowerCase();
|
|
217
270
|
|
|
218
271
|
// Only process membersOf function
|
|
@@ -223,18 +276,18 @@ var FindMembersOfArgumentsVisitor = /*#__PURE__*/function (_BaseAstNodeFinder3)
|
|
|
223
276
|
functionOperand.arguments.forEach(function (arg) {
|
|
224
277
|
// Normalize both values by removing extra whitespace for comparison
|
|
225
278
|
// This handles both "id: uuid" and "id:uuid" formats
|
|
226
|
-
var normalizedArgValue =
|
|
227
|
-
var normalizedTeamId =
|
|
228
|
-
if (
|
|
279
|
+
var normalizedArgValue = _this4.normalizeValue(arg.value);
|
|
280
|
+
var normalizedTeamId = _this4.normalizeValue(_this4.teamId);
|
|
281
|
+
if (_this4.equalsIgnoreCase(normalizedArgValue, normalizedTeamId)) {
|
|
229
282
|
matchingArgs.push(arg);
|
|
230
283
|
}
|
|
231
284
|
});
|
|
232
285
|
return matchingArgs;
|
|
233
286
|
});
|
|
234
|
-
|
|
235
|
-
return
|
|
287
|
+
_this4.teamId = teamId;
|
|
288
|
+
return _this4;
|
|
236
289
|
}
|
|
237
|
-
(0, _inherits2.default)(FindMembersOfArgumentsVisitor,
|
|
290
|
+
(0, _inherits2.default)(FindMembersOfArgumentsVisitor, _BaseAstNodeFinder4);
|
|
238
291
|
return (0, _createClass2.default)(FindMembersOfArgumentsVisitor, [{
|
|
239
292
|
key: "normalizeValue",
|
|
240
293
|
value:
|
|
@@ -85,16 +85,16 @@ var ValidQueryVisitor = exports.ValidQueryVisitor = /*#__PURE__*/function (_Abst
|
|
|
85
85
|
}).join(', '), ")");
|
|
86
86
|
});
|
|
87
87
|
(0, _defineProperty2.default)(_this, "visitFunctionOperand", function (functionOperand) {
|
|
88
|
-
// Only include membersOf function as it has arguments that need hydration
|
|
89
|
-
// Other functions like currentUser() don't have hydratable arguments
|
|
90
88
|
var functionName = functionOperand.function.value.toLowerCase();
|
|
91
|
-
if (functionName !== 'membersof' || !(0, _platformFeatureFlags.fg)('jira-membersof-team-support')) {
|
|
92
|
-
return '';
|
|
93
|
-
}
|
|
94
89
|
var args = functionOperand.arguments.map(function (arg) {
|
|
95
90
|
return arg.text;
|
|
96
91
|
}).join(', ');
|
|
97
|
-
|
|
92
|
+
|
|
93
|
+
// The generic gate supersedes the legacy membersOf-specific gate: when
|
|
94
|
+
// jql-function-arg-hydration is on, any function with arguments is included (covering
|
|
95
|
+
// membersOf and all others). Otherwise fall back to the legacy membersOf-only path.
|
|
96
|
+
var shouldIncludeFunction = (0, _platformFeatureFlags.fg)('jql-function-arg-hydration') ? functionOperand.arguments.length > 0 : functionName === 'membersof' && (0, _platformFeatureFlags.fg)('jira-membersof-team-support');
|
|
97
|
+
return shouldIncludeFunction ? "".concat(functionOperand.function.text, "(").concat(args, ")") : '';
|
|
98
98
|
});
|
|
99
99
|
return _this;
|
|
100
100
|
}
|
package/dist/cjs/state/index.js
CHANGED
|
@@ -4,7 +4,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
|
|
|
4
4
|
Object.defineProperty(exports, "__esModule", {
|
|
5
5
|
value: true
|
|
6
6
|
});
|
|
7
|
-
exports.useStoreActions = exports.useScopedId = exports.useRichInlineNodesEnabled = exports.useOnSyntaxHelp = exports.useLineNumbersVisible = exports.useJqlError = exports.useIsSearching = exports.useIntl = exports.useIdPrefix = exports.useHydratedValue = exports.useHydratedUser = exports.useHydratedTeam = exports.useHydratedProject = exports.useHydratedGoal = exports.useHydratedDeprecations = exports.useExternalMessages = exports.useEditorViewHasFocus = exports.useEditorView = exports.useEditorStateHasJqlError = exports.useEditorState = exports.useCustomErrorComponent = exports.useAutocompleteProvider = exports.useAutocompletePosition = exports.useAutocompleteOptions = exports.useAutocompleteLoading = exports.useAutocompleteIsOpen = exports.useAutocomplete = exports.initialState = exports.actions = exports.EditorStateContainer = void 0;
|
|
7
|
+
exports.useStoreActions = exports.useScopedId = exports.useRichInlineNodesEnabled = exports.useOnSyntaxHelp = exports.useLineNumbersVisible = exports.useJqlError = exports.useIsSearching = exports.useIntl = exports.useIdPrefix = exports.useHydratedValue = exports.useHydratedUser = exports.useHydratedTeam = exports.useHydratedProject = exports.useHydratedLozengeWithAvatar = exports.useHydratedGoal = exports.useHydratedDeprecations = exports.useExternalMessages = exports.useEditorViewHasFocus = exports.useEditorView = exports.useEditorStateHasJqlError = exports.useEditorState = exports.useCustomErrorComponent = exports.useAutocompleteProvider = exports.useAutocompletePosition = exports.useAutocompleteOptions = exports.useAutocompleteLoading = exports.useAutocompleteIsOpen = exports.useAutocomplete = exports.initialState = exports.actions = exports.EditorStateContainer = void 0;
|
|
8
8
|
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
9
9
|
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
|
|
10
10
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
@@ -19,6 +19,7 @@ var _view = require("@atlaskit/editor-prosemirror/view");
|
|
|
19
19
|
var _featureGateJsClient = _interopRequireDefault(require("@atlaskit/feature-gate-js-client"));
|
|
20
20
|
var _jqlAst = require("@atlaskit/jql-ast");
|
|
21
21
|
var _jqlAutocomplete = require("@atlaskit/jql-autocomplete");
|
|
22
|
+
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
22
23
|
var _analytics = require("../analytics");
|
|
23
24
|
var _selectErrorCommand = require("../commands/select-error-command");
|
|
24
25
|
var _constants = require("../common/constants");
|
|
@@ -336,10 +337,26 @@ var actions = exports.actions = {
|
|
|
336
337
|
onFields = _getState$autocomplet.onFields,
|
|
337
338
|
onOperators = _getState$autocomplet.onOperators,
|
|
338
339
|
onValues = _getState$autocomplet.onValues,
|
|
339
|
-
onFunctions = _getState$autocomplet.onFunctions
|
|
340
|
+
onFunctions = _getState$autocomplet.onFunctions,
|
|
341
|
+
onFunctionArguments = _getState$autocomplet.onFunctionArguments;
|
|
340
342
|
var optionTypes = [];
|
|
341
343
|
var observables = [];
|
|
342
|
-
if (rules.
|
|
344
|
+
if (rules.functionArgument && onFunctionArguments && (0, _platformFeatureFlags.fg)('enable-jql-membersof-autocomplete')) {
|
|
345
|
+
var _context$field, _context$functionName;
|
|
346
|
+
// When the caret is inside a function argument (e.g. membersOf("...")), we call
|
|
347
|
+
// onFunctionArguments unconditionally in preference to the generic value/function
|
|
348
|
+
// providers. rules.functionArgument can co-exist with rules.function so we must check it
|
|
349
|
+
// first — before the outer rules.value/rules.function block — to avoid it being
|
|
350
|
+
// swallowed by the else-if chain.
|
|
351
|
+
var _rules$functionArgume = rules.functionArgument,
|
|
352
|
+
matchedText = _rules$functionArgume.matchedText,
|
|
353
|
+
context = _rules$functionArgume.context;
|
|
354
|
+
var fieldName = (_context$field = context === null || context === void 0 ? void 0 : context.field) !== null && _context$field !== void 0 ? _context$field : '';
|
|
355
|
+
var functionName = (_context$functionName = context === null || context === void 0 ? void 0 : context.functionName) !== null && _context$functionName !== void 0 ? _context$functionName : '';
|
|
356
|
+
var functionArguments$ = onFunctionArguments(fieldName, matchedText, functionName);
|
|
357
|
+
optionTypes.push('values');
|
|
358
|
+
observables.push(dispatch(actions.appendOptionsForObservable('values', functionArguments$, rules.functionArgument, 'value')));
|
|
359
|
+
} else if (rules.value || rules.function ||
|
|
343
360
|
// If EMPTY is suggested as a token, we are also in "operand mode" and we don't want to call other providers
|
|
344
361
|
// e.g. "assignee is " will return "EMPTY" token and "operator" rule as suggestions (because of "is not")
|
|
345
362
|
tokens.values.includes('EMPTY')) {
|
|
@@ -357,16 +374,16 @@ var actions = exports.actions = {
|
|
|
357
374
|
}
|
|
358
375
|
} else if (rules.operator) {
|
|
359
376
|
var _rules$operator = rules.operator,
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
var operators$ = (0, _autocomplete.sortOperators)(onOperators(
|
|
377
|
+
_context = _rules$operator.context,
|
|
378
|
+
_matchedText = _rules$operator.matchedText;
|
|
379
|
+
var operators$ = (0, _autocomplete.sortOperators)(onOperators(_matchedText, _context === null || _context === void 0 ? void 0 : _context.field));
|
|
363
380
|
optionTypes.push('operators');
|
|
364
381
|
observables.push(dispatch(actions.appendOptionsForObservable('operators', operators$, rules.operator, 'operator')));
|
|
365
382
|
} else if (rules.field) {
|
|
366
383
|
var _rules$field = rules.field,
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
var fields$ = onFields(
|
|
384
|
+
_context2 = _rules$field.context,
|
|
385
|
+
_matchedText2 = _rules$field.matchedText;
|
|
386
|
+
var fields$ = onFields(_matchedText2, _context2 === null || _context2 === void 0 ? void 0 : _context2.clause);
|
|
370
387
|
optionTypes.push('fields');
|
|
371
388
|
observables.push(dispatch(actions.appendOptionsForObservable('fields', fields$, rules.field, 'field')));
|
|
372
389
|
}
|
|
@@ -950,16 +967,25 @@ var useHydratedGoal = exports.useHydratedGoal = (0, _reactSweetState.createHook)
|
|
|
950
967
|
return goal && goal.type === 'goal' ? goal : undefined;
|
|
951
968
|
}
|
|
952
969
|
});
|
|
970
|
+
var useHydratedLozengeWithAvatar = exports.useHydratedLozengeWithAvatar = (0, _reactSweetState.createHook)(Store, {
|
|
971
|
+
selector: function selector(state, _ref43) {
|
|
972
|
+
var _state$hydratedValues0;
|
|
973
|
+
var id = _ref43.id,
|
|
974
|
+
fieldName = _ref43.fieldName;
|
|
975
|
+
var value = (_state$hydratedValues0 = state.hydratedValues[(0, _util.normaliseHydrationKey)(fieldName)]) === null || _state$hydratedValues0 === void 0 ? void 0 : _state$hydratedValues0.get((0, _jqlAst.normaliseJqlString)(id));
|
|
976
|
+
return value && value.type === 'lozengeWithAvatar' ? value : undefined;
|
|
977
|
+
}
|
|
978
|
+
});
|
|
953
979
|
var useHydratedDeprecations = exports.useHydratedDeprecations = (0, _reactSweetState.createHook)(Store, {
|
|
954
980
|
selector: function selector(state) {
|
|
955
981
|
var ast = (0, _jqlAst2.getJastFromState)(state.editorState);
|
|
956
982
|
var fieldsInQuery = (0, _util2.getFieldNodes)(ast);
|
|
957
983
|
var toReturn = [];
|
|
958
|
-
Object.entries(state.hydratedValues).forEach(function (
|
|
959
|
-
var _state$
|
|
960
|
-
var
|
|
961
|
-
fieldName =
|
|
962
|
-
(_state$
|
|
984
|
+
Object.entries(state.hydratedValues).forEach(function (_ref44) {
|
|
985
|
+
var _state$hydratedValues1;
|
|
986
|
+
var _ref45 = (0, _slicedToArray2.default)(_ref44, 1),
|
|
987
|
+
fieldName = _ref45[0];
|
|
988
|
+
(_state$hydratedValues1 = state.hydratedValues[fieldName]) === null || _state$hydratedValues1 === void 0 || _state$hydratedValues1.forEach(function (value) {
|
|
963
989
|
if (value.type === 'deprecated-field') {
|
|
964
990
|
if (fieldsInQuery.has(value.id.toLowerCase())) {
|
|
965
991
|
toReturn.push(value);
|
|
@@ -982,19 +1008,19 @@ var useOnSyntaxHelp = exports.useOnSyntaxHelp = (0, _reactSweetState.createHook)
|
|
|
982
1008
|
});
|
|
983
1009
|
var EditorStateContainer = exports.EditorStateContainer = (0, _reactSweetState.createContainer)(Store, {
|
|
984
1010
|
onInit: function onInit() {
|
|
985
|
-
return function (
|
|
986
|
-
var getState =
|
|
987
|
-
setState =
|
|
988
|
-
dispatch =
|
|
989
|
-
var intlRef =
|
|
990
|
-
query =
|
|
991
|
-
isSearching =
|
|
992
|
-
autocompleteProvider =
|
|
993
|
-
externalMessages =
|
|
994
|
-
enableRichInlineNodes =
|
|
995
|
-
onDebugUnsafeMessage =
|
|
996
|
-
onSyntaxHelp =
|
|
997
|
-
customComponents =
|
|
1011
|
+
return function (_ref46, _ref47) {
|
|
1012
|
+
var getState = _ref46.getState,
|
|
1013
|
+
setState = _ref46.setState,
|
|
1014
|
+
dispatch = _ref46.dispatch;
|
|
1015
|
+
var intlRef = _ref47.intlRef,
|
|
1016
|
+
query = _ref47.query,
|
|
1017
|
+
isSearching = _ref47.isSearching,
|
|
1018
|
+
autocompleteProvider = _ref47.autocompleteProvider,
|
|
1019
|
+
externalMessages = _ref47.externalMessages,
|
|
1020
|
+
enableRichInlineNodes = _ref47.enableRichInlineNodes,
|
|
1021
|
+
onDebugUnsafeMessage = _ref47.onDebugUnsafeMessage,
|
|
1022
|
+
onSyntaxHelp = _ref47.onSyntaxHelp,
|
|
1023
|
+
customComponents = _ref47.customComponents;
|
|
998
1024
|
setState({
|
|
999
1025
|
controlledQuery: query,
|
|
1000
1026
|
query: query,
|
|
@@ -1023,18 +1049,18 @@ var EditorStateContainer = exports.EditorStateContainer = (0, _reactSweetState.c
|
|
|
1023
1049
|
};
|
|
1024
1050
|
},
|
|
1025
1051
|
onUpdate: function onUpdate() {
|
|
1026
|
-
return function (
|
|
1027
|
-
var getState =
|
|
1028
|
-
setState =
|
|
1029
|
-
dispatch =
|
|
1030
|
-
var controlledQueryProp =
|
|
1031
|
-
isSearching =
|
|
1032
|
-
autocompleteProvider =
|
|
1033
|
-
externalMessages =
|
|
1034
|
-
enableRichInlineNodes =
|
|
1035
|
-
onDebugUnsafeMessage =
|
|
1036
|
-
onSyntaxHelp =
|
|
1037
|
-
customComponents =
|
|
1052
|
+
return function (_ref48, _ref49) {
|
|
1053
|
+
var getState = _ref48.getState,
|
|
1054
|
+
setState = _ref48.setState,
|
|
1055
|
+
dispatch = _ref48.dispatch;
|
|
1056
|
+
var controlledQueryProp = _ref49.query,
|
|
1057
|
+
isSearching = _ref49.isSearching,
|
|
1058
|
+
autocompleteProvider = _ref49.autocompleteProvider,
|
|
1059
|
+
externalMessages = _ref49.externalMessages,
|
|
1060
|
+
enableRichInlineNodes = _ref49.enableRichInlineNodes,
|
|
1061
|
+
onDebugUnsafeMessage = _ref49.onDebugUnsafeMessage,
|
|
1062
|
+
onSyntaxHelp = _ref49.onSyntaxHelp,
|
|
1063
|
+
customComponents = _ref49.customComponents;
|
|
1038
1064
|
var _getState12 = getState(),
|
|
1039
1065
|
controlledQuery = _getState12.controlledQuery,
|
|
1040
1066
|
query = _getState12.query;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { ANALYTICS_CHANNEL, useJqlPackageAnalytics } from '@atlaskit/jql-editor-common';
|
|
2
2
|
export const useJqlEditorAnalytics = analyticsSource => {
|
|
3
|
-
return useJqlPackageAnalytics(analyticsSource, "@atlaskit/jql-editor", "6.
|
|
3
|
+
return useJqlPackageAnalytics(analyticsSource, "@atlaskit/jql-editor", "6.5.0", ANALYTICS_CHANNEL);
|
|
4
4
|
};
|
|
@@ -16,6 +16,7 @@ export const defaultAutocompleteProvider = {
|
|
|
16
16
|
onFields: () => empty(),
|
|
17
17
|
onOperators: () => empty(),
|
|
18
18
|
onValues: () => empty(),
|
|
19
|
-
onFunctions: () => empty()
|
|
19
|
+
onFunctions: () => empty(),
|
|
20
|
+
onFunctionArguments: () => empty()
|
|
20
21
|
};
|
|
21
22
|
export const JQLAutocompletePluginKey = new PluginKey(AUTOCOMPLETE_PLUGIN_NAME);
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { goal } from './goal';
|
|
2
|
+
import { lozengeWithAvatar } from './lozenge-with-avatar';
|
|
2
3
|
import { project } from './project';
|
|
3
4
|
import { team } from './team';
|
|
4
5
|
import { user } from './user';
|
|
5
6
|
export const richInlineNodes = {
|
|
7
|
+
lozengeWithAvatar,
|
|
6
8
|
user,
|
|
7
9
|
team,
|
|
8
10
|
project,
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Avatar from '@atlaskit/avatar';
|
|
3
|
+
import { useHydratedLozengeWithAvatar } from '../../../../state';
|
|
4
|
+
import { AvatarWrapper, LozengeWithAvatarContainer, NameContainer } from './styled';
|
|
5
|
+
const LozengeWithAvatar = ({
|
|
6
|
+
id,
|
|
7
|
+
name,
|
|
8
|
+
fieldName,
|
|
9
|
+
selected,
|
|
10
|
+
error
|
|
11
|
+
}) => {
|
|
12
|
+
const [lozengeWithAvatar] = useHydratedLozengeWithAvatar({
|
|
13
|
+
id,
|
|
14
|
+
fieldName
|
|
15
|
+
});
|
|
16
|
+
return /*#__PURE__*/React.createElement(LozengeWithAvatarContainer, {
|
|
17
|
+
selected: selected,
|
|
18
|
+
error: error
|
|
19
|
+
}, /*#__PURE__*/React.createElement(AvatarWrapper, null, /*#__PURE__*/React.createElement(Avatar, {
|
|
20
|
+
src: lozengeWithAvatar === null || lozengeWithAvatar === void 0 ? void 0 : lozengeWithAvatar.avatarUrl,
|
|
21
|
+
borderColor: "transparent",
|
|
22
|
+
size: "xsmall"
|
|
23
|
+
})), /*#__PURE__*/React.createElement(NameContainer, null, name));
|
|
24
|
+
};
|
|
25
|
+
export const lozengeWithAvatar = {
|
|
26
|
+
component: LozengeWithAvatar,
|
|
27
|
+
attrs: {
|
|
28
|
+
id: {},
|
|
29
|
+
name: {},
|
|
30
|
+
fieldName: {}
|
|
31
|
+
}
|
|
32
|
+
};
|