ketekny-ui-kit 1.0.50 → 1.0.52

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +119 -114
  2. package/package.json +1 -4
  3. package/src/ui/kTree.vue +194 -38
package/README.md CHANGED
@@ -1,56 +1,95 @@
1
- # Ketekny UI Kit
1
+ # ketekny-ui-kit
2
2
 
3
- Vue 3 UI component library styled with Tailwind CSS.
3
+ Vue 3 UI component library with Tailwind CSS styling and utility plugins.
4
4
 
5
- ## Installation
5
+ ## Requirements
6
+
7
+ - `vue`: `^3.0.0` (peer dependency)
8
+ - Tailwind CSS build pipeline in your app (`tailwindcss`, `postcss`, `autoprefixer`)
6
9
 
7
- 1. Install the package:
10
+ ## Installation
8
11
 
9
12
  ```bash
10
13
  npm install ketekny-ui-kit
11
14
  ```
12
15
 
13
- 2. Ensure required peer/runtime dependencies exist in your app:
16
+ Required peer dependency:
17
+
18
+ ```bash
19
+ npm install vue
20
+ ```
21
+
22
+ If your app does not already use Tailwind CSS:
14
23
 
15
24
  ```bash
16
- npm install vue tailwindcss postcss autoprefixer
25
+ npm install -D tailwindcss postcss autoprefixer
17
26
  ```
18
27
 
19
- If your project already has Vue + Tailwind configured, you can skip step 2.
28
+ ## Quick Start
29
+
30
+ In your app entry (`main.js` / `main.ts`):
31
+
32
+ ```js
33
+ import { createApp } from "vue";
34
+ import App from "./App.vue";
35
+ import "ketekny-ui-kit/styles.css";
36
+
37
+ import {
38
+ kButton,
39
+ kInput,
40
+ kSelect,
41
+ kDateSelector,
42
+ kToggle,
43
+ toastPlugin,
44
+ confirmPlugin,
45
+ alertPlugin,
46
+ inputDialogPlugin,
47
+ tooltipPlugin,
48
+ } from "ketekny-ui-kit";
49
+
50
+ const app = createApp(App);
20
51
 
21
- ## CI Publish Auth (NPM Token)
52
+ app.component("kButton", kButton);
53
+ app.component("kInput", kInput);
54
+ app.component("kSelect", kSelect);
55
+ app.component("kDateSelector", kDateSelector);
56
+ app.component("kToggle", kToggle);
22
57
 
23
- To publish from GitLab CI, create an npm granular token and store it in the runner/project CI variables.
58
+ app.use(toastPlugin);
59
+ app.use(confirmPlugin);
60
+ app.use(alertPlugin);
61
+ app.use(inputDialogPlugin);
62
+ app.use(tooltipPlugin);
24
63
 
25
- ### 1. Create npm token
64
+ app.mount("#app");
65
+ ```
26
66
 
27
- 1. Open npm -> `Access Tokens` -> `Generate New Token` (Granular).
28
- 2. Set permissions to `Read and write`.
29
- 3. Under package access, allow `ketekny-ui-kit`.
30
- 4. Enable `Bypass two-factor authentication (2FA)`.
31
- 5. Create token and copy it (shown only once).
67
+ Example component usage:
32
68
 
33
- ### 2. Save token in GitLab CI/CD variables
69
+ ```vue
70
+ <template>
71
+ <div class="p-6 space-y-3">
72
+ <kInput v-model="name" label="Name" />
73
+ <kButton label="Save" success @click="save" />
74
+ </div>
75
+ </template>
34
76
 
35
- 1. Open GitLab project -> `Settings` -> `CI/CD` -> `Variables`.
36
- 2. Add variable:
37
- - Key: `NPM_TOKEN`
38
- - Value: `<your npm token>`
39
- - Type: `Variable`
40
- - Masked: `true`
41
- - Protected: `true` (if publishing only from protected branches, e.g. `main`)
42
- 3. Save and rerun pipeline.
77
+ <script setup>
78
+ import { ref } from "vue";
43
79
 
44
- ### 3. Verify runner/tag
80
+ const name = ref("");
45
81
 
46
- - Ensure the runner used by publish jobs has the required tag (currently `front`).
47
- - Ensure the project can use that runner.
82
+ function save() {
83
+ console.log("Saved:", name.value);
84
+ }
85
+ </script>
86
+ ```
48
87
 
49
- ## Tailwind Setup (Required)
88
+ ## Tailwind Setup
50
89
 
51
- Use the provided Tailwind preset so semantic colors and component color tokens are always available.
90
+ Use the provided preset to include semantic tokens and safelist patterns used by dynamic component classes.
52
91
 
53
- ### `tailwind.config.js` (ESM)
92
+ `tailwind.config.js` (ESM):
54
93
 
55
94
  ```js
56
95
  import tailwindPreset from "ketekny-ui-kit/tailwind-preset.js";
@@ -66,7 +105,7 @@ export default {
66
105
  };
67
106
  ```
68
107
 
69
- ### `tailwind.config.cjs` (CommonJS)
108
+ `tailwind.config.cjs` (CommonJS):
70
109
 
71
110
  ```js
72
111
  const tailwindPreset = require("ketekny-ui-kit/tailwind-preset.js").default;
@@ -82,101 +121,66 @@ module.exports = {
82
121
  };
83
122
  ```
84
123
 
85
- Notes:
86
- - The preset contains the semantic color definitions and safelist patterns used by dynamic classes (alerts/toasts/button intents).
87
- - If you replace `safelist` in your own config, keep equivalent patterns or dynamic semantic classes may be purged.
124
+ ## Plugins
88
125
 
89
- ## App CSS Setup
126
+ - `toastPlugin`: adds `$toast.success|error|warning|info|show(...)`
127
+ - `confirmPlugin`: adds `$confirm(options)`
128
+ - `alertPlugin`: adds `$alert.success|warning|error|info(...)`
129
+ - `inputDialogPlugin`: adds `$prompt(options)`
130
+ - `tooltipPlugin`: registers `v-tooltip`
90
131
 
91
- Import the kit stylesheet in your app entry (`main.js` / `main.ts`):
132
+ ### Plugin API (quick reference)
92
133
 
93
- ```js
94
- import "ketekny-ui-kit/styles.css";
95
- ```
134
+ - `$toast.success(message, options?)`
135
+ - `$toast.error(message, options?)`
136
+ - `$toast.warning(message, options?)`
137
+ - `$toast.info(message, options?)`
138
+ - `$toast.show({ type?, message, duration?, ... })`
139
+ - `$confirm({ title?, message, onConfirm?, onCancel? })`
140
+ - `$alert.success(message)`
141
+ - `$alert.error(message)`
142
+ - `$alert.warning(message)`
143
+ - `$alert.info(message)`
144
+ - `$prompt({ title?, message?, placeholder?, onConfirm? })`
96
145
 
97
- ## Vue Setup
146
+ Note: exact option keys may vary by component/plugin implementation version.
98
147
 
99
- `main.js` / `main.ts` example:
148
+ ## Exports
100
149
 
101
- ```js
102
- import { createApp } from "vue";
103
- import App from "./App.vue";
104
- import "./style.css";
150
+ Components:
105
151
 
106
- import {
107
- kButton,
108
- kInput,
109
- kSelect,
110
- kDateSelector,
111
- kToggle,
112
- toastPlugin,
113
- confirmPlugin,
114
- alertPlugin,
115
- inputDialogPlugin,
116
- tooltipPlugin,
117
- } from "ketekny-ui-kit";
152
+ - `kMessage`, `kCode`, `kToolbar`, `kTable`, `kTabs`, `kChip`
153
+ - `kSpinner`, `kDatatable`, `kIcon`, `kMenu`, `kSkeleton`
154
+ - `kProgressBar`, `kTree`, `kButton`, `kSelect`, `kUploader`
155
+ - `kToggle`, `kInput`, `kDateSelector`, `kDateSelectorV2`, `kEditor`
156
+ - `kSelectButton`, `kTags`, `kSearch`, `kArrayList`, `kList`, `kTextArea`
157
+ - `kDialog`, `kDrawer`
158
+ - `kAppHeader`, `kAppFooter`, `kAppMain`, `kHero`
118
159
 
119
- const app = createApp(App);
160
+ Other exports:
120
161
 
121
- app.component("kButton", kButton);
122
- app.component("kInput", kInput);
123
- app.component("kSelect", kSelect);
124
- app.component("kDateSelector", kDateSelector);
125
- app.component("kToggle", kToggle);
162
+ - `toastPlugin`, `confirmPlugin`, `alertPlugin`, `tooltipPlugin`, `inputDialogPlugin`
163
+ - `tailwindPreset`
126
164
 
127
- app.use(toastPlugin);
128
- app.use(confirmPlugin);
129
- app.use(alertPlugin);
130
- app.use(inputDialogPlugin);
131
- app.use(tooltipPlugin);
165
+ ## Common Issues
132
166
 
133
- app.mount("#app");
134
- ```
167
+ - Styles look missing:
168
+ - Confirm `import "ketekny-ui-kit/styles.css";` is in your app entry file.
169
+ - Tailwind classes from the kit are not applied:
170
+ - Confirm `tailwind.config` includes `./node_modules/ketekny-ui-kit/**/*.{vue,js,ts,jsx,tsx}` in `content`.
171
+ - Confirm `presets: [tailwindPreset]` is present.
172
+ - Vue peer dependency warning:
173
+ - Install/update Vue 3 in your app: `npm install vue@^3`.
174
+ - Dynamic semantic classes are purged:
175
+ - Keep the provided preset safelist patterns, or replicate them in your own Tailwind config.
135
176
 
136
- ## Available Plugins
137
-
138
- - `toastPlugin` adds `$toast` (`success`, `error`, `warning`, `info`, `show`)
139
- - `confirmPlugin` adds `$confirm(options)`
140
- - `alertPlugin` adds `$alert.success|warning|error|info(...)`
141
- - `inputDialogPlugin` adds `$prompt(options)`
142
- - `tooltipPlugin` registers `v-tooltip`
143
-
144
- ## Available Exports
145
-
146
- ### Components
147
-
148
- - `kMessage`
149
- - `kButton`
150
- - `kChip`
151
- - `kCode`
152
- - `kDialog`
153
- - `kDrawer`
154
- - `kInput`
155
- - `kDateSelector`
156
- - `kToolbar`
157
- - `kSelect`
158
- - `kTable`
159
- - `kTabs`
160
- - `kToggle`
161
- - `kUploader`
162
- - `kEditor`
163
- - `kSpinner`
164
- - `kSelectButton`
165
- - `kDatatable`
166
- - `kIcon`
167
- - `kMenu`
168
- - `kTags`
169
- - `kSearch`
170
- - `kArrayList`
171
- - `kSkeleton`
172
- - `kAppHeader`
173
- - `kAppFooter`
174
- - `kAppMain`
175
- - `kHero`
176
-
177
- ### Tailwind preset export
177
+ ## Development
178
178
 
179
- - `tailwindPreset`
179
+ ```bash
180
+ npm run dev
181
+ npm run build
182
+ npm run preview
183
+ ```
180
184
 
181
185
  ## License
182
186
 
@@ -184,4 +188,5 @@ ISC
184
188
 
185
189
  ## Links
186
190
 
187
- - [npm](https://www.npmjs.com/package/ketekny-ui-kit)
191
+ - Live demo/docs: https://ui.ketekny.gr
192
+ - npm: https://www.npmjs.com/package/ketekny-ui-kit
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ketekny-ui-kit",
3
3
  "type": "module",
4
- "version": "1.0.50",
4
+ "version": "1.0.52",
5
5
  "description": "A Vue 3 UI component library with Tailwind CSS styling",
6
6
  "main": "index.js",
7
7
  "files": [
@@ -43,13 +43,10 @@
43
43
  "class-variance-authority": "^0.7.1",
44
44
  "clsx": "^2.1.1",
45
45
  "element-plus": "^2.13.5",
46
- "he-tree-vue": "^3.1.2",
47
- "json-editor-vue": "^0.18.1",
48
46
  "lucide-vue-next": "^0.511.0",
49
47
  "moment": "^2.30.1",
50
48
  "primeicons": "^7.0.0",
51
49
  "primevue": "^4.3.4",
52
- "quill": "2.0.2",
53
50
  "reka-ui": "^2.8.2",
54
51
  "simple-code-editor": "^2.0.9",
55
52
  "tailwind-merge": "^3.5.0",
package/src/ui/kTree.vue CHANGED
@@ -1,54 +1,128 @@
1
1
  <template>
2
2
  <div class="k-tree text-primary/90" :class="disabled ? 'k-tree--disabled' : ''">
3
- <HETree
4
- ref="treeRef"
5
- class="k-tree__root"
6
- :value="treeData"
3
+ <TreeBranch
4
+ :nodes="treeData"
7
5
  :indent="indent"
6
+ :disabled="disabled"
8
7
  :draggable="draggable"
9
8
  :droppable="droppable"
10
- :edge-scroll="edgeScroll"
11
- :trigger-class="triggerClass"
12
- @nodeFoldedChanged="syncExpandedFromTree"
13
- @drop="onDrop"
14
- @change="onChange">
15
- <template #default="{ node, path, tree }">
9
+ :selected-keys="resolvedSelectionKeys"
10
+ :resolve-node-label="resolveNodeLabel"
11
+ @node-click="onNodeClick"
12
+ @toggle="toggleNode"
13
+ @drag-start="onDragStart"
14
+ @node-drop="onNodeDrop">
15
+ <template #default="{ node, path }">
16
+ <slot name="default" :node="node" :path="path" :tree="treeApi">
17
+ {{ resolveNodeLabel(node) }}
18
+ </slot>
19
+ </template>
20
+ </TreeBranch>
21
+ </div>
22
+ </template>
23
+
24
+ <script>
25
+ import { ChevronRight } from 'lucide-vue-next'
26
+
27
+ const TreeBranch = {
28
+ name: 'TreeBranch',
29
+ components: { ChevronRight },
30
+ props: {
31
+ nodes: { type: Array, default: () => [] },
32
+ indent: { type: Number, default: 20 },
33
+ disabled: { type: Boolean, default: false },
34
+ draggable: { type: [Boolean, Function], default: false },
35
+ droppable: { type: [Boolean, Function], default: false },
36
+ selectedKeys: { type: Object, default: () => ({}) },
37
+ resolveNodeLabel: { type: Function, required: true },
38
+ },
39
+ emits: ['node-click', 'toggle', 'drag-start', 'node-drop'],
40
+ methods: {
41
+ hasChildren(node) {
42
+ return Array.isArray(node?.children) && node.children.length > 0
43
+ },
44
+ canDrag(node) {
45
+ return this.draggable === true || (typeof this.draggable === 'function' && this.draggable(node.__raw || node))
46
+ },
47
+ canDrop(node) {
48
+ return this.droppable === true || (typeof this.droppable === 'function' && this.droppable(node.__raw || node))
49
+ },
50
+ isSelected(key) {
51
+ return this.selectedKeys?.[String(key)] === true
52
+ },
53
+ onDragStart(event, node) {
54
+ if (!this.canDrag(node)) {
55
+ event.preventDefault()
56
+ return
57
+ }
58
+ event.dataTransfer.effectAllowed = 'move'
59
+ event.dataTransfer.setData('text/plain', String(node.key))
60
+ this.$emit('drag-start', node)
61
+ },
62
+ onDrop(event, node) {
63
+ if (!this.canDrop(node)) return
64
+ event.preventDefault()
65
+ this.$emit('node-drop', node)
66
+ },
67
+ },
68
+ template: `
69
+ <ul class="k-tree-branch">
70
+ <li v-for="node in nodes" :key="node.key" class="k-tree-node">
16
71
  <div
17
72
  class="k-tree-node-content"
18
- :class="isSelected(node.key) ? 'k-tree-node-content--selected' : ''"
19
- @click="onNodeClick($event, node)">
73
+ :class="[
74
+ isSelected(node.key) ? 'k-tree-node-content--selected' : '',
75
+ canDrop(node) ? 'k-tree-node-content--droppable' : '',
76
+ ]"
77
+ :style="{ paddingLeft: node.__level ? (node.__level * indent + 7) + 'px' : undefined }"
78
+ :draggable="canDrag(node)"
79
+ @dragstart="onDragStart($event, node)"
80
+ @dragover.prevent="canDrop(node)"
81
+ @drop="onDrop($event, node)"
82
+ @click="$emit('node-click', $event, node)">
20
83
  <button
21
84
  v-if="hasChildren(node)"
22
85
  type="button"
23
86
  class="k-tree-node-toggler"
24
87
  :aria-label="node.$folded ? 'Expand node' : 'Collapse node'"
25
- @click.stop="toggleNode(tree, node, path)">
88
+ @click.stop="$emit('toggle', node)">
26
89
  <ChevronRight class="k-tree-node-toggler-icon" :class="node.$folded ? '' : 'k-tree-node-toggler-icon--open'" />
27
90
  </button>
28
91
 
29
92
  <span v-else class="k-tree-node-toggler k-tree-node-toggler--empty"></span>
30
93
 
31
94
  <div class="k-tree-node-label">
32
- <slot name="default" :node="node.__raw || node" :path="path" :tree="tree">
95
+ <slot :node="node.__raw || node" :path="node.__path">
33
96
  {{ resolveNodeLabel(node.__raw || node) }}
34
97
  </slot>
35
98
  </div>
36
99
  </div>
37
- </template>
38
- </HETree>
39
- </div>
40
- </template>
41
100
 
42
- <script>
43
- import { ChevronRight } from 'lucide-vue-next'
44
- import { Tree as BaseTree, Fold, Draggable } from 'he-tree-vue'
45
- import 'he-tree-vue/dist/he-tree-vue.css'
46
-
47
- const HETree = BaseTree.mixPlugins([Fold, Draggable])
101
+ <TreeBranch
102
+ v-if="hasChildren(node) && !node.$folded"
103
+ :nodes="node.children"
104
+ :indent="indent"
105
+ :disabled="disabled"
106
+ :draggable="draggable"
107
+ :droppable="droppable"
108
+ :selected-keys="selectedKeys"
109
+ :resolve-node-label="resolveNodeLabel"
110
+ @node-click="(...args) => $emit('node-click', ...args)"
111
+ @toggle="(...args) => $emit('toggle', ...args)"
112
+ @drag-start="(...args) => $emit('drag-start', ...args)"
113
+ @node-drop="(...args) => $emit('node-drop', ...args)">
114
+ <template #default="slotProps">
115
+ <slot v-bind="slotProps" />
116
+ </template>
117
+ </TreeBranch>
118
+ </li>
119
+ </ul>
120
+ `,
121
+ }
48
122
 
49
123
  export default {
50
124
  name: 'kTree',
51
- components: { HETree, ChevronRight },
125
+ components: { TreeBranch },
52
126
  props: {
53
127
  value: { type: Array, default: () => [] },
54
128
  selectionKeys: { type: [Object, null], default: null },
@@ -67,6 +141,7 @@ export default {
67
141
  data() {
68
142
  return {
69
143
  internalExpandedKeys: { ...(this.expandedKeys || {}) },
144
+ draggedNode: null,
70
145
  treeData: [],
71
146
  }
72
147
  },
@@ -74,6 +149,12 @@ export default {
74
149
  resolvedSelectionKeys() {
75
150
  return this.selectionKeys ?? this.modelValue ?? {}
76
151
  },
152
+ treeApi() {
153
+ return {
154
+ toggleFold: this.toggleNode,
155
+ treeData: this.treeData,
156
+ }
157
+ },
77
158
  },
78
159
  watch: {
79
160
  value: {
@@ -95,15 +176,18 @@ export default {
95
176
  rebuildTreeData() {
96
177
  this.treeData = this.buildTreeData(this.value || [], '', this.internalExpandedKeys)
97
178
  },
98
- buildTreeData(nodes, parentPath, expandedMap) {
179
+ buildTreeData(nodes, parentPath, expandedMap, level = 0) {
99
180
  return (nodes || []).map((node, index) => {
100
181
  const key = this.getNodeKey(node, parentPath, index)
101
- const children = this.buildTreeData(node.children || [], key, expandedMap)
182
+ const path = parentPath ? `${parentPath}-${index}` : String(index)
183
+ const children = this.buildTreeData(node.children || [], key, expandedMap, level + 1)
102
184
  return {
103
185
  ...node,
104
186
  key,
105
187
  children,
106
188
  __raw: node,
189
+ __level: level,
190
+ __path: path,
107
191
  $folded: this.hasChildren(node) ? expandedMap?.[key] !== true : false,
108
192
  }
109
193
  })
@@ -148,19 +232,76 @@ export default {
148
232
  this.emitSelection(next)
149
233
  this.$emit('nodeSelect', { originalEvent, node: node.__raw || node, data: node.__raw || node })
150
234
  },
151
- toggleNode(tree, node, path) {
152
- tree.toggleFold(node, path)
153
- this.$nextTick(() => this.syncExpandedFromTree(tree))
235
+ toggleNode(node) {
236
+ node.$folded = !node.$folded
237
+ this.syncExpandedFromTree()
238
+ },
239
+ onDragStart(node) {
240
+ this.draggedNode = node
154
241
  },
155
- onDrop(store) {
156
- this.$emit('drop', store)
242
+ onNodeDrop(targetNode) {
243
+ if (!this.draggedNode || this.draggedNode.key === targetNode.key) return
244
+ const nextTree = this.cloneWithoutNode(this.treeData, this.draggedNode.key)
245
+ const moved = this.stripInternalFields(this.draggedNode)
246
+ const changedTree = this.appendChild(nextTree, targetNode.key, moved)
247
+
248
+ this.treeData = changedTree
249
+ this.draggedNode = null
250
+ this.syncExpandedFromTree()
251
+
252
+ const payload = {
253
+ dragNode: moved,
254
+ dropNode: this.stripInternalFields(targetNode),
255
+ treeData: this.toRawNodes(this.treeData),
256
+ }
257
+ this.$emit('drop', payload)
258
+ this.$emit('change', payload)
157
259
  },
158
- onChange(store) {
159
- this.$nextTick(() => this.syncExpandedFromTree(store?.targetTree || this.$refs.treeRef))
160
- this.$emit('change', store)
260
+ cloneWithoutNode(nodes, key) {
261
+ return (nodes || [])
262
+ .filter((node) => String(node.key) !== String(key))
263
+ .map((node) => ({
264
+ ...node,
265
+ children: this.cloneWithoutNode(node.children || [], key),
266
+ }))
267
+ },
268
+ appendChild(nodes, targetKey, child) {
269
+ return (nodes || []).map((node) => {
270
+ if (String(node.key) === String(targetKey)) {
271
+ const nextChildren = [...(node.children || []), this.decorateMovedNode(child, node)]
272
+ return { ...node, children: nextChildren, $folded: false }
273
+ }
274
+ return { ...node, children: this.appendChild(node.children || [], targetKey, child) }
275
+ })
161
276
  },
162
- syncExpandedFromTree(treeVm) {
163
- const source = treeVm?.treeData || this.treeData || []
277
+ decorateMovedNode(node, parent) {
278
+ const key = this.getNodeKey(node, String(parent.key), (parent.children || []).length)
279
+ const children = (node.children || []).map((child, index) => ({
280
+ ...this.decorateMovedNode(child, { key, children: node.children }),
281
+ __path: `${key}-${index}`,
282
+ }))
283
+ return {
284
+ ...node,
285
+ key,
286
+ children,
287
+ __raw: node,
288
+ __level: (parent.__level || 0) + 1,
289
+ __path: `${parent.__path}-${(parent.children || []).length}`,
290
+ $folded: this.hasChildren(node) ? false : false,
291
+ }
292
+ },
293
+ stripInternalFields(node) {
294
+ const { __raw, __level, __path, $folded, ...rest } = node || {}
295
+ return {
296
+ ...rest,
297
+ children: (rest.children || []).map((child) => this.stripInternalFields(child)),
298
+ }
299
+ },
300
+ toRawNodes(nodes) {
301
+ return (nodes || []).map((node) => this.stripInternalFields(node))
302
+ },
303
+ syncExpandedFromTree() {
304
+ const source = this.treeData || []
164
305
  const next = {}
165
306
  const walk = (nodes) => {
166
307
  ;(nodes || []).forEach((node) => {
@@ -179,7 +320,7 @@ export default {
179
320
  }
180
321
  </script>
181
322
 
182
- <style scoped>
323
+ <style>
183
324
  .k-tree {
184
325
  background: #ffffff;
185
326
  border: 0;
@@ -188,6 +329,17 @@ export default {
188
329
  overflow: auto;
189
330
  }
190
331
 
332
+ .k-tree-branch {
333
+ list-style: none;
334
+ margin: 0;
335
+ padding: 0;
336
+ }
337
+
338
+ .k-tree-node {
339
+ margin: 0;
340
+ padding: 0;
341
+ }
342
+
191
343
  .k-tree--disabled {
192
344
  opacity: 0.6;
193
345
  pointer-events: none;
@@ -211,6 +363,10 @@ export default {
211
363
  background: #f8fafc;
212
364
  }
213
365
 
366
+ .k-tree-node-content--droppable {
367
+ cursor: grab;
368
+ }
369
+
214
370
  .k-tree-node-content--selected {
215
371
  background: #ecfdf3;
216
372
  color: #15803d;