funda-ui 1.0.400 → 1.0.405

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.
@@ -67,6 +67,9 @@ module.exports = {
67
67
  /***/ ((module) => {
68
68
 
69
69
  function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
70
+ var _excluded = ["children"];
71
+ function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
72
+ function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
70
73
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
71
74
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
72
75
  function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
@@ -135,28 +138,44 @@ function flatTree(arr) {
135
138
  }
136
139
 
137
140
  /**
138
- * Add depth to each item in the tree
139
- * @param {Array} arr - Hierarchical array
140
- * @param {?String | ?Number} parentId - Parent id
141
- * @param {?String} keyId - Key value of id.
142
- * @param {?String} keyParentId - Key value of parent id.
143
- * @param {?Number} depth - Depth of the item.
144
- * @returns
141
+ * Get all depth
142
+ * @param {Object} node
143
+ * @returns Number
145
144
  */
145
+ function getAllDepth(arr) {
146
+ var count = function count(children) {
147
+ return children.reduce(function (depth, child) {
148
+ return Math.max(depth, 1 + count(child.children)); // increment depth of children by 1, and compare it with accumulated depth of other children within the same element
149
+ }, 0); //default value 0 that's returned if there are no children
150
+ };
151
+
152
+ return count(arr);
153
+ }
154
+
155
+ /**
156
+ * Add depth to each item in the tree
157
+ * @param {Array} arr - Hierarchical array
158
+ * @param {?String} keyId - Key value of id.
159
+ * @param {?String} keyParentId - Key value of parent id.
160
+ * @param {?Number} depth - Depth of the item.
161
+ * @returns Number
162
+ */
146
163
  function addTreeDepth(arr) {
147
- var parentId = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
148
- var keyId = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'id';
149
- var keyParentId = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'parent_id';
150
- var depth = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0;
151
- arr.forEach(function (item) {
152
- item.depth = depth;
153
- // Query all child nodes by parent node ID
154
- if (item.children && item.children.length > 0) {
155
- addTreeDepth(item.children, item[keyId], keyId, keyParentId, ++depth);
156
- } else {
157
- depth = 0;
164
+ var keyId = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'id';
165
+ var parentItem = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
166
+ var depth = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
167
+ return arr.reduce(function (acc, el) {
168
+ var children = el.children,
169
+ otherProps = _objectWithoutProperties(el, _excluded);
170
+ acc.push(_objectSpread(_objectSpread({}, otherProps), {}, {
171
+ parentItem: parentItem,
172
+ depth: depth
173
+ }));
174
+ if (children) {
175
+ return acc.concat(addTreeDepth(children, keyId, el[keyId], depth + 1));
158
176
  }
159
- });
177
+ return acc;
178
+ }, []);
160
179
  }
161
180
 
162
181
  /**
@@ -173,6 +192,7 @@ function addTreeIndent(arr) {
173
192
  var keyName = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'label';
174
193
  arr.forEach(function (item) {
175
194
  var indent = '';
195
+ console.log(item.depth);
176
196
  if (item.depth) {
177
197
  Array(item.depth).fill(0).forEach(function (k, i) {
178
198
  indent += placeholder;
@@ -184,6 +204,7 @@ function addTreeIndent(arr) {
184
204
  });
185
205
  }
186
206
  module.exports = {
207
+ getAllDepth: getAllDepth,
187
208
  convertTree: convertTree,
188
209
  flatTree: flatTree,
189
210
  addTreeDepth: addTreeDepth,
@@ -739,8 +760,7 @@ var MultiFuncSelect = /*#__PURE__*/(0,external_root_React_commonjs2_react_common
739
760
  // STEP 2: ===========
740
761
  // Set hierarchical categories ( with sub-categories )
741
762
  if (hierarchical) {
742
- (0,tree.addTreeDepth)(_ORGIN_DATA);
743
- _ORGIN_DATA = (0,tree.flatTree)(_ORGIN_DATA);
763
+ _ORGIN_DATA = (0,tree.addTreeDepth)(_ORGIN_DATA);
744
764
  (0,tree.addTreeIndent)(_ORGIN_DATA, INDENT_PLACEHOLDER, INDENT_LAST_PLACEHOLDER, 'label');
745
765
  }
746
766
 
@@ -855,8 +875,7 @@ var MultiFuncSelect = /*#__PURE__*/(0,external_root_React_commonjs2_react_common
855
875
  // STEP 2: ===========
856
876
  // Set hierarchical categories ( with sub-categories )
857
877
  if (hierarchical) {
858
- (0,tree.addTreeDepth)(optionsDataInit);
859
- optionsDataInit = (0,tree.flatTree)(optionsDataInit);
878
+ optionsDataInit = (0,tree.addTreeDepth)(optionsDataInit);
860
879
  (0,tree.addTreeIndent)(optionsDataInit, INDENT_PLACEHOLDER, INDENT_LAST_PLACEHOLDER, 'label');
861
880
  }
862
881
 
package/Toast/index.css CHANGED
@@ -69,6 +69,34 @@
69
69
  .toasts__wrapper .toast-container {
70
70
  margin-bottom: var(--toasts-container-gap);
71
71
  }
72
+ .toasts__wrapper .toast-container.auto-anim-switch--initfirst {
73
+ transform: translateY(-20);
74
+ opacity: 0;
75
+ }
76
+ .toasts__wrapper .toast-container.auto-anim-switch--first {
77
+ animation-name: toast-anim-show-first;
78
+ animation-duration: 0.3s;
79
+ animation-timing-function: linear;
80
+ animation-delay: 0;
81
+ animation-iteration-count: 1;
82
+ animation-fill-mode: forwards;
83
+ }
84
+ .toasts__wrapper .toast-container.auto-anim-switch:not(.only-one) {
85
+ animation-name: toast-anim-hide;
86
+ animation-duration: 0.3s;
87
+ animation-timing-function: linear;
88
+ animation-delay: 0;
89
+ animation-iteration-count: 1;
90
+ animation-fill-mode: forwards;
91
+ }
92
+ .toasts__wrapper .toast-container.only-one.auto-anim-switch {
93
+ animation-name: toast-anim-hide--onlyone;
94
+ animation-duration: 0.3s;
95
+ animation-timing-function: linear;
96
+ animation-delay: 0;
97
+ animation-iteration-count: 1;
98
+ animation-fill-mode: forwards;
99
+ }
72
100
  .toasts__wrapper.toasts__wrapper--cascading .toast-container {
73
101
  margin-bottom: 0;
74
102
  }
@@ -105,3 +133,50 @@
105
133
  .toast-container.hide-end {
106
134
  display: none;
107
135
  }
136
+
137
+ @keyframes toast-anim-initfirst {
138
+ 0% {
139
+ transform: translateY(-20px);
140
+ opacity: 1;
141
+ }
142
+ 100% {
143
+ transform: translateY(20px);
144
+ opacity: 0;
145
+ }
146
+ }
147
+ @keyframes toast-anim-show-first {
148
+ 0% {
149
+ transform: translateY(-20px);
150
+ opacity: 0;
151
+ }
152
+ 100% {
153
+ transform: translateY(0);
154
+ opacity: 1;
155
+ }
156
+ }
157
+ @keyframes toast-anim-hide {
158
+ 0% {
159
+ transform: translateY(-100%);
160
+ opacity: 1;
161
+ }
162
+ 100% {
163
+ transform: translateY(10px);
164
+ opacity: 0;
165
+ /* prevent auto-close's item */
166
+ display: block;
167
+ }
168
+ }
169
+ @keyframes toast-anim-hide--onlyone {
170
+ 0% {
171
+ transform: translateY(-100%);
172
+ opacity: 1;
173
+ /* prevent auto-close's item */
174
+ display: block;
175
+ }
176
+ 100% {
177
+ transform: translateY(10px);
178
+ opacity: 0;
179
+ /* prevent auto-close's item */
180
+ display: block;
181
+ }
182
+ }
package/Toast/index.d.ts CHANGED
@@ -7,8 +7,8 @@ declare global {
7
7
  declare type ToastProps = {
8
8
  /** Specify data of toasts as a JSON string format. */
9
9
  data: any[any];
10
- /** Use asynchronous triggering */
11
- async?: boolean;
10
+ /** Automatically hide multiple items */
11
+ autoHideMultiple?: boolean;
12
12
  /** The direction of the toast. */
13
13
  direction?: string;
14
14
  /** Set an automatic closing time, multiple items will be accumulated in order.
@@ -29,6 +29,8 @@ declare type ToastProps = {
29
29
  closeBtnColor?: string;
30
30
  /** Disable the close button. */
31
31
  closeDisabled?: boolean;
32
+ /** */
33
+ async?: boolean;
32
34
  /** -- */
33
35
  id?: string;
34
36
  onClose?: (e: HTMLDivElement, currentIndex: number, data: HTMLDivElement[]) => void;
package/Toast/index.js CHANGED
@@ -105,7 +105,8 @@ var external_root_React_commonjs2_react_commonjs_react_amd_react_default = /*#__
105
105
 
106
106
  ;
107
107
  var Item = function Item(props) {
108
- var index = props.index,
108
+ var onlyOne = props.onlyOne,
109
+ index = props.index,
109
110
  title = props.title,
110
111
  note = props.note,
111
112
  message = props.message,
@@ -117,7 +118,7 @@ var Item = function Item(props) {
117
118
  closeBtnColor = props.closeBtnColor,
118
119
  closeDisabled = props.closeDisabled;
119
120
  return /*#__PURE__*/external_root_React_commonjs2_react_commonjs_react_amd_react_default().createElement((external_root_React_commonjs2_react_commonjs_react_amd_react_default()).Fragment, null, /*#__PURE__*/external_root_React_commonjs2_react_commonjs_react_amd_react_default().createElement("div", {
120
- className: "toast-container",
121
+ className: "toast-container ".concat(onlyOne ? 'only-one' : ''),
121
122
  "data-index": index,
122
123
  style: cascading ? {
123
124
  transform: "perspective(100px) translateZ(-".concat(2 * index, "px) translateY(").concat(35 * index, "px)"),
@@ -172,6 +173,7 @@ var Item = function Item(props) {
172
173
 
173
174
  var Toast = function Toast(props) {
174
175
  var async = props.async,
176
+ autoHideMultiple = props.autoHideMultiple,
175
177
  direction = props.direction,
176
178
  autoCloseTime = props.autoCloseTime,
177
179
  autoCloseReverse = props.autoCloseReverse,
@@ -187,7 +189,7 @@ var Toast = function Toast(props) {
187
189
  var uniqueID = (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useId)().replace(/\:/g, "-");
188
190
  var idRes = id || uniqueID;
189
191
  var rootRef = (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useRef)(null);
190
- var depth = data.length + 1;
192
+ var depth = autoHideMultiple ? data.slice(-2).length + 1 : data.length + 1;
191
193
  var cascadingEnabled = typeof cascading === 'undefined' ? true : cascading;
192
194
  function init() {
193
195
  // Move HTML templates to tag end body </body>
@@ -203,6 +205,20 @@ var Toast = function Toast(props) {
203
205
  handleClose(index, currentItem);
204
206
  });
205
207
  });
208
+
209
+ // Automatically hide multiple items
210
+ // It creates a transition animation effect with multiple records and only one displayed.
211
+ if (autoHideMultiple) {
212
+ var _list = [].slice.call(rootRef.current.querySelectorAll('.toast-container'));
213
+ _list.forEach(function (node, i) {
214
+ if (i !== _list.length - 1) {
215
+ node.classList.add('auto-anim-switch');
216
+ } else {
217
+ node.classList.add('auto-anim-switch--initfirst');
218
+ node.classList.add('auto-anim-switch--first');
219
+ }
220
+ });
221
+ }
206
222
  }
207
223
 
208
224
  // Initialize data
@@ -210,8 +226,8 @@ var Toast = function Toast(props) {
210
226
  var $toast = document.querySelector("#".concat(rootRef.current.id));
211
227
  if ($toast !== null) {
212
228
  if ($toast.dataset.async == 'true') {
213
- var _list = [].slice.call(rootRef.current.querySelectorAll('.toast-container'));
214
- _list.forEach(function (node, i) {
229
+ var _list2 = [].slice.call(rootRef.current.querySelectorAll('.toast-container'));
230
+ _list2.forEach(function (node, i) {
215
231
  node.classList.remove('hide-end');
216
232
  // rearrange
217
233
  if (cascadingEnabled) node.style.transform = "perspective(100px) translateZ(-".concat(2 * i, "px) translateY(").concat(35 * i, "px)");
@@ -264,9 +280,11 @@ var Toast = function Toast(props) {
264
280
  window.setCloseToast = setTimeout(function () {
265
281
  var _list = [].slice.call(rootRef.current.querySelectorAll('.toast-container'));
266
282
  if (autoCloseReverse) {
267
- _list[items.length - index].classList.add('hide-start');
283
+ var _list3;
284
+ (_list3 = _list[items.length - index]) === null || _list3 === void 0 ? void 0 : _list3.classList.add('hide-start');
268
285
  } else {
269
- _list[index - 1].classList.add('hide-start');
286
+ var _list4;
287
+ (_list4 = _list[index - 1]) === null || _list4 === void 0 ? void 0 : _list4.classList.add('hide-start');
270
288
  }
271
289
 
272
290
  //Let the removed animation show
@@ -284,7 +302,8 @@ var Toast = function Toast(props) {
284
302
  return !node.classList.contains('hide-end');
285
303
  }));
286
304
  } else {
287
- _list[index - 1].classList.add('hide-end');
305
+ var _list5;
306
+ (_list5 = _list[index - 1]) === null || _list5 === void 0 ? void 0 : _list5.classList.add('hide-end');
288
307
  //
289
308
  onClose === null || onClose === void 0 ? void 0 : onClose(rootRef.current, Number(index - 1), _list.filter(function (node) {
290
309
  return !node.classList.contains('hide-end');
@@ -336,8 +355,9 @@ var Toast = function Toast(props) {
336
355
  ref: rootRef
337
356
  }, /*#__PURE__*/external_root_React_commonjs2_react_commonjs_react_amd_react_default().createElement("div", {
338
357
  className: "toasts"
339
- }, data.map(function (item, i) {
358
+ }, (autoHideMultiple ? data.slice(-2) : data).map(function (item, i) {
340
359
  return /*#__PURE__*/external_root_React_commonjs2_react_commonjs_react_amd_react_default().createElement(src_Item, {
360
+ onlyOne: data.length === 1 ? true : false,
341
361
  depth: depth - i,
342
362
  key: i,
343
363
  index: i,
@@ -67,6 +67,9 @@ module.exports = {
67
67
  /***/ ((module) => {
68
68
 
69
69
  function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
70
+ var _excluded = ["children"];
71
+ function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
72
+ function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
70
73
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
71
74
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
72
75
  function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
@@ -135,28 +138,44 @@ function flatTree(arr) {
135
138
  }
136
139
 
137
140
  /**
138
- * Add depth to each item in the tree
139
- * @param {Array} arr - Hierarchical array
140
- * @param {?String | ?Number} parentId - Parent id
141
- * @param {?String} keyId - Key value of id.
142
- * @param {?String} keyParentId - Key value of parent id.
143
- * @param {?Number} depth - Depth of the item.
144
- * @returns
141
+ * Get all depth
142
+ * @param {Object} node
143
+ * @returns Number
145
144
  */
145
+ function getAllDepth(arr) {
146
+ var count = function count(children) {
147
+ return children.reduce(function (depth, child) {
148
+ return Math.max(depth, 1 + count(child.children)); // increment depth of children by 1, and compare it with accumulated depth of other children within the same element
149
+ }, 0); //default value 0 that's returned if there are no children
150
+ };
151
+
152
+ return count(arr);
153
+ }
154
+
155
+ /**
156
+ * Add depth to each item in the tree
157
+ * @param {Array} arr - Hierarchical array
158
+ * @param {?String} keyId - Key value of id.
159
+ * @param {?String} keyParentId - Key value of parent id.
160
+ * @param {?Number} depth - Depth of the item.
161
+ * @returns Number
162
+ */
146
163
  function addTreeDepth(arr) {
147
- var parentId = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
148
- var keyId = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'id';
149
- var keyParentId = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'parent_id';
150
- var depth = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0;
151
- arr.forEach(function (item) {
152
- item.depth = depth;
153
- // Query all child nodes by parent node ID
154
- if (item.children && item.children.length > 0) {
155
- addTreeDepth(item.children, item[keyId], keyId, keyParentId, ++depth);
156
- } else {
157
- depth = 0;
164
+ var keyId = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'id';
165
+ var parentItem = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
166
+ var depth = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
167
+ return arr.reduce(function (acc, el) {
168
+ var children = el.children,
169
+ otherProps = _objectWithoutProperties(el, _excluded);
170
+ acc.push(_objectSpread(_objectSpread({}, otherProps), {}, {
171
+ parentItem: parentItem,
172
+ depth: depth
173
+ }));
174
+ if (children) {
175
+ return acc.concat(addTreeDepth(children, keyId, el[keyId], depth + 1));
158
176
  }
159
- });
177
+ return acc;
178
+ }, []);
160
179
  }
161
180
 
162
181
  /**
@@ -173,6 +192,7 @@ function addTreeIndent(arr) {
173
192
  var keyName = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'label';
174
193
  arr.forEach(function (item) {
175
194
  var indent = '';
195
+ console.log(item.depth);
176
196
  if (item.depth) {
177
197
  Array(item.depth).fill(0).forEach(function (k, i) {
178
198
  indent += placeholder;
@@ -184,6 +204,7 @@ function addTreeIndent(arr) {
184
204
  });
185
205
  }
186
206
  module.exports = {
207
+ getAllDepth: getAllDepth,
187
208
  convertTree: convertTree,
188
209
  flatTree: flatTree,
189
210
  addTreeDepth: addTreeDepth,
@@ -739,8 +760,7 @@ var MultiFuncSelect = /*#__PURE__*/(0,external_root_React_commonjs2_react_common
739
760
  // STEP 2: ===========
740
761
  // Set hierarchical categories ( with sub-categories )
741
762
  if (hierarchical) {
742
- (0,tree.addTreeDepth)(_ORGIN_DATA);
743
- _ORGIN_DATA = (0,tree.flatTree)(_ORGIN_DATA);
763
+ _ORGIN_DATA = (0,tree.addTreeDepth)(_ORGIN_DATA);
744
764
  (0,tree.addTreeIndent)(_ORGIN_DATA, INDENT_PLACEHOLDER, INDENT_LAST_PLACEHOLDER, 'label');
745
765
  }
746
766
 
@@ -855,8 +875,7 @@ var MultiFuncSelect = /*#__PURE__*/(0,external_root_React_commonjs2_react_common
855
875
  // STEP 2: ===========
856
876
  // Set hierarchical categories ( with sub-categories )
857
877
  if (hierarchical) {
858
- (0,tree.addTreeDepth)(optionsDataInit);
859
- optionsDataInit = (0,tree.flatTree)(optionsDataInit);
878
+ optionsDataInit = (0,tree.addTreeDepth)(optionsDataInit);
860
879
  (0,tree.addTreeIndent)(optionsDataInit, INDENT_PLACEHOLDER, INDENT_LAST_PLACEHOLDER, 'label');
861
880
  }
862
881
 
@@ -7,8 +7,8 @@ declare global {
7
7
  declare type ToastProps = {
8
8
  /** Specify data of toasts as a JSON string format. */
9
9
  data: any[any];
10
- /** Use asynchronous triggering */
11
- async?: boolean;
10
+ /** Automatically hide multiple items */
11
+ autoHideMultiple?: boolean;
12
12
  /** The direction of the toast. */
13
13
  direction?: string;
14
14
  /** Set an automatic closing time, multiple items will be accumulated in order.
@@ -29,6 +29,8 @@ declare type ToastProps = {
29
29
  closeBtnColor?: string;
30
30
  /** Disable the close button. */
31
31
  closeDisabled?: boolean;
32
+ /** */
33
+ async?: boolean;
32
34
  /** -- */
33
35
  id?: string;
34
36
  onClose?: (e: HTMLDivElement, currentIndex: number, data: HTMLDivElement[]) => void;
@@ -105,7 +105,8 @@ var external_root_React_commonjs2_react_commonjs_react_amd_react_default = /*#__
105
105
 
106
106
  ;
107
107
  var Item = function Item(props) {
108
- var index = props.index,
108
+ var onlyOne = props.onlyOne,
109
+ index = props.index,
109
110
  title = props.title,
110
111
  note = props.note,
111
112
  message = props.message,
@@ -117,7 +118,7 @@ var Item = function Item(props) {
117
118
  closeBtnColor = props.closeBtnColor,
118
119
  closeDisabled = props.closeDisabled;
119
120
  return /*#__PURE__*/external_root_React_commonjs2_react_commonjs_react_amd_react_default().createElement((external_root_React_commonjs2_react_commonjs_react_amd_react_default()).Fragment, null, /*#__PURE__*/external_root_React_commonjs2_react_commonjs_react_amd_react_default().createElement("div", {
120
- className: "toast-container",
121
+ className: "toast-container ".concat(onlyOne ? 'only-one' : ''),
121
122
  "data-index": index,
122
123
  style: cascading ? {
123
124
  transform: "perspective(100px) translateZ(-".concat(2 * index, "px) translateY(").concat(35 * index, "px)"),
@@ -172,6 +173,7 @@ var Item = function Item(props) {
172
173
 
173
174
  var Toast = function Toast(props) {
174
175
  var async = props.async,
176
+ autoHideMultiple = props.autoHideMultiple,
175
177
  direction = props.direction,
176
178
  autoCloseTime = props.autoCloseTime,
177
179
  autoCloseReverse = props.autoCloseReverse,
@@ -187,7 +189,7 @@ var Toast = function Toast(props) {
187
189
  var uniqueID = (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useId)().replace(/\:/g, "-");
188
190
  var idRes = id || uniqueID;
189
191
  var rootRef = (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useRef)(null);
190
- var depth = data.length + 1;
192
+ var depth = autoHideMultiple ? data.slice(-2).length + 1 : data.length + 1;
191
193
  var cascadingEnabled = typeof cascading === 'undefined' ? true : cascading;
192
194
  function init() {
193
195
  // Move HTML templates to tag end body </body>
@@ -203,6 +205,20 @@ var Toast = function Toast(props) {
203
205
  handleClose(index, currentItem);
204
206
  });
205
207
  });
208
+
209
+ // Automatically hide multiple items
210
+ // It creates a transition animation effect with multiple records and only one displayed.
211
+ if (autoHideMultiple) {
212
+ var _list = [].slice.call(rootRef.current.querySelectorAll('.toast-container'));
213
+ _list.forEach(function (node, i) {
214
+ if (i !== _list.length - 1) {
215
+ node.classList.add('auto-anim-switch');
216
+ } else {
217
+ node.classList.add('auto-anim-switch--initfirst');
218
+ node.classList.add('auto-anim-switch--first');
219
+ }
220
+ });
221
+ }
206
222
  }
207
223
 
208
224
  // Initialize data
@@ -210,8 +226,8 @@ var Toast = function Toast(props) {
210
226
  var $toast = document.querySelector("#".concat(rootRef.current.id));
211
227
  if ($toast !== null) {
212
228
  if ($toast.dataset.async == 'true') {
213
- var _list = [].slice.call(rootRef.current.querySelectorAll('.toast-container'));
214
- _list.forEach(function (node, i) {
229
+ var _list2 = [].slice.call(rootRef.current.querySelectorAll('.toast-container'));
230
+ _list2.forEach(function (node, i) {
215
231
  node.classList.remove('hide-end');
216
232
  // rearrange
217
233
  if (cascadingEnabled) node.style.transform = "perspective(100px) translateZ(-".concat(2 * i, "px) translateY(").concat(35 * i, "px)");
@@ -264,9 +280,11 @@ var Toast = function Toast(props) {
264
280
  window.setCloseToast = setTimeout(function () {
265
281
  var _list = [].slice.call(rootRef.current.querySelectorAll('.toast-container'));
266
282
  if (autoCloseReverse) {
267
- _list[items.length - index].classList.add('hide-start');
283
+ var _list3;
284
+ (_list3 = _list[items.length - index]) === null || _list3 === void 0 ? void 0 : _list3.classList.add('hide-start');
268
285
  } else {
269
- _list[index - 1].classList.add('hide-start');
286
+ var _list4;
287
+ (_list4 = _list[index - 1]) === null || _list4 === void 0 ? void 0 : _list4.classList.add('hide-start');
270
288
  }
271
289
 
272
290
  //Let the removed animation show
@@ -284,7 +302,8 @@ var Toast = function Toast(props) {
284
302
  return !node.classList.contains('hide-end');
285
303
  }));
286
304
  } else {
287
- _list[index - 1].classList.add('hide-end');
305
+ var _list5;
306
+ (_list5 = _list[index - 1]) === null || _list5 === void 0 ? void 0 : _list5.classList.add('hide-end');
288
307
  //
289
308
  onClose === null || onClose === void 0 ? void 0 : onClose(rootRef.current, Number(index - 1), _list.filter(function (node) {
290
309
  return !node.classList.contains('hide-end');
@@ -336,8 +355,9 @@ var Toast = function Toast(props) {
336
355
  ref: rootRef
337
356
  }, /*#__PURE__*/external_root_React_commonjs2_react_commonjs_react_amd_react_default().createElement("div", {
338
357
  className: "toasts"
339
- }, data.map(function (item, i) {
358
+ }, (autoHideMultiple ? data.slice(-2) : data).map(function (item, i) {
340
359
  return /*#__PURE__*/external_root_React_commonjs2_react_commonjs_react_amd_react_default().createElement(src_Item, {
360
+ onlyOne: data.length === 1 ? true : false,
341
361
  depth: depth - i,
342
362
  key: i,
343
363
  index: i,
@@ -69,6 +69,34 @@
69
69
  .toasts__wrapper .toast-container {
70
70
  margin-bottom: var(--toasts-container-gap);
71
71
  }
72
+ .toasts__wrapper .toast-container.auto-anim-switch--initfirst {
73
+ transform: translateY(-20);
74
+ opacity: 0;
75
+ }
76
+ .toasts__wrapper .toast-container.auto-anim-switch--first {
77
+ animation-name: toast-anim-show-first;
78
+ animation-duration: 0.3s;
79
+ animation-timing-function: linear;
80
+ animation-delay: 0;
81
+ animation-iteration-count: 1;
82
+ animation-fill-mode: forwards;
83
+ }
84
+ .toasts__wrapper .toast-container.auto-anim-switch:not(.only-one) {
85
+ animation-name: toast-anim-hide;
86
+ animation-duration: 0.3s;
87
+ animation-timing-function: linear;
88
+ animation-delay: 0;
89
+ animation-iteration-count: 1;
90
+ animation-fill-mode: forwards;
91
+ }
92
+ .toasts__wrapper .toast-container.only-one.auto-anim-switch {
93
+ animation-name: toast-anim-hide--onlyone;
94
+ animation-duration: 0.3s;
95
+ animation-timing-function: linear;
96
+ animation-delay: 0;
97
+ animation-iteration-count: 1;
98
+ animation-fill-mode: forwards;
99
+ }
72
100
  .toasts__wrapper.toasts__wrapper--cascading .toast-container {
73
101
  margin-bottom: 0;
74
102
  }
@@ -105,3 +133,50 @@
105
133
  .toast-container.hide-end {
106
134
  display: none;
107
135
  }
136
+
137
+ @keyframes toast-anim-initfirst {
138
+ 0% {
139
+ transform: translateY(-20px);
140
+ opacity: 1;
141
+ }
142
+ 100% {
143
+ transform: translateY(20px);
144
+ opacity: 0;
145
+ }
146
+ }
147
+ @keyframes toast-anim-show-first {
148
+ 0% {
149
+ transform: translateY(-20px);
150
+ opacity: 0;
151
+ }
152
+ 100% {
153
+ transform: translateY(0);
154
+ opacity: 1;
155
+ }
156
+ }
157
+ @keyframes toast-anim-hide {
158
+ 0% {
159
+ transform: translateY(-100%);
160
+ opacity: 1;
161
+ }
162
+ 100% {
163
+ transform: translateY(10px);
164
+ opacity: 0;
165
+ /* prevent auto-close's item */
166
+ display: block;
167
+ }
168
+ }
169
+ @keyframes toast-anim-hide--onlyone {
170
+ 0% {
171
+ transform: translateY(-100%);
172
+ opacity: 1;
173
+ /* prevent auto-close's item */
174
+ display: block;
175
+ }
176
+ 100% {
177
+ transform: translateY(10px);
178
+ opacity: 0;
179
+ /* prevent auto-close's item */
180
+ display: block;
181
+ }
182
+ }
@@ -5,7 +5,6 @@ import useThrottle from './utils/useThrottle';
5
5
 
6
6
 
7
7
  import {
8
- flatTree,
9
8
  addTreeDepth,
10
9
  addTreeIndent
11
10
  } from './utils/tree';
@@ -479,13 +478,12 @@ const MultiFuncSelect = forwardRef((props: MultiFuncSelectProps, ref: any) => {
479
478
  // set value if the attribute `data-options` of component exists, only valid for single selection (it may be an empty array)
480
479
  if (typeof defaultValue !== 'undefined' && defaultValue !== '') valueInputRef.current.dataset.value = defaultValue;
481
480
  }
482
-
481
+
483
482
 
484
483
  // STEP 2: ===========
485
484
  // Set hierarchical categories ( with sub-categories )
486
485
  if ( hierarchical ) {
487
- addTreeDepth(_ORGIN_DATA);
488
- _ORGIN_DATA = flatTree(_ORGIN_DATA);
486
+ _ORGIN_DATA = addTreeDepth(_ORGIN_DATA);
489
487
  addTreeIndent(_ORGIN_DATA, INDENT_PLACEHOLDER, INDENT_LAST_PLACEHOLDER, 'label');
490
488
  }
491
489
 
@@ -630,8 +628,7 @@ const MultiFuncSelect = forwardRef((props: MultiFuncSelectProps, ref: any) => {
630
628
  // STEP 2: ===========
631
629
  // Set hierarchical categories ( with sub-categories )
632
630
  if ( hierarchical ) {
633
- addTreeDepth(optionsDataInit);
634
- optionsDataInit = flatTree(optionsDataInit);
631
+ optionsDataInit = addTreeDepth(optionsDataInit);
635
632
  addTreeIndent(optionsDataInit, INDENT_PLACEHOLDER, INDENT_LAST_PLACEHOLDER, 'label');
636
633
  }
637
634
 
@@ -48,26 +48,40 @@ function flatTree(arr) {
48
48
  return result;
49
49
  }
50
50
 
51
-
52
51
  /**
53
- * Add depth to each item in the tree
54
- * @param {Array} arr - Hierarchical array
55
- * @param {?String | ?Number} parentId - Parent id
56
- * @param {?String} keyId - Key value of id.
57
- * @param {?String} keyParentId - Key value of parent id.
58
- * @param {?Number} depth - Depth of the item.
59
- * @returns
52
+ * Get all depth
53
+ * @param {Object} node
54
+ * @returns Number
60
55
  */
61
- function addTreeDepth(arr, parentId = '', keyId = 'id', keyParentId = 'parent_id', depth = 0) {
62
- arr.forEach((item) => {
63
- item.depth = depth;
64
- // Query all child nodes by parent node ID
65
- if (item.children && item.children.length > 0) {
66
- addTreeDepth(item.children, item[keyId], keyId, keyParentId, ++depth);
67
- } else {
68
- depth = 0;
69
- }
70
- });
56
+ function getAllDepth(arr) {
57
+ const count = (children) => {
58
+ return children.reduce((depth, child) => {
59
+ return Math.max(depth, 1 + count(child.children)); // increment depth of children by 1, and compare it with accumulated depth of other children within the same element
60
+ }, 0); //default value 0 that's returned if there are no children
61
+ }
62
+ return count(arr);
63
+ }
64
+
65
+
66
+
67
+
68
+ /**
69
+ * Add depth to each item in the tree
70
+ * @param {Array} arr - Hierarchical array
71
+ * @param {?String} keyId - Key value of id.
72
+ * @param {?String} keyParentId - Key value of parent id.
73
+ * @param {?Number} depth - Depth of the item.
74
+ * @returns Number
75
+ */
76
+ function addTreeDepth(arr, keyId = 'id', parentItem = '', depth = 0) {
77
+ return arr.reduce((acc, el) => {
78
+ const { children, ...otherProps } = el;
79
+ acc.push({ ...otherProps, parentItem, depth });
80
+ if (children) {
81
+ return acc.concat(addTreeDepth(children, keyId, el[keyId], depth + 1));
82
+ }
83
+ return acc;
84
+ }, []);
71
85
  }
72
86
 
73
87
 
@@ -81,8 +95,10 @@ function addTreeDepth(arr, parentId = '', keyId = 'id', keyParentId = 'parent_id
81
95
  * @returns Array
82
96
  */
83
97
  function addTreeIndent(arr, placeholder = '&nbsp;&nbsp;&nbsp;&nbsp;', lastPlaceholder = '', keyName = 'label') {
98
+
84
99
  arr.forEach((item) => {
85
100
  let indent = '';
101
+ console.log(item.depth);
86
102
  if (item.depth) {
87
103
  Array(item.depth).fill(0).forEach((k, i) => {
88
104
  indent += placeholder;
@@ -95,6 +111,7 @@ function addTreeIndent(arr, placeholder = '&nbsp;&nbsp;&nbsp;&nbsp;', lastPlaceh
95
111
  }
96
112
 
97
113
  module.exports = {
114
+ getAllDepth,
98
115
  convertTree,
99
116
  flatTree,
100
117
  addTreeDepth,
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
 
3
3
  interface ItemProps extends React.ComponentPropsWithoutRef<any> {
4
+ onlyOne?: boolean;
4
5
  index: number;
5
6
  title?: string | React.ReactNode | boolean;
6
7
  note?: string | React.ReactNode | boolean;
@@ -17,6 +18,7 @@ interface ItemProps extends React.ComponentPropsWithoutRef<any> {
17
18
  const Item = (props: ItemProps) => {
18
19
 
19
20
  const {
21
+ onlyOne,
20
22
  index,
21
23
  title,
22
24
  note,
@@ -36,7 +38,7 @@ const Item = (props: ItemProps) => {
36
38
  <>
37
39
 
38
40
  <div
39
- className={`toast-container`}
41
+ className={`toast-container ${onlyOne ? 'only-one' : ''}`}
40
42
  data-index={index}
41
43
  style={cascading ? {
42
44
  transform: `perspective(100px) translateZ(-${2 * index}px) translateY(${35 * index}px)`,
@@ -83,6 +83,40 @@
83
83
 
84
84
  .toast-container {
85
85
  margin-bottom: var(--toasts-container-gap);
86
+
87
+ &.auto-anim-switch--initfirst {
88
+ transform: translateY(-20);
89
+ opacity: 0;
90
+ }
91
+ &.auto-anim-switch--first {
92
+ animation-name: toast-anim-show-first;
93
+ animation-duration: .3s;
94
+ animation-timing-function: linear;
95
+ animation-delay: 0;
96
+ animation-iteration-count: 1;
97
+ animation-fill-mode: forwards;
98
+ }
99
+
100
+ &.auto-anim-switch:not(.only-one) {
101
+ animation-name: toast-anim-hide;
102
+ animation-duration: .3s;
103
+ animation-timing-function: linear;
104
+ animation-delay: 0;
105
+ animation-iteration-count: 1;
106
+ animation-fill-mode: forwards;
107
+ }
108
+
109
+
110
+ &.only-one.auto-anim-switch {
111
+ animation-name: toast-anim-hide--onlyone;
112
+ animation-duration: .3s;
113
+ animation-timing-function: linear;
114
+ animation-delay: 0;
115
+ animation-iteration-count: 1;
116
+ animation-fill-mode: forwards;
117
+ }
118
+
119
+
86
120
  }
87
121
 
88
122
  /* cascading divs */
@@ -140,3 +174,64 @@
140
174
  }
141
175
 
142
176
  }
177
+
178
+ @keyframes toast-anim-initfirst {
179
+ 0% {
180
+ transform: translateY(-20px);
181
+ opacity: 1;
182
+ }
183
+
184
+ 100% {
185
+ transform: translateY(20px);
186
+ opacity: 0;
187
+ }
188
+ }
189
+
190
+ @keyframes toast-anim-show-first {
191
+ 0% {
192
+ transform: translateY(-20px);
193
+ opacity: 0;
194
+ }
195
+
196
+ 100% {
197
+ transform: translateY(0);
198
+ opacity: 1;
199
+ }
200
+ }
201
+
202
+ @keyframes toast-anim-hide {
203
+ 0% {
204
+ transform: translateY(-100%);
205
+ opacity: 1;
206
+ }
207
+
208
+ 100% {
209
+ transform: translateY(10px);
210
+ opacity: 0;
211
+
212
+ /* prevent auto-close's item */
213
+ display: block;
214
+
215
+ }
216
+ }
217
+
218
+
219
+ @keyframes toast-anim-hide--onlyone {
220
+ 0% {
221
+ transform: translateY(-100%);
222
+ opacity: 1;
223
+
224
+
225
+ /* prevent auto-close's item */
226
+ display: block;
227
+ }
228
+
229
+ 100% {
230
+ transform: translateY(10px);
231
+ opacity: 0;
232
+
233
+ /* prevent auto-close's item */
234
+ display: block;
235
+
236
+ }
237
+ }
@@ -1,7 +1,8 @@
1
- import React, { useId, useEffect, useState, useRef } from 'react';
1
+ import React, { useId, useEffect, useRef } from 'react';
2
2
 
3
3
  import Item from './Item';
4
4
 
5
+
5
6
  declare global {
6
7
  interface Window {
7
8
  setCloseToast?: any;
@@ -12,8 +13,8 @@ declare global {
12
13
  type ToastProps = {
13
14
  /** Specify data of toasts as a JSON string format. */
14
15
  data: any[any];
15
- /** Use asynchronous triggering */
16
- async?: boolean;
16
+ /** Automatically hide multiple items */
17
+ autoHideMultiple?: boolean;
17
18
  /** The direction of the toast. */
18
19
  direction?: string;
19
20
  /** Set an automatic closing time, multiple items will be accumulated in order.
@@ -34,6 +35,8 @@ type ToastProps = {
34
35
  closeBtnColor?: string;
35
36
  /** Disable the close button. */
36
37
  closeDisabled?: boolean;
38
+ /** */
39
+ async?: boolean;
37
40
  /** -- */
38
41
  id?: string;
39
42
  onClose?: (e: HTMLDivElement, currentIndex: number, data: HTMLDivElement[]) => void;
@@ -43,6 +46,7 @@ type ToastProps = {
43
46
  const Toast = (props: ToastProps) => {
44
47
  const {
45
48
  async,
49
+ autoHideMultiple,
46
50
  direction,
47
51
  autoCloseTime,
48
52
  autoCloseReverse,
@@ -61,11 +65,11 @@ const Toast = (props: ToastProps) => {
61
65
  const uniqueID = useId().replace(/\:/g, "-");
62
66
  const idRes = id || uniqueID;
63
67
  const rootRef = useRef<any>(null);
64
- let depth: number = data.length + 1;
68
+ let depth: number = autoHideMultiple ? data.slice(-2).length + 1 : data.length + 1;
65
69
  const cascadingEnabled = typeof cascading === 'undefined' ? true : cascading;
66
70
 
67
71
  function init() {
68
-
72
+
69
73
 
70
74
  // Move HTML templates to tag end body </body>
71
75
  // render() don't use "Fragment", in order to avoid error "Failed to execute 'insertBefore' on 'Node'"
@@ -79,7 +83,25 @@ const Toast = (props: ToastProps) => {
79
83
  const currentItem = node.closest('.toast-container');
80
84
  handleClose(index as never, currentItem as never);
81
85
  });
82
- });
86
+ });
87
+
88
+
89
+ // Automatically hide multiple items
90
+ // It creates a transition animation effect with multiple records and only one displayed.
91
+ if (autoHideMultiple) {
92
+ const _list: HTMLDivElement[] = [].slice.call(rootRef.current.querySelectorAll('.toast-container'));
93
+ _list.forEach((node: any, i: number) => {
94
+ if (i !== _list.length - 1) {
95
+ node.classList.add('auto-anim-switch');
96
+ } else {
97
+ node.classList.add('auto-anim-switch--initfirst');
98
+ node.classList.add('auto-anim-switch--first');
99
+ }
100
+ });
101
+ }
102
+
103
+
104
+
83
105
  }
84
106
 
85
107
 
@@ -110,6 +132,7 @@ const Toast = (props: ToastProps) => {
110
132
  const _autoCloseTime: any = typeof (autoCloseTime) === 'undefined' || autoCloseTime === false ? false : autoCloseTime;
111
133
  if (_autoCloseTime !== false) {
112
134
  const items = JSON.parse(JSON.stringify(data));
135
+
113
136
  autoClose(0, items, _autoCloseTime);
114
137
 
115
138
  }
@@ -158,9 +181,9 @@ const Toast = (props: ToastProps) => {
158
181
  const _list: HTMLDivElement[] = [].slice.call(rootRef.current.querySelectorAll('.toast-container'));
159
182
 
160
183
  if ( autoCloseReverse ) {
161
- _list[items.length-index].classList.add('hide-start');
184
+ _list[items.length-index]?.classList.add('hide-start');
162
185
  } else {
163
- _list[index-1].classList.add('hide-start');
186
+ _list[index-1]?.classList.add('hide-start');
164
187
  }
165
188
 
166
189
 
@@ -179,7 +202,7 @@ const Toast = (props: ToastProps) => {
179
202
  onClose?.(rootRef.current, Number(items.length-index), _list.filter((node: HTMLDivElement) => !node.classList.contains('hide-end') ));
180
203
 
181
204
  } else {
182
- _list[index-1].classList.add('hide-end');
205
+ _list[index-1]?.classList.add('hide-end');
183
206
  //
184
207
  onClose?.(rootRef.current, Number(index-1), _list.filter((node: HTMLDivElement) => !node.classList.contains('hide-end') ));
185
208
  }
@@ -205,6 +228,7 @@ const Toast = (props: ToastProps) => {
205
228
 
206
229
  useEffect(() => {
207
230
 
231
+
208
232
  // Initialize
209
233
  //------------------------------------------
210
234
  init();
@@ -238,8 +262,9 @@ const Toast = (props: ToastProps) => {
238
262
 
239
263
  <div id={`toasts__wrapper-${idRes}`} data-async={async ? async : false} className={`toasts__wrapper toasts__wrapper--${direction ? direction : 'bottom-center'} ${cascadingEnabled ? 'toasts__wrapper--cascading' : ''}`} ref={rootRef}>
240
264
  <div className="toasts">
241
- {data.map((item: any, i: number) => {
265
+ {(autoHideMultiple ? data.slice(-2) : data).map((item: any, i: number) => {
242
266
  return <Item
267
+ onlyOne={data.length === 1 ? true : false}
243
268
  depth={depth - i}
244
269
  key={i}
245
270
  index={i}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "author": "UIUX Lab",
3
3
  "email": "uiuxlab@gmail.com",
4
4
  "name": "funda-ui",
5
- "version": "1.0.400",
5
+ "version": "1.0.405",
6
6
  "description": "React components using pure Bootstrap 5+ which does not contain any external style and script libraries.",
7
7
  "repository": {
8
8
  "type": "git",