@krometra/ui-kit 0.3.0 → 0.4.1
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/README.md +15 -3
- package/dist/ui-kit.css +1 -1
- package/dist/ui-kit.js +2436 -2166
- package/dist/ui-kit.umd.cjs +3 -3
- package/package.json +1 -1
- package/src/components/KGantt.vue +402 -0
- package/src/index.js +3 -2
package/package.json
CHANGED
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
/**
|
|
3
|
+
* KGantt — диаграмма Ганта по дням: слева список задач, справа полосы сроков.
|
|
4
|
+
* Задачи группируются (по исполнителю, команде, этапу); у группы сводная
|
|
5
|
+
* полоса от самого раннего начала до самого позднего конца, группы
|
|
6
|
+
* сворачиваются. Даты — строки 'YYYY-MM-DD'.
|
|
7
|
+
*
|
|
8
|
+
* items: [{ key, label, start, end?, group?, code?, tone?, hint?, href?, muted?,
|
|
9
|
+
* segments?: [{ start, end }] }]
|
|
10
|
+
* groups: [{ key, label, hint? }] — порядок групп; без него задачи идут одним списком
|
|
11
|
+
* tone — 'primary' | 'success' | 'warning' | 'danger' | 'info' | 'neutral'
|
|
12
|
+
* muted — завершённая или неактуальная задача, полоса приглушена
|
|
13
|
+
* segments — несколько отрезков в одной строке (работа с перерывами)
|
|
14
|
+
* dayWidth — минимальная ширина дня в px; свободное место колонки дней делят поровну
|
|
15
|
+
*
|
|
16
|
+
* <KGantt :items="tasks" :groups="people" from="2026-10-19" to="2026-11-06"
|
|
17
|
+
* @item-click="open">
|
|
18
|
+
* <template #group="{ group }"><KAvatar :name="group.label" size="sm" />{{ group.label }}</template>
|
|
19
|
+
* </KGantt>
|
|
20
|
+
*
|
|
21
|
+
* Слоты: #group="{ group, items, collapsed }", #label="{ item }", #bar="{ item }".
|
|
22
|
+
*/
|
|
23
|
+
import { computed, ref } from 'vue'
|
|
24
|
+
import KIcon from './KIcon.vue'
|
|
25
|
+
import KTooltip from './KTooltip.vue'
|
|
26
|
+
|
|
27
|
+
const props = defineProps({
|
|
28
|
+
items: { type: Array, default: () => [] },
|
|
29
|
+
groups: { type: Array, default: () => [] },
|
|
30
|
+
from: { type: String, required: true },
|
|
31
|
+
to: { type: String, required: true },
|
|
32
|
+
today: { type: String, default: () => new Date(Date.now() - new Date().getTimezoneOffset() * 60000).toISOString().slice(0, 10) },
|
|
33
|
+
weekends: { type: Boolean, default: false },
|
|
34
|
+
dayWidth: { type: Number, default: 56 },
|
|
35
|
+
labelWidth: { type: String, default: '18rem' },
|
|
36
|
+
emptyText: { type: String, default: 'Нет задач в этом периоде' },
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
const emit = defineEmits(['item-click'])
|
|
40
|
+
|
|
41
|
+
const WEEKDAYS = ['вс', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб']
|
|
42
|
+
const MONTHS = ['янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек']
|
|
43
|
+
|
|
44
|
+
const utc = (iso) => new Date(`${iso}T00:00:00Z`)
|
|
45
|
+
const addDays = (iso, n) => new Date(utc(iso).getTime() + n * 86400000).toISOString().slice(0, 10)
|
|
46
|
+
|
|
47
|
+
const days = computed(() => {
|
|
48
|
+
const list = []
|
|
49
|
+
for (let iso = props.from; iso <= props.to; iso = addDays(iso, 1)) {
|
|
50
|
+
const wd = utc(iso).getUTCDay()
|
|
51
|
+
const weekend = wd === 0 || wd === 6
|
|
52
|
+
if (weekend && !props.weekends) continue
|
|
53
|
+
list.push({ iso, wd, weekend, date: utc(iso).getUTCDate(), month: utc(iso).getUTCMonth() })
|
|
54
|
+
}
|
|
55
|
+
return list.map((d, i) => ({ ...d, monthStart: i === 0 || d.month !== list[i - 1].month }))
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
const todayIndex = computed(() => days.value.findIndex(d => d.iso === props.today))
|
|
59
|
+
|
|
60
|
+
function span(start, end) {
|
|
61
|
+
const s = start || end
|
|
62
|
+
const e = end || start
|
|
63
|
+
if (!s || e < props.from || s > props.to) return null
|
|
64
|
+
const list = days.value
|
|
65
|
+
const first = list.findIndex(d => d.iso >= s)
|
|
66
|
+
let last = -1
|
|
67
|
+
for (let i = list.length - 1; i >= 0; i--) if (list[i].iso <= e) { last = i; break }
|
|
68
|
+
if (first < 0 || last < first) return null
|
|
69
|
+
return { first, last, cutStart: s < props.from, cutEnd: e > props.to }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function barsOf(item) {
|
|
73
|
+
const parts = item.segments?.length ? item.segments : [{ start: item.start, end: item.end }]
|
|
74
|
+
return parts.map(p => span(p.start, p.end)).filter(Boolean)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const collapsed = ref(new Set())
|
|
78
|
+
function toggle(key) {
|
|
79
|
+
const next = new Set(collapsed.value)
|
|
80
|
+
next.has(key) ? next.delete(key) : next.add(key)
|
|
81
|
+
collapsed.value = next
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const rows = computed(() => {
|
|
85
|
+
const out = []
|
|
86
|
+
if (!props.groups.length) {
|
|
87
|
+
for (const item of props.items) out.push({ type: 'item', key: item.key, item, bars: barsOf(item) })
|
|
88
|
+
return out
|
|
89
|
+
}
|
|
90
|
+
for (const group of props.groups) {
|
|
91
|
+
const items = props.items.filter(i => i.group === group.key)
|
|
92
|
+
const bars = items.flatMap(barsOf)
|
|
93
|
+
const summary = bars.length
|
|
94
|
+
? { first: Math.min(...bars.map(b => b.first)), last: Math.max(...bars.map(b => b.last)) }
|
|
95
|
+
: null
|
|
96
|
+
const isCollapsed = collapsed.value.has(group.key)
|
|
97
|
+
out.push({ type: 'group', key: `g:${group.key}`, group, items, summary, collapsed: isCollapsed })
|
|
98
|
+
if (!isCollapsed) for (const item of items) out.push({ type: 'item', key: `${group.key}:${item.key}`, item, bars: barsOf(item) })
|
|
99
|
+
}
|
|
100
|
+
return out
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
const gridStyle = computed(() => ({
|
|
104
|
+
'--k-gantt-day-w': `${props.dayWidth}px`,
|
|
105
|
+
'--k-gantt-label-w': props.labelWidth,
|
|
106
|
+
gridTemplateColumns: `var(--k-gantt-label-w) repeat(${days.value.length}, minmax(var(--k-gantt-day-w), 1fr))`,
|
|
107
|
+
}))
|
|
108
|
+
|
|
109
|
+
const barStyle = (bar) => {
|
|
110
|
+
const n = days.value.length
|
|
111
|
+
return {
|
|
112
|
+
left: `calc(${(bar.first / n) * 100}% + 3px)`,
|
|
113
|
+
width: `calc(${((bar.last - bar.first + 1) / n) * 100}% - 6px)`,
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const dayLines = computed(() => {
|
|
118
|
+
const marks = []
|
|
119
|
+
days.value.forEach((d, i) => {
|
|
120
|
+
if (d.weekend) marks.push({ key: `w${i}`, kind: 'weekend', i })
|
|
121
|
+
if (i > 0 && d.wd === 1 && !props.weekends) marks.push({ key: `m${i}`, kind: 'week', i })
|
|
122
|
+
})
|
|
123
|
+
return marks
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
function open(item) {
|
|
127
|
+
emit('item-click', item)
|
|
128
|
+
}
|
|
129
|
+
</script>
|
|
130
|
+
|
|
131
|
+
<template>
|
|
132
|
+
<div class="k-gantt">
|
|
133
|
+
<div class="k-gantt__scroll">
|
|
134
|
+
<div class="k-gantt__grid" :style="gridStyle">
|
|
135
|
+
<div class="k-gantt__corner"><slot name="corner" /></div>
|
|
136
|
+
<div
|
|
137
|
+
v-for="(d, i) in days"
|
|
138
|
+
:key="d.iso"
|
|
139
|
+
class="k-gantt__day"
|
|
140
|
+
:class="{ 'k-gantt__day--today': i === todayIndex, 'k-gantt__day--weekend': d.weekend, 'k-gantt__day--week': i > 0 && d.wd === 1 && !weekends }"
|
|
141
|
+
>
|
|
142
|
+
<span class="k-gantt__month">{{ d.monthStart ? MONTHS[d.month] : '' }}</span>
|
|
143
|
+
<span class="k-gantt__date">{{ d.date }}</span>
|
|
144
|
+
<span class="k-gantt__wd">{{ WEEKDAYS[d.wd] }}</span>
|
|
145
|
+
</div>
|
|
146
|
+
|
|
147
|
+
<div v-if="!rows.length" class="k-gantt__empty">{{ emptyText }}</div>
|
|
148
|
+
|
|
149
|
+
<template v-for="(row, r) in rows" :key="row.key">
|
|
150
|
+
<template v-if="row.type === 'group'">
|
|
151
|
+
<button type="button" class="k-gantt__label k-gantt__label--group" :style="{ gridRow: r + 2 }" @click="toggle(row.group.key)">
|
|
152
|
+
<KIcon :name="row.collapsed ? 'chevron-right' : 'chevron-down'" :size="14" class="k-gantt__chevron" />
|
|
153
|
+
<span class="k-gantt__group-name">
|
|
154
|
+
<slot name="group" :group="row.group" :items="row.items" :collapsed="row.collapsed">{{ row.group.label }}</slot>
|
|
155
|
+
</span>
|
|
156
|
+
<span class="k-gantt__count">{{ row.items.length }}</span>
|
|
157
|
+
</button>
|
|
158
|
+
<div class="k-gantt__track k-gantt__track--group" :style="{ gridRow: r + 2 }">
|
|
159
|
+
<div v-if="row.summary" class="k-gantt__summary" :style="barStyle(row.summary)" />
|
|
160
|
+
</div>
|
|
161
|
+
</template>
|
|
162
|
+
|
|
163
|
+
<template v-else>
|
|
164
|
+
<div class="k-gantt__label" :class="{ 'k-gantt__label--muted': row.item.muted }" :style="{ gridRow: r + 2 }">
|
|
165
|
+
<slot name="label" :item="row.item">
|
|
166
|
+
<span v-if="row.item.code" class="k-gantt__code">{{ row.item.code }}</span>
|
|
167
|
+
<span class="k-gantt__title">{{ row.item.label }}</span>
|
|
168
|
+
</slot>
|
|
169
|
+
</div>
|
|
170
|
+
<div class="k-gantt__track" :style="{ gridRow: r + 2 }">
|
|
171
|
+
<div
|
|
172
|
+
v-for="(bar, b) in row.bars"
|
|
173
|
+
:key="b"
|
|
174
|
+
class="k-gantt__bar-slot"
|
|
175
|
+
:style="barStyle(bar)"
|
|
176
|
+
>
|
|
177
|
+
<KTooltip :disabled="!row.item.hint" placement="top">
|
|
178
|
+
<component
|
|
179
|
+
:is="row.item.href ? 'a' : 'button'"
|
|
180
|
+
:href="row.item.href || undefined"
|
|
181
|
+
:target="row.item.href ? '_blank' : undefined"
|
|
182
|
+
:rel="row.item.href ? 'noopener' : undefined"
|
|
183
|
+
:type="row.item.href ? undefined : 'button'"
|
|
184
|
+
class="k-gantt__bar"
|
|
185
|
+
:class="[
|
|
186
|
+
`k-gantt__bar--${row.item.tone || 'primary'}`,
|
|
187
|
+
{ 'k-gantt__bar--muted': row.item.muted, 'k-gantt__bar--cut-start': bar.cutStart, 'k-gantt__bar--cut-end': bar.cutEnd },
|
|
188
|
+
]"
|
|
189
|
+
@click="open(row.item)"
|
|
190
|
+
>
|
|
191
|
+
<slot v-if="b === 0" name="bar" :item="row.item">{{ row.item.code || row.item.label }}</slot>
|
|
192
|
+
</component>
|
|
193
|
+
<template #content><span class="k-gantt__hint">{{ row.item.hint }}</span></template>
|
|
194
|
+
</KTooltip>
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
</template>
|
|
198
|
+
</template>
|
|
199
|
+
|
|
200
|
+
<div
|
|
201
|
+
v-for="m in dayLines"
|
|
202
|
+
:key="m.key"
|
|
203
|
+
class="k-gantt__mark"
|
|
204
|
+
:class="`k-gantt__mark--${m.kind}`"
|
|
205
|
+
:style="{ gridColumn: m.i + 2, gridRow: `2 / ${rows.length + 2}` }"
|
|
206
|
+
/>
|
|
207
|
+
<div
|
|
208
|
+
v-if="todayIndex >= 0 && rows.length"
|
|
209
|
+
class="k-gantt__today"
|
|
210
|
+
:style="{ gridColumn: todayIndex + 2, gridRow: `2 / ${rows.length + 2}` }"
|
|
211
|
+
/>
|
|
212
|
+
</div>
|
|
213
|
+
</div>
|
|
214
|
+
</div>
|
|
215
|
+
</template>
|
|
216
|
+
|
|
217
|
+
<style scoped>
|
|
218
|
+
.k-gantt {
|
|
219
|
+
border: 1px solid var(--k-border);
|
|
220
|
+
border-radius: var(--k-radius);
|
|
221
|
+
background: var(--k-surface);
|
|
222
|
+
overflow: hidden;
|
|
223
|
+
}
|
|
224
|
+
.k-gantt__scroll { overflow: auto; max-height: inherit; }
|
|
225
|
+
.k-gantt__grid { display: grid; grid-auto-rows: minmax(2.25rem, auto); position: relative; min-width: max-content; }
|
|
226
|
+
|
|
227
|
+
.k-gantt__corner,
|
|
228
|
+
.k-gantt__day {
|
|
229
|
+
position: sticky;
|
|
230
|
+
top: 0;
|
|
231
|
+
z-index: 3;
|
|
232
|
+
background: var(--k-surface-muted);
|
|
233
|
+
border-bottom: 1px solid var(--k-border);
|
|
234
|
+
}
|
|
235
|
+
.k-gantt__corner {
|
|
236
|
+
grid-row: 1;
|
|
237
|
+
grid-column: 1;
|
|
238
|
+
left: 0;
|
|
239
|
+
z-index: 4;
|
|
240
|
+
display: flex;
|
|
241
|
+
align-items: flex-end;
|
|
242
|
+
padding: 0.5rem 0.75rem;
|
|
243
|
+
border-right: 1px solid var(--k-border);
|
|
244
|
+
font-size: 0.6875rem;
|
|
245
|
+
font-weight: 600;
|
|
246
|
+
letter-spacing: 0.05em;
|
|
247
|
+
text-transform: uppercase;
|
|
248
|
+
color: var(--k-text-muted);
|
|
249
|
+
}
|
|
250
|
+
.k-gantt__day {
|
|
251
|
+
grid-row: 1;
|
|
252
|
+
display: grid;
|
|
253
|
+
grid-template-rows: 0.9rem auto auto;
|
|
254
|
+
justify-items: center;
|
|
255
|
+
padding: 0.35rem 0 0.4rem;
|
|
256
|
+
font-variant-numeric: tabular-nums;
|
|
257
|
+
}
|
|
258
|
+
.k-gantt__day--week { box-shadow: inset 1px 0 0 var(--k-border); }
|
|
259
|
+
.k-gantt__month { font-size: 0.625rem; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; color: var(--k-primary); }
|
|
260
|
+
.k-gantt__date {
|
|
261
|
+
min-width: 1.75rem;
|
|
262
|
+
padding: 0.05rem 0.35rem;
|
|
263
|
+
border-radius: 999px;
|
|
264
|
+
font-size: 0.875rem;
|
|
265
|
+
font-weight: 600;
|
|
266
|
+
text-align: center;
|
|
267
|
+
color: var(--k-text);
|
|
268
|
+
}
|
|
269
|
+
.k-gantt__wd { font-size: 0.6875rem; color: var(--k-text-faint); }
|
|
270
|
+
.k-gantt__day--weekend .k-gantt__date { color: var(--k-text-faint); }
|
|
271
|
+
.k-gantt__day--today .k-gantt__date { background: var(--k-primary); color: var(--k-on-primary); }
|
|
272
|
+
.k-gantt__day--today .k-gantt__wd { color: var(--k-primary); font-weight: 600; }
|
|
273
|
+
|
|
274
|
+
.k-gantt__label {
|
|
275
|
+
grid-column: 1;
|
|
276
|
+
position: sticky;
|
|
277
|
+
left: 0;
|
|
278
|
+
z-index: 2;
|
|
279
|
+
display: flex;
|
|
280
|
+
align-items: center;
|
|
281
|
+
gap: 0.5rem;
|
|
282
|
+
min-width: 0;
|
|
283
|
+
padding: 0.35rem 0.75rem 0.35rem 2rem;
|
|
284
|
+
background: var(--k-surface);
|
|
285
|
+
border-right: 1px solid var(--k-border);
|
|
286
|
+
border-bottom: 1px solid var(--k-border);
|
|
287
|
+
font-size: 0.8125rem;
|
|
288
|
+
color: var(--k-text);
|
|
289
|
+
}
|
|
290
|
+
.k-gantt__label--muted { color: var(--k-text-faint); }
|
|
291
|
+
.k-gantt__label--group {
|
|
292
|
+
padding-left: 0.5rem;
|
|
293
|
+
border: none;
|
|
294
|
+
border-right: 1px solid var(--k-border);
|
|
295
|
+
border-bottom: 1px solid var(--k-border);
|
|
296
|
+
background: var(--k-surface-muted);
|
|
297
|
+
font: inherit;
|
|
298
|
+
font-weight: 600;
|
|
299
|
+
font-size: 0.875rem;
|
|
300
|
+
text-align: left;
|
|
301
|
+
cursor: pointer;
|
|
302
|
+
}
|
|
303
|
+
.k-gantt__label--group:focus-visible { outline: 2px solid var(--k-primary); outline-offset: -2px; }
|
|
304
|
+
.k-gantt__chevron { color: var(--k-text-muted); flex: none; }
|
|
305
|
+
.k-gantt__group-name { display: inline-flex; align-items: center; gap: 0.5rem; min-width: 0; flex: 1; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
|
|
306
|
+
.k-gantt__count {
|
|
307
|
+
flex: none;
|
|
308
|
+
min-width: 1.4rem;
|
|
309
|
+
padding: 0 0.4rem;
|
|
310
|
+
border-radius: 999px;
|
|
311
|
+
background: var(--k-surface);
|
|
312
|
+
color: var(--k-text-muted);
|
|
313
|
+
font-size: 0.6875rem;
|
|
314
|
+
font-weight: 600;
|
|
315
|
+
text-align: center;
|
|
316
|
+
line-height: 1.4rem;
|
|
317
|
+
}
|
|
318
|
+
.k-gantt__code { flex: none; font-variant-numeric: tabular-nums; font-weight: 600; color: var(--k-text-muted); }
|
|
319
|
+
.k-gantt__title { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
|
|
320
|
+
|
|
321
|
+
.k-gantt__track {
|
|
322
|
+
grid-column: 2 / -1;
|
|
323
|
+
position: relative;
|
|
324
|
+
border-bottom: 1px solid var(--k-border);
|
|
325
|
+
}
|
|
326
|
+
.k-gantt__track--group { background: var(--k-surface-muted); }
|
|
327
|
+
.k-gantt__summary {
|
|
328
|
+
position: absolute;
|
|
329
|
+
z-index: 1;
|
|
330
|
+
top: 50%;
|
|
331
|
+
height: 0.5rem;
|
|
332
|
+
margin-top: -0.25rem;
|
|
333
|
+
border-radius: 2px;
|
|
334
|
+
background: var(--k-text-muted);
|
|
335
|
+
opacity: 0.55;
|
|
336
|
+
}
|
|
337
|
+
.k-gantt__summary::before,
|
|
338
|
+
.k-gantt__summary::after {
|
|
339
|
+
content: '';
|
|
340
|
+
position: absolute;
|
|
341
|
+
bottom: -0.3rem;
|
|
342
|
+
border: 0.3rem solid transparent;
|
|
343
|
+
border-top-color: var(--k-text-muted);
|
|
344
|
+
}
|
|
345
|
+
.k-gantt__summary::before { left: 0; border-left-color: var(--k-text-muted); }
|
|
346
|
+
.k-gantt__summary::after { right: 0; border-right-color: var(--k-text-muted); }
|
|
347
|
+
|
|
348
|
+
.k-gantt__bar-slot { position: absolute; top: 0.3rem; bottom: 0.3rem; z-index: 1; display: flex; }
|
|
349
|
+
.k-gantt__bar-slot :deep(.k-tooltip) { display: flex; flex: 1; min-width: 0; }
|
|
350
|
+
.k-gantt__bar {
|
|
351
|
+
flex: 1;
|
|
352
|
+
min-width: 0;
|
|
353
|
+
display: flex;
|
|
354
|
+
align-items: center;
|
|
355
|
+
padding: 0 0.5rem;
|
|
356
|
+
border: none;
|
|
357
|
+
border-left: 3px solid var(--k-gantt-accent);
|
|
358
|
+
border-radius: var(--k-radius-sm);
|
|
359
|
+
background: var(--k-gantt-fill);
|
|
360
|
+
color: var(--k-gantt-ink);
|
|
361
|
+
font: inherit;
|
|
362
|
+
font-size: 0.75rem;
|
|
363
|
+
font-weight: 600;
|
|
364
|
+
font-variant-numeric: tabular-nums;
|
|
365
|
+
white-space: nowrap;
|
|
366
|
+
overflow: hidden;
|
|
367
|
+
text-overflow: ellipsis;
|
|
368
|
+
text-decoration: none;
|
|
369
|
+
cursor: pointer;
|
|
370
|
+
transition: box-shadow 0.15s, transform 0.15s;
|
|
371
|
+
}
|
|
372
|
+
.k-gantt__bar:hover { box-shadow: var(--k-shadow-sm); transform: translateY(-1px); }
|
|
373
|
+
.k-gantt__bar:focus-visible { outline: 2px solid var(--k-primary); outline-offset: 1px; }
|
|
374
|
+
.k-gantt__bar--cut-start { border-top-left-radius: 0; border-bottom-left-radius: 0; border-left-style: dashed; }
|
|
375
|
+
.k-gantt__bar--cut-end { border-top-right-radius: 0; border-bottom-right-radius: 0; }
|
|
376
|
+
.k-gantt__bar--muted { opacity: 0.5; text-decoration: line-through; }
|
|
377
|
+
.k-gantt__bar--primary { --k-gantt-accent: var(--k-primary); --k-gantt-fill: var(--k-primary-soft); --k-gantt-ink: var(--k-primary); }
|
|
378
|
+
.k-gantt__bar--success { --k-gantt-accent: var(--k-success); --k-gantt-fill: var(--k-success-soft); --k-gantt-ink: var(--k-success-strong); }
|
|
379
|
+
.k-gantt__bar--warning { --k-gantt-accent: var(--k-warning); --k-gantt-fill: var(--k-warning-soft); --k-gantt-ink: var(--k-warning-strong); }
|
|
380
|
+
.k-gantt__bar--danger { --k-gantt-accent: var(--k-danger); --k-gantt-fill: var(--k-danger-soft); --k-gantt-ink: var(--k-danger-strong); }
|
|
381
|
+
.k-gantt__bar--info { --k-gantt-accent: var(--k-info); --k-gantt-fill: var(--k-info-soft); --k-gantt-ink: var(--k-info-strong); }
|
|
382
|
+
.k-gantt__bar--neutral { --k-gantt-accent: var(--k-border-strong); --k-gantt-fill: var(--k-surface-muted); --k-gantt-ink: var(--k-text-muted); }
|
|
383
|
+
.k-gantt__hint { white-space: pre-line; }
|
|
384
|
+
|
|
385
|
+
.k-gantt__mark { pointer-events: none; z-index: 0; }
|
|
386
|
+
.k-gantt__mark--weekend { background: var(--k-surface-muted); }
|
|
387
|
+
.k-gantt__mark--week { box-shadow: inset 1px 0 0 var(--k-border); }
|
|
388
|
+
.k-gantt__today {
|
|
389
|
+
pointer-events: none;
|
|
390
|
+
z-index: 0;
|
|
391
|
+
background: var(--k-primary-soft);
|
|
392
|
+
opacity: 0.6;
|
|
393
|
+
box-shadow: inset 2px 0 0 var(--k-primary);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
.k-gantt__empty {
|
|
397
|
+
grid-column: 1 / -1;
|
|
398
|
+
padding: 2rem;
|
|
399
|
+
text-align: center;
|
|
400
|
+
color: var(--k-text-muted);
|
|
401
|
+
}
|
|
402
|
+
</style>
|
package/src/index.js
CHANGED
|
@@ -29,6 +29,7 @@ import KDropdown from './components/KDropdown.vue'
|
|
|
29
29
|
import KEmpty from './components/KEmpty.vue'
|
|
30
30
|
import KFileUpload from './components/KFileUpload.vue'
|
|
31
31
|
import KFilterBar from './components/KFilterBar.vue'
|
|
32
|
+
import KGantt from './components/KGantt.vue'
|
|
32
33
|
import KIcon from './components/KIcon.vue'
|
|
33
34
|
import KInput from './components/KInput.vue'
|
|
34
35
|
import KMenuItem from './components/KMenuItem.vue'
|
|
@@ -53,7 +54,7 @@ import KTooltip from './components/KTooltip.vue'
|
|
|
53
54
|
|
|
54
55
|
export {
|
|
55
56
|
KAlert, KAvatar, KBadge, KButton, KCard, KCheckbox, KCombobox, KCountdown,
|
|
56
|
-
KDescriptions, KDropdown, KEmpty, KFileUpload, KFilterBar, KIcon,
|
|
57
|
+
KDescriptions, KDropdown, KEmpty, KFileUpload, KFilterBar, KGantt, KIcon,
|
|
57
58
|
KInput, KMenuItem, KModal, KMoney, KPage, KPagination, KProgress,
|
|
58
59
|
KRadioGroup, KSegmented, KSelect, KShell, KStat, KSteps, KSwitch, KTable,
|
|
59
60
|
KTabs, KTextarea, KTimeline, KToast, KTooltip,
|
|
@@ -69,7 +70,7 @@ export * from './format.js'
|
|
|
69
70
|
|
|
70
71
|
const components = {
|
|
71
72
|
KAlert, KAvatar, KBadge, KButton, KCard, KCheckbox, KCombobox, KCountdown,
|
|
72
|
-
KDescriptions, KDropdown, KEmpty, KFileUpload, KFilterBar, KIcon,
|
|
73
|
+
KDescriptions, KDropdown, KEmpty, KFileUpload, KFilterBar, KGantt, KIcon,
|
|
73
74
|
KInput, KMenuItem, KModal, KMoney, KPage, KPagination, KProgress,
|
|
74
75
|
KRadioGroup, KSegmented, KSelect, KShell, KStat, KSteps, KSwitch, KTable,
|
|
75
76
|
KTabs, KTextarea, KTimeline, KToast, KTooltip,
|