@alaarab/ogrid-vue-radix 2.0.4
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 +103 -0
- package/dist/esm/ColumnChooser/ColumnChooser.module.css +139 -0
- package/dist/esm/ColumnHeaderFilter/ColumnHeaderFilter.module.css +245 -0
- package/dist/esm/DataGridTable/DataGridTable.module.css +63 -0
- package/dist/esm/DataGridTable/MarchingAntsOverlay.js +163 -0
- package/dist/esm/PaginationControls/PaginationControls.module.css +110 -0
- package/dist/esm/index.js +13 -0
- package/dist/types/DataGridTable/MarchingAntsOverlay.d.ts +69 -0
- package/dist/types/index.d.ts +10 -0
- package/package.json +63 -0
package/README.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# @alaarab/ogrid-vue-radix
|
|
2
|
+
|
|
3
|
+
Lightweight Vue 3 data grid with minimal dependencies. Built with Headless UI Vue for a clean, accessible interface.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @alaarab/ogrid-vue-radix
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```vue
|
|
14
|
+
<script setup lang="ts">
|
|
15
|
+
import { ref } from 'vue';
|
|
16
|
+
import { OGrid } from '@alaarab/ogrid-vue-radix';
|
|
17
|
+
|
|
18
|
+
const columns = ref([
|
|
19
|
+
{ columnId: 'id', name: 'ID', type: 'numeric' },
|
|
20
|
+
{ columnId: 'name', name: 'Name', type: 'text', editable: true },
|
|
21
|
+
{ columnId: 'email', name: 'Email', type: 'text', editable: true },
|
|
22
|
+
{ columnId: 'age', name: 'Age', type: 'numeric', editable: true }
|
|
23
|
+
]);
|
|
24
|
+
|
|
25
|
+
const data = ref([
|
|
26
|
+
{ id: 1, name: 'Alice', email: 'alice@example.com', age: 28 },
|
|
27
|
+
{ id: 2, name: 'Bob', email: 'bob@example.com', age: 32 },
|
|
28
|
+
{ id: 3, name: 'Charlie', email: 'charlie@example.com', age: 25 }
|
|
29
|
+
]);
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<template>
|
|
33
|
+
<OGrid
|
|
34
|
+
:columns="columns"
|
|
35
|
+
:data="data"
|
|
36
|
+
:getRowId="(row) => row.id"
|
|
37
|
+
:editable="true"
|
|
38
|
+
:cellSelection="true"
|
|
39
|
+
/>
|
|
40
|
+
</template>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Why OGrid Vue Radix?
|
|
44
|
+
|
|
45
|
+
**Lightweight alternative** to Vuetify and PrimeVue data grids:
|
|
46
|
+
- 📦 Minimal bundle size (~80KB gzipped)
|
|
47
|
+
- 🎨 Built with Headless UI Vue - no large component library required
|
|
48
|
+
- 🚀 Same powerful features as OGrid Vue Vuetify/PrimeVue
|
|
49
|
+
- 💅 Clean, modern design with CSS variables for easy customization
|
|
50
|
+
|
|
51
|
+
## Features
|
|
52
|
+
|
|
53
|
+
- ✅ Sorting, filtering, and pagination
|
|
54
|
+
- ✅ Cell editing (inline text, select, checkbox, date)
|
|
55
|
+
- ✅ Cell and row selection
|
|
56
|
+
- ✅ Column resizing and reordering
|
|
57
|
+
- ✅ Column pinning (sticky left/right)
|
|
58
|
+
- ✅ Virtual scrolling for large datasets
|
|
59
|
+
- ✅ CSV export
|
|
60
|
+
- ✅ Keyboard navigation
|
|
61
|
+
- ✅ Context menu (copy, cut, paste)
|
|
62
|
+
- ✅ Undo/redo
|
|
63
|
+
- ✅ Server-side data sources
|
|
64
|
+
- ✅ Column groups (multi-level headers)
|
|
65
|
+
- ✅ Sidebar panels (columns, filters)
|
|
66
|
+
- ✅ TypeScript support
|
|
67
|
+
|
|
68
|
+
## Documentation
|
|
69
|
+
|
|
70
|
+
Full documentation: **[ogrid.dev](https://ogrid.dev)**
|
|
71
|
+
- [Getting Started](https://ogrid.dev/docs/quick-start)
|
|
72
|
+
- [API Reference](https://ogrid.dev/docs/api/props)
|
|
73
|
+
- [Examples](https://ogrid.dev/docs/examples)
|
|
74
|
+
|
|
75
|
+
## Customization
|
|
76
|
+
|
|
77
|
+
Customize appearance with CSS variables:
|
|
78
|
+
|
|
79
|
+
```css
|
|
80
|
+
:root {
|
|
81
|
+
--ogrid-border: #e0e0e0;
|
|
82
|
+
--ogrid-bg: #ffffff;
|
|
83
|
+
--ogrid-header-bg: #f5f5f5;
|
|
84
|
+
--ogrid-primary: #0066cc;
|
|
85
|
+
--ogrid-active-cell-border: #0066cc;
|
|
86
|
+
--ogrid-selected-cell-bg: #e3f2fd;
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Architecture
|
|
91
|
+
|
|
92
|
+
OGrid Vue Radix is part of the OGrid framework family:
|
|
93
|
+
- **@alaarab/ogrid-core** - Pure TypeScript types and utilities
|
|
94
|
+
- **@alaarab/ogrid-vue** - Vue 3 composables and headless components
|
|
95
|
+
- **@alaarab/ogrid-vue-radix** - Headless UI implementation (this package)
|
|
96
|
+
- **@alaarab/ogrid-vue-vuetify** - Vuetify 3 implementation
|
|
97
|
+
- **@alaarab/ogrid-vue-primevue** - PrimeVue 4 implementation
|
|
98
|
+
|
|
99
|
+
All Vue packages share the same headless core and expose identical APIs.
|
|
100
|
+
|
|
101
|
+
## License
|
|
102
|
+
|
|
103
|
+
MIT © Ala Arab
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
.container {
|
|
2
|
+
position: relative;
|
|
3
|
+
display: inline-flex;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.trigger-button {
|
|
7
|
+
display: inline-flex;
|
|
8
|
+
align-items: center;
|
|
9
|
+
gap: 6px;
|
|
10
|
+
padding: 6px 12px;
|
|
11
|
+
border: 1px solid var(--ogrid-border, #ccc);
|
|
12
|
+
border-radius: 6px;
|
|
13
|
+
background: var(--ogrid-bg, #fff);
|
|
14
|
+
cursor: pointer;
|
|
15
|
+
font-size: 13px;
|
|
16
|
+
font-weight: 600;
|
|
17
|
+
color: var(--ogrid-fg, #333);
|
|
18
|
+
transition: background 0.15s, border-color 0.15s;
|
|
19
|
+
}
|
|
20
|
+
.trigger-button:hover {
|
|
21
|
+
background: var(--ogrid-bg-hover, #f5f5f5);
|
|
22
|
+
border-color: var(--ogrid-border-hover, #999);
|
|
23
|
+
}
|
|
24
|
+
.trigger-button[aria-expanded=true] {
|
|
25
|
+
border-color: var(--ogrid-primary, #0066cc);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.button-icon {
|
|
29
|
+
font-size: 16px;
|
|
30
|
+
line-height: 1;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.chevron {
|
|
34
|
+
font-size: 12px;
|
|
35
|
+
color: var(--ogrid-muted, #888);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.dropdown {
|
|
39
|
+
position: absolute;
|
|
40
|
+
right: 0;
|
|
41
|
+
top: calc(100% + 4px);
|
|
42
|
+
min-width: 220px;
|
|
43
|
+
background: var(--ogrid-bg, #fff);
|
|
44
|
+
border-radius: 6px;
|
|
45
|
+
box-shadow: var(--ogrid-shadow, 0 4px 16px rgba(0, 0, 0, 0.12));
|
|
46
|
+
border: 1px solid var(--ogrid-border, #e0e0e0);
|
|
47
|
+
display: flex;
|
|
48
|
+
flex-direction: column;
|
|
49
|
+
padding: 0;
|
|
50
|
+
z-index: 50;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.header {
|
|
54
|
+
padding: 8px 12px;
|
|
55
|
+
border-bottom: 1px solid var(--ogrid-border, #e5e5e5);
|
|
56
|
+
font-weight: 600;
|
|
57
|
+
font-size: 13px;
|
|
58
|
+
color: var(--ogrid-fg, #333);
|
|
59
|
+
background: var(--ogrid-bg-subtle, #f5f5f5);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.options-list {
|
|
63
|
+
max-height: 320px;
|
|
64
|
+
overflow-y: auto;
|
|
65
|
+
padding: 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.option-item {
|
|
69
|
+
padding: 4px 12px;
|
|
70
|
+
display: flex;
|
|
71
|
+
align-items: center;
|
|
72
|
+
min-height: 32px;
|
|
73
|
+
gap: 8px;
|
|
74
|
+
}
|
|
75
|
+
.option-item:hover {
|
|
76
|
+
background: var(--ogrid-bg-hover, #f0f0f0);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.checkbox-input {
|
|
80
|
+
width: 16px;
|
|
81
|
+
height: 16px;
|
|
82
|
+
border: 1px solid var(--ogrid-border, #888);
|
|
83
|
+
border-radius: 3px;
|
|
84
|
+
background: var(--ogrid-bg, #fff);
|
|
85
|
+
cursor: pointer;
|
|
86
|
+
flex-shrink: 0;
|
|
87
|
+
}
|
|
88
|
+
.checkbox-input:checked {
|
|
89
|
+
background: var(--ogrid-primary, #0066cc);
|
|
90
|
+
border-color: var(--ogrid-primary, #0066cc);
|
|
91
|
+
}
|
|
92
|
+
.checkbox-input:disabled {
|
|
93
|
+
opacity: 0.5;
|
|
94
|
+
cursor: not-allowed;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.checkbox-label {
|
|
98
|
+
cursor: pointer;
|
|
99
|
+
font-size: 13px;
|
|
100
|
+
color: var(--ogrid-fg, #333);
|
|
101
|
+
user-select: none;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.actions {
|
|
105
|
+
display: flex;
|
|
106
|
+
justify-content: flex-end;
|
|
107
|
+
gap: 8px;
|
|
108
|
+
padding: 8px 12px;
|
|
109
|
+
border-top: 1px solid var(--ogrid-border, #e5e5e5);
|
|
110
|
+
background: var(--ogrid-bg-subtle, #f5f5f5);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.clear-button {
|
|
114
|
+
padding: 6px 12px;
|
|
115
|
+
border: 1px solid var(--ogrid-border, #ccc);
|
|
116
|
+
border-radius: 4px;
|
|
117
|
+
background: var(--ogrid-bg, #fff);
|
|
118
|
+
color: var(--ogrid-muted, #666);
|
|
119
|
+
font-size: 12px;
|
|
120
|
+
cursor: pointer;
|
|
121
|
+
}
|
|
122
|
+
.clear-button:hover {
|
|
123
|
+
background: var(--ogrid-bg-hover, #f5f5f5);
|
|
124
|
+
color: var(--ogrid-fg, #333);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.select-all-button {
|
|
128
|
+
padding: 6px 16px;
|
|
129
|
+
border: none;
|
|
130
|
+
border-radius: 4px;
|
|
131
|
+
background: var(--ogrid-primary, #0066cc);
|
|
132
|
+
color: var(--ogrid-primary-fg, #fff);
|
|
133
|
+
font-size: 12px;
|
|
134
|
+
font-weight: 600;
|
|
135
|
+
cursor: pointer;
|
|
136
|
+
}
|
|
137
|
+
.select-all-button:hover {
|
|
138
|
+
background: var(--ogrid-primary-hover, #0052a3);
|
|
139
|
+
}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
.column-header {
|
|
2
|
+
display: flex;
|
|
3
|
+
align-items: center;
|
|
4
|
+
gap: 4px;
|
|
5
|
+
width: 100%;
|
|
6
|
+
max-width: 100%;
|
|
7
|
+
min-width: 0;
|
|
8
|
+
position: relative;
|
|
9
|
+
box-sizing: border-box;
|
|
10
|
+
overflow: hidden;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.header-content {
|
|
14
|
+
display: flex;
|
|
15
|
+
align-items: center;
|
|
16
|
+
flex: 1;
|
|
17
|
+
min-width: 0;
|
|
18
|
+
overflow: hidden;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.column-name {
|
|
22
|
+
display: block;
|
|
23
|
+
min-width: 0;
|
|
24
|
+
max-width: 100%;
|
|
25
|
+
overflow: hidden;
|
|
26
|
+
text-overflow: ellipsis;
|
|
27
|
+
white-space: nowrap;
|
|
28
|
+
font-weight: 600;
|
|
29
|
+
font-size: 14px;
|
|
30
|
+
color: var(--ogrid-fg, #242424);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.header-actions {
|
|
34
|
+
display: flex;
|
|
35
|
+
align-items: center;
|
|
36
|
+
gap: 2px;
|
|
37
|
+
margin-left: auto;
|
|
38
|
+
flex-shrink: 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.sort-icon {
|
|
42
|
+
display: flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
justify-content: center;
|
|
45
|
+
width: 24px;
|
|
46
|
+
height: 24px;
|
|
47
|
+
padding: 4px;
|
|
48
|
+
border: none;
|
|
49
|
+
border-radius: 4px;
|
|
50
|
+
background: transparent;
|
|
51
|
+
color: var(--ogrid-muted, #616161);
|
|
52
|
+
cursor: pointer;
|
|
53
|
+
flex-shrink: 0;
|
|
54
|
+
font-size: 14px;
|
|
55
|
+
}
|
|
56
|
+
.sort-icon:hover {
|
|
57
|
+
background: var(--ogrid-bg-hover, #f5f5f5);
|
|
58
|
+
color: var(--ogrid-fg, #424242);
|
|
59
|
+
}
|
|
60
|
+
.sort-icon.sort-active {
|
|
61
|
+
background: var(--ogrid-bg-selected, #e0e0e0);
|
|
62
|
+
color: var(--ogrid-fg, #333);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.filter-icon {
|
|
66
|
+
display: flex;
|
|
67
|
+
align-items: center;
|
|
68
|
+
justify-content: center;
|
|
69
|
+
width: 24px;
|
|
70
|
+
height: 24px;
|
|
71
|
+
padding: 4px;
|
|
72
|
+
border: none;
|
|
73
|
+
border-radius: 4px;
|
|
74
|
+
background: transparent;
|
|
75
|
+
color: var(--ogrid-muted, #616161);
|
|
76
|
+
cursor: pointer;
|
|
77
|
+
flex-shrink: 0;
|
|
78
|
+
position: relative;
|
|
79
|
+
font-size: 14px;
|
|
80
|
+
}
|
|
81
|
+
.filter-icon:hover {
|
|
82
|
+
background: var(--ogrid-bg-hover, #f5f5f5);
|
|
83
|
+
color: var(--ogrid-fg, #424242);
|
|
84
|
+
}
|
|
85
|
+
.filter-icon.filter-active {
|
|
86
|
+
background: var(--ogrid-bg-selected, #e0e0e0);
|
|
87
|
+
color: var(--ogrid-primary, #0066cc);
|
|
88
|
+
}
|
|
89
|
+
.filter-icon.filter-open {
|
|
90
|
+
background: var(--ogrid-bg-selected, #e8e8e8);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.filter-badge {
|
|
94
|
+
position: absolute;
|
|
95
|
+
top: 2px;
|
|
96
|
+
right: 2px;
|
|
97
|
+
width: 6px;
|
|
98
|
+
height: 6px;
|
|
99
|
+
background: var(--ogrid-primary, #0066cc);
|
|
100
|
+
border-radius: 50%;
|
|
101
|
+
border: 1px solid var(--ogrid-bg, #fff);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.popover-content {
|
|
105
|
+
position: absolute;
|
|
106
|
+
top: calc(100% + 4px);
|
|
107
|
+
right: 0;
|
|
108
|
+
z-index: 1000;
|
|
109
|
+
min-width: 280px;
|
|
110
|
+
max-width: 320px;
|
|
111
|
+
background: var(--ogrid-bg, #fff);
|
|
112
|
+
border: 1px solid var(--ogrid-border, #d1d1d1);
|
|
113
|
+
border-radius: 8px;
|
|
114
|
+
box-shadow: var(--ogrid-shadow, 0 4px 16px rgba(0, 0, 0, 0.12));
|
|
115
|
+
overflow: hidden;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.popover-header {
|
|
119
|
+
padding: 10px 14px;
|
|
120
|
+
font-size: 12px;
|
|
121
|
+
font-weight: 600;
|
|
122
|
+
color: var(--ogrid-muted, #616161);
|
|
123
|
+
border-bottom: 1px solid var(--ogrid-border, #e0e0e0);
|
|
124
|
+
background: var(--ogrid-bg-subtle, #fafafa);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.popover-search {
|
|
128
|
+
padding: 10px 12px;
|
|
129
|
+
border-bottom: 1px solid var(--ogrid-border, #e0e0e0);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.search-input {
|
|
133
|
+
width: 100%;
|
|
134
|
+
padding: 6px 10px;
|
|
135
|
+
border: 1px solid var(--ogrid-border, #d1d1d1);
|
|
136
|
+
border-radius: 4px;
|
|
137
|
+
font-size: 14px;
|
|
138
|
+
box-sizing: border-box;
|
|
139
|
+
}
|
|
140
|
+
.search-input:focus {
|
|
141
|
+
outline: none;
|
|
142
|
+
border-color: var(--ogrid-primary, #0066cc);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.result-count {
|
|
146
|
+
margin-top: 6px;
|
|
147
|
+
font-size: 11px;
|
|
148
|
+
color: var(--ogrid-muted, #616161);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.select-all-row {
|
|
152
|
+
display: flex;
|
|
153
|
+
gap: 8px;
|
|
154
|
+
padding: 6px 12px;
|
|
155
|
+
border-bottom: 1px solid var(--ogrid-border, #e0e0e0);
|
|
156
|
+
background: var(--ogrid-bg-subtle, #fafafa);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.select-all-button {
|
|
160
|
+
background: none;
|
|
161
|
+
border: none;
|
|
162
|
+
color: var(--ogrid-primary, #0066cc);
|
|
163
|
+
font-size: 12px;
|
|
164
|
+
font-weight: 500;
|
|
165
|
+
cursor: pointer;
|
|
166
|
+
padding: 4px 8px;
|
|
167
|
+
border-radius: 4px;
|
|
168
|
+
}
|
|
169
|
+
.select-all-button:hover {
|
|
170
|
+
background: var(--ogrid-bg-hover, #e8f4fc);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.popover-options {
|
|
174
|
+
overflow-y: auto;
|
|
175
|
+
max-height: 250px;
|
|
176
|
+
padding: 6px 0;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.popover-option {
|
|
180
|
+
padding: 4px 12px;
|
|
181
|
+
display: flex;
|
|
182
|
+
align-items: center;
|
|
183
|
+
gap: 8px;
|
|
184
|
+
}
|
|
185
|
+
.popover-option:hover {
|
|
186
|
+
background: var(--ogrid-bg-hover, #f5f5f5);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.filter-checkbox {
|
|
190
|
+
width: 16px;
|
|
191
|
+
height: 16px;
|
|
192
|
+
border: 1px solid var(--ogrid-border, #888);
|
|
193
|
+
border-radius: 3px;
|
|
194
|
+
cursor: pointer;
|
|
195
|
+
flex-shrink: 0;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.popover-actions {
|
|
199
|
+
display: flex;
|
|
200
|
+
justify-content: flex-end;
|
|
201
|
+
gap: 8px;
|
|
202
|
+
padding: 8px 12px;
|
|
203
|
+
border-top: 1px solid var(--ogrid-border, #e0e0e0);
|
|
204
|
+
background: var(--ogrid-bg-subtle, #f5f5f5);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.clear-button {
|
|
208
|
+
padding: 6px 12px;
|
|
209
|
+
border: 1px solid var(--ogrid-border, #ccc);
|
|
210
|
+
border-radius: 4px;
|
|
211
|
+
background: var(--ogrid-bg, #fff);
|
|
212
|
+
color: var(--ogrid-muted, #666);
|
|
213
|
+
font-size: 12px;
|
|
214
|
+
cursor: pointer;
|
|
215
|
+
}
|
|
216
|
+
.clear-button:hover {
|
|
217
|
+
background: var(--ogrid-bg-hover, #f5f5f5);
|
|
218
|
+
color: var(--ogrid-fg, #333);
|
|
219
|
+
}
|
|
220
|
+
.clear-button:disabled {
|
|
221
|
+
opacity: 0.5;
|
|
222
|
+
cursor: not-allowed;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.apply-button {
|
|
226
|
+
padding: 6px 16px;
|
|
227
|
+
border: none;
|
|
228
|
+
border-radius: 4px;
|
|
229
|
+
background: var(--ogrid-primary, #0066cc);
|
|
230
|
+
color: var(--ogrid-primary-fg, #fff);
|
|
231
|
+
font-size: 12px;
|
|
232
|
+
font-weight: 600;
|
|
233
|
+
cursor: pointer;
|
|
234
|
+
}
|
|
235
|
+
.apply-button:hover {
|
|
236
|
+
background: var(--ogrid-primary-hover, #0052a3);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.loading-container,
|
|
240
|
+
.no-results {
|
|
241
|
+
padding: 20px;
|
|
242
|
+
text-align: center;
|
|
243
|
+
font-size: 13px;
|
|
244
|
+
color: var(--ogrid-muted, #666);
|
|
245
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
.ogrid-table-wrapper {
|
|
2
|
+
position: relative;
|
|
3
|
+
flex: 1;
|
|
4
|
+
min-height: 0;
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: column;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.ogrid-loading {
|
|
10
|
+
display: flex;
|
|
11
|
+
align-items: center;
|
|
12
|
+
justify-content: center;
|
|
13
|
+
padding: 48px;
|
|
14
|
+
color: var(--ogrid-muted, #888);
|
|
15
|
+
font-size: 14px;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.ogrid-table-container {
|
|
19
|
+
flex: 1;
|
|
20
|
+
overflow: auto;
|
|
21
|
+
position: relative;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.ogrid-table {
|
|
25
|
+
width: 100%;
|
|
26
|
+
border-collapse: collapse;
|
|
27
|
+
font-size: 14px;
|
|
28
|
+
background: var(--ogrid-bg, #fff);
|
|
29
|
+
}
|
|
30
|
+
.ogrid-table th {
|
|
31
|
+
position: sticky;
|
|
32
|
+
top: 0;
|
|
33
|
+
background: var(--ogrid-header-bg, #f5f5f5);
|
|
34
|
+
border-bottom: 1px solid var(--ogrid-border, #e0e0e0);
|
|
35
|
+
padding: 8px 12px;
|
|
36
|
+
text-align: left;
|
|
37
|
+
font-weight: 600;
|
|
38
|
+
z-index: 2;
|
|
39
|
+
}
|
|
40
|
+
.ogrid-table th.sortable {
|
|
41
|
+
cursor: pointer;
|
|
42
|
+
user-select: none;
|
|
43
|
+
}
|
|
44
|
+
.ogrid-table th.sortable:hover {
|
|
45
|
+
background: var(--ogrid-header-hover-bg, #f0f0f0);
|
|
46
|
+
}
|
|
47
|
+
.ogrid-table td {
|
|
48
|
+
padding: 8px 12px;
|
|
49
|
+
border-bottom: 1px solid var(--ogrid-border, #e0e0e0);
|
|
50
|
+
}
|
|
51
|
+
.ogrid-table td.active-cell {
|
|
52
|
+
outline: 2px solid var(--ogrid-active-border, #0078d4);
|
|
53
|
+
outline-offset: -2px;
|
|
54
|
+
}
|
|
55
|
+
.ogrid-table td.editable {
|
|
56
|
+
cursor: cell;
|
|
57
|
+
}
|
|
58
|
+
.ogrid-table tr:hover {
|
|
59
|
+
background: var(--ogrid-hover-bg, #f9f9f9);
|
|
60
|
+
}
|
|
61
|
+
.ogrid-table tr.selected-row {
|
|
62
|
+
background: var(--ogrid-selected-bg, #e3f2fd);
|
|
63
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MarchingAntsOverlay — Renders range overlays on top of the grid:
|
|
3
|
+
*
|
|
4
|
+
* 1. **Selection range**: solid green border around the current selection
|
|
5
|
+
* 2. **Copy/Cut range**: animated dashed border (marching ants) like Excel
|
|
6
|
+
*
|
|
7
|
+
* Uses SVG rects positioned via cell data-attribute measurements.
|
|
8
|
+
*/
|
|
9
|
+
import { defineComponent, ref, computed, watch, onMounted, onUnmounted, h } from 'vue';
|
|
10
|
+
// Inject the @keyframes rule once into <head> (deduplicates across multiple OGrid instances)
|
|
11
|
+
function ensureKeyframes() {
|
|
12
|
+
if (typeof document === 'undefined')
|
|
13
|
+
return;
|
|
14
|
+
if (document.getElementById('ogrid-marching-ants-keyframes'))
|
|
15
|
+
return;
|
|
16
|
+
const style = document.createElement('style');
|
|
17
|
+
style.id = 'ogrid-marching-ants-keyframes';
|
|
18
|
+
style.textContent =
|
|
19
|
+
'@keyframes ogrid-marching-ants{to{stroke-dashoffset:-8}}';
|
|
20
|
+
document.head.appendChild(style);
|
|
21
|
+
}
|
|
22
|
+
/** Measure the bounding rect of a range within a container. */
|
|
23
|
+
function measureRange(container, range, colOffset) {
|
|
24
|
+
const startGlobalCol = range.startCol + colOffset;
|
|
25
|
+
const endGlobalCol = range.endCol + colOffset;
|
|
26
|
+
const topLeft = container.querySelector(`[data-row-index="${range.startRow}"][data-col-index="${startGlobalCol}"]`);
|
|
27
|
+
const bottomRight = container.querySelector(`[data-row-index="${range.endRow}"][data-col-index="${endGlobalCol}"]`);
|
|
28
|
+
if (!topLeft || !bottomRight)
|
|
29
|
+
return null;
|
|
30
|
+
const cRect = container.getBoundingClientRect();
|
|
31
|
+
const tlRect = topLeft.getBoundingClientRect();
|
|
32
|
+
const brRect = bottomRight.getBoundingClientRect();
|
|
33
|
+
return {
|
|
34
|
+
top: tlRect.top - cRect.top,
|
|
35
|
+
left: tlRect.left - cRect.left,
|
|
36
|
+
width: brRect.right - tlRect.left,
|
|
37
|
+
height: brRect.bottom - tlRect.top,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export const MarchingAntsOverlay = defineComponent({
|
|
41
|
+
name: 'MarchingAntsOverlay',
|
|
42
|
+
props: {
|
|
43
|
+
/** Ref to the positioned container that wraps the table (must have position: relative) */
|
|
44
|
+
containerRef: { type: Object, required: true },
|
|
45
|
+
/** Current selection range — solid green border */
|
|
46
|
+
selectionRange: { type: Object, default: null },
|
|
47
|
+
/** Copy range — animated dashed border */
|
|
48
|
+
copyRange: { type: Object, default: null },
|
|
49
|
+
/** Cut range — animated dashed border */
|
|
50
|
+
cutRange: { type: Object, default: null },
|
|
51
|
+
/** Column offset — 1 when checkbox column is present, else 0 */
|
|
52
|
+
colOffset: { type: Number, required: true },
|
|
53
|
+
},
|
|
54
|
+
setup(props) {
|
|
55
|
+
const selRect = ref(null);
|
|
56
|
+
const clipRect = ref(null);
|
|
57
|
+
let rafId = 0;
|
|
58
|
+
let ro;
|
|
59
|
+
const clipRange = computed(() => props.copyRange ?? props.cutRange);
|
|
60
|
+
const measureAll = () => {
|
|
61
|
+
const container = props.containerRef.value;
|
|
62
|
+
if (!container) {
|
|
63
|
+
selRect.value = null;
|
|
64
|
+
clipRect.value = null;
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
selRect.value = props.selectionRange ? measureRange(container, props.selectionRange, props.colOffset) : null;
|
|
68
|
+
clipRect.value = clipRange.value ? measureRange(container, clipRange.value, props.colOffset) : null;
|
|
69
|
+
};
|
|
70
|
+
// Inject keyframes on mount
|
|
71
|
+
onMounted(() => {
|
|
72
|
+
ensureKeyframes();
|
|
73
|
+
});
|
|
74
|
+
// Measure when any range changes; re-measure on resize
|
|
75
|
+
watch([() => props.selectionRange, clipRange, () => props.containerRef.value], () => {
|
|
76
|
+
if (!props.selectionRange && !clipRange.value) {
|
|
77
|
+
selRect.value = null;
|
|
78
|
+
clipRect.value = null;
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
// Delay one frame so cells are rendered
|
|
82
|
+
rafId = requestAnimationFrame(measureAll);
|
|
83
|
+
const container = props.containerRef.value;
|
|
84
|
+
if (container) {
|
|
85
|
+
ro?.disconnect();
|
|
86
|
+
ro = new ResizeObserver(measureAll);
|
|
87
|
+
ro.observe(container);
|
|
88
|
+
}
|
|
89
|
+
}, { immediate: true });
|
|
90
|
+
onUnmounted(() => {
|
|
91
|
+
cancelAnimationFrame(rafId);
|
|
92
|
+
ro?.disconnect();
|
|
93
|
+
});
|
|
94
|
+
const clipRangeMatchesSel = computed(() => {
|
|
95
|
+
const sel = props.selectionRange;
|
|
96
|
+
const clip = clipRange.value;
|
|
97
|
+
return (sel != null &&
|
|
98
|
+
clip != null &&
|
|
99
|
+
sel.startRow === clip.startRow &&
|
|
100
|
+
sel.startCol === clip.startCol &&
|
|
101
|
+
sel.endRow === clip.endRow &&
|
|
102
|
+
sel.endCol === clip.endCol);
|
|
103
|
+
});
|
|
104
|
+
return () => {
|
|
105
|
+
if (!selRect.value && !clipRect.value)
|
|
106
|
+
return null;
|
|
107
|
+
return h('div', { style: { position: 'relative' } }, [
|
|
108
|
+
// Selection range: solid green border (hidden when clipboard range overlaps)
|
|
109
|
+
selRect.value && !clipRangeMatchesSel.value ? h('svg', {
|
|
110
|
+
style: {
|
|
111
|
+
position: 'absolute',
|
|
112
|
+
top: `${selRect.value.top}px`,
|
|
113
|
+
left: `${selRect.value.left}px`,
|
|
114
|
+
width: `${selRect.value.width}px`,
|
|
115
|
+
height: `${selRect.value.height}px`,
|
|
116
|
+
pointerEvents: 'none',
|
|
117
|
+
zIndex: 4,
|
|
118
|
+
overflow: 'visible',
|
|
119
|
+
},
|
|
120
|
+
'aria-hidden': 'true',
|
|
121
|
+
}, [
|
|
122
|
+
h('rect', {
|
|
123
|
+
x: 1,
|
|
124
|
+
y: 1,
|
|
125
|
+
width: Math.max(0, selRect.value.width - 2),
|
|
126
|
+
height: Math.max(0, selRect.value.height - 2),
|
|
127
|
+
fill: 'none',
|
|
128
|
+
stroke: 'var(--ogrid-selection, #217346)',
|
|
129
|
+
'stroke-width': 2,
|
|
130
|
+
}),
|
|
131
|
+
]) : null,
|
|
132
|
+
// Copy/Cut range: animated marching ants
|
|
133
|
+
clipRect.value ? h('svg', {
|
|
134
|
+
style: {
|
|
135
|
+
position: 'absolute',
|
|
136
|
+
top: `${clipRect.value.top}px`,
|
|
137
|
+
left: `${clipRect.value.left}px`,
|
|
138
|
+
width: `${clipRect.value.width}px`,
|
|
139
|
+
height: `${clipRect.value.height}px`,
|
|
140
|
+
pointerEvents: 'none',
|
|
141
|
+
zIndex: 5,
|
|
142
|
+
overflow: 'visible',
|
|
143
|
+
},
|
|
144
|
+
'aria-hidden': 'true',
|
|
145
|
+
}, [
|
|
146
|
+
h('rect', {
|
|
147
|
+
x: 1,
|
|
148
|
+
y: 1,
|
|
149
|
+
width: Math.max(0, clipRect.value.width - 2),
|
|
150
|
+
height: Math.max(0, clipRect.value.height - 2),
|
|
151
|
+
fill: 'none',
|
|
152
|
+
stroke: 'var(--ogrid-selection, #217346)',
|
|
153
|
+
'stroke-width': 2,
|
|
154
|
+
'stroke-dasharray': '4 4',
|
|
155
|
+
style: {
|
|
156
|
+
animation: 'ogrid-marching-ants 0.5s linear infinite',
|
|
157
|
+
},
|
|
158
|
+
}),
|
|
159
|
+
]) : null,
|
|
160
|
+
]);
|
|
161
|
+
};
|
|
162
|
+
},
|
|
163
|
+
});
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
.pagination {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-wrap: wrap;
|
|
4
|
+
align-items: center;
|
|
5
|
+
justify-content: space-between;
|
|
6
|
+
gap: 14px 24px;
|
|
7
|
+
width: 100%;
|
|
8
|
+
min-width: 0;
|
|
9
|
+
box-sizing: border-box;
|
|
10
|
+
padding: 0;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.pagination-info {
|
|
14
|
+
font-size: 13px;
|
|
15
|
+
color: var(--ogrid-muted, #606060);
|
|
16
|
+
flex-shrink: 0;
|
|
17
|
+
font-variant-numeric: tabular-nums;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.pagination-controls {
|
|
21
|
+
display: flex;
|
|
22
|
+
align-items: center;
|
|
23
|
+
gap: 4px;
|
|
24
|
+
flex-wrap: wrap;
|
|
25
|
+
flex: 1 1 auto;
|
|
26
|
+
justify-content: center;
|
|
27
|
+
min-width: 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.nav-btn {
|
|
31
|
+
min-width: 28px;
|
|
32
|
+
min-height: 28px;
|
|
33
|
+
padding: 4px;
|
|
34
|
+
border: 1px solid var(--ogrid-border, #ccc);
|
|
35
|
+
border-radius: 50%;
|
|
36
|
+
background: var(--ogrid-bg, #fff);
|
|
37
|
+
color: var(--ogrid-fg, #333);
|
|
38
|
+
cursor: pointer;
|
|
39
|
+
display: inline-flex;
|
|
40
|
+
align-items: center;
|
|
41
|
+
justify-content: center;
|
|
42
|
+
font-size: 14px;
|
|
43
|
+
}
|
|
44
|
+
.nav-btn:hover:not(:disabled) {
|
|
45
|
+
background: var(--ogrid-bg-hover, #f5f5f5);
|
|
46
|
+
border-color: var(--ogrid-border-hover, #999);
|
|
47
|
+
}
|
|
48
|
+
.nav-btn:disabled {
|
|
49
|
+
opacity: 0.5;
|
|
50
|
+
cursor: not-allowed;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.page-numbers {
|
|
54
|
+
display: inline-flex;
|
|
55
|
+
align-items: center;
|
|
56
|
+
gap: 4px;
|
|
57
|
+
margin: 0 8px;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.page-btn {
|
|
61
|
+
min-width: 28px;
|
|
62
|
+
min-height: 28px;
|
|
63
|
+
padding: 4px 8px;
|
|
64
|
+
border: 1px solid var(--ogrid-border, #ccc);
|
|
65
|
+
border-radius: 4px;
|
|
66
|
+
background: var(--ogrid-bg, #fff);
|
|
67
|
+
color: var(--ogrid-fg, #333);
|
|
68
|
+
cursor: pointer;
|
|
69
|
+
font-size: 13px;
|
|
70
|
+
font-variant-numeric: tabular-nums;
|
|
71
|
+
}
|
|
72
|
+
.page-btn:hover {
|
|
73
|
+
background: var(--ogrid-bg-hover, #f5f5f5);
|
|
74
|
+
}
|
|
75
|
+
.page-btn.active {
|
|
76
|
+
background: var(--ogrid-primary, #0066cc);
|
|
77
|
+
border-color: var(--ogrid-primary, #0066cc);
|
|
78
|
+
color: var(--ogrid-primary-fg, #fff);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.ellipsis {
|
|
82
|
+
display: inline-flex;
|
|
83
|
+
align-items: center;
|
|
84
|
+
justify-content: center;
|
|
85
|
+
min-width: 24px;
|
|
86
|
+
font-size: 12px;
|
|
87
|
+
color: var(--ogrid-muted, #888);
|
|
88
|
+
user-select: none;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.page-size-selector {
|
|
92
|
+
display: inline-flex;
|
|
93
|
+
align-items: center;
|
|
94
|
+
gap: 8px;
|
|
95
|
+
flex-shrink: 0;
|
|
96
|
+
}
|
|
97
|
+
.page-size-selector .page-size-label {
|
|
98
|
+
font-size: 13px;
|
|
99
|
+
color: var(--ogrid-muted, #606060);
|
|
100
|
+
user-select: none;
|
|
101
|
+
white-space: nowrap;
|
|
102
|
+
}
|
|
103
|
+
.page-size-selector .page-size-select {
|
|
104
|
+
min-width: 72px;
|
|
105
|
+
padding: 4px 8px;
|
|
106
|
+
border: 1px solid var(--ogrid-border, #ccc);
|
|
107
|
+
border-radius: 4px;
|
|
108
|
+
background: var(--ogrid-bg, #fff);
|
|
109
|
+
font-size: 13px;
|
|
110
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Main components
|
|
2
|
+
export { default as OGrid } from './OGrid/OGrid.vue';
|
|
3
|
+
export { default as DataGridTable } from './DataGridTable/DataGridTable.vue';
|
|
4
|
+
export { default as ColumnChooser } from './ColumnChooser/ColumnChooser.vue';
|
|
5
|
+
export { default as ColumnHeaderFilter } from './ColumnHeaderFilter/ColumnHeaderFilter.vue';
|
|
6
|
+
export { default as PaginationControls } from './PaginationControls/PaginationControls.vue';
|
|
7
|
+
// DataGridTable sub-components
|
|
8
|
+
export { default as StatusBar } from './DataGridTable/StatusBar.vue';
|
|
9
|
+
export { default as GridContextMenu } from './DataGridTable/GridContextMenu.vue';
|
|
10
|
+
export { default as MarchingAntsOverlay } from './DataGridTable/MarchingAntsOverlay.vue';
|
|
11
|
+
export { default as InlineCellEditor } from './DataGridTable/InlineCellEditor.vue';
|
|
12
|
+
// Re-export everything from @alaarab/ogrid-vue (which re-exports from @alaarab/ogrid-core)
|
|
13
|
+
export * from '@alaarab/ogrid-vue';
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MarchingAntsOverlay — Renders range overlays on top of the grid:
|
|
3
|
+
*
|
|
4
|
+
* 1. **Selection range**: solid green border around the current selection
|
|
5
|
+
* 2. **Copy/Cut range**: animated dashed border (marching ants) like Excel
|
|
6
|
+
*
|
|
7
|
+
* Uses SVG rects positioned via cell data-attribute measurements.
|
|
8
|
+
*/
|
|
9
|
+
import { type PropType, type Ref } from 'vue';
|
|
10
|
+
import type { ISelectionRange } from '@alaarab/ogrid-vue';
|
|
11
|
+
export declare const MarchingAntsOverlay: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
12
|
+
/** Ref to the positioned container that wraps the table (must have position: relative) */
|
|
13
|
+
containerRef: {
|
|
14
|
+
type: PropType<Ref<HTMLElement | null>>;
|
|
15
|
+
required: true;
|
|
16
|
+
};
|
|
17
|
+
/** Current selection range — solid green border */
|
|
18
|
+
selectionRange: {
|
|
19
|
+
type: PropType<ISelectionRange | null>;
|
|
20
|
+
default: null;
|
|
21
|
+
};
|
|
22
|
+
/** Copy range — animated dashed border */
|
|
23
|
+
copyRange: {
|
|
24
|
+
type: PropType<ISelectionRange | null>;
|
|
25
|
+
default: null;
|
|
26
|
+
};
|
|
27
|
+
/** Cut range — animated dashed border */
|
|
28
|
+
cutRange: {
|
|
29
|
+
type: PropType<ISelectionRange | null>;
|
|
30
|
+
default: null;
|
|
31
|
+
};
|
|
32
|
+
/** Column offset — 1 when checkbox column is present, else 0 */
|
|
33
|
+
colOffset: {
|
|
34
|
+
type: NumberConstructor;
|
|
35
|
+
required: true;
|
|
36
|
+
};
|
|
37
|
+
}>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
38
|
+
[key: string]: any;
|
|
39
|
+
}> | null, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
|
40
|
+
/** Ref to the positioned container that wraps the table (must have position: relative) */
|
|
41
|
+
containerRef: {
|
|
42
|
+
type: PropType<Ref<HTMLElement | null>>;
|
|
43
|
+
required: true;
|
|
44
|
+
};
|
|
45
|
+
/** Current selection range — solid green border */
|
|
46
|
+
selectionRange: {
|
|
47
|
+
type: PropType<ISelectionRange | null>;
|
|
48
|
+
default: null;
|
|
49
|
+
};
|
|
50
|
+
/** Copy range — animated dashed border */
|
|
51
|
+
copyRange: {
|
|
52
|
+
type: PropType<ISelectionRange | null>;
|
|
53
|
+
default: null;
|
|
54
|
+
};
|
|
55
|
+
/** Cut range — animated dashed border */
|
|
56
|
+
cutRange: {
|
|
57
|
+
type: PropType<ISelectionRange | null>;
|
|
58
|
+
default: null;
|
|
59
|
+
};
|
|
60
|
+
/** Column offset — 1 when checkbox column is present, else 0 */
|
|
61
|
+
colOffset: {
|
|
62
|
+
type: NumberConstructor;
|
|
63
|
+
required: true;
|
|
64
|
+
};
|
|
65
|
+
}>> & Readonly<{}>, {
|
|
66
|
+
selectionRange: ISelectionRange | null;
|
|
67
|
+
copyRange: ISelectionRange | null;
|
|
68
|
+
cutRange: ISelectionRange | null;
|
|
69
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { default as OGrid } from './OGrid/OGrid.vue';
|
|
2
|
+
export { default as DataGridTable } from './DataGridTable/DataGridTable.vue';
|
|
3
|
+
export { default as ColumnChooser } from './ColumnChooser/ColumnChooser.vue';
|
|
4
|
+
export { default as ColumnHeaderFilter } from './ColumnHeaderFilter/ColumnHeaderFilter.vue';
|
|
5
|
+
export { default as PaginationControls } from './PaginationControls/PaginationControls.vue';
|
|
6
|
+
export { default as StatusBar } from './DataGridTable/StatusBar.vue';
|
|
7
|
+
export { default as GridContextMenu } from './DataGridTable/GridContextMenu.vue';
|
|
8
|
+
export { default as MarchingAntsOverlay } from './DataGridTable/MarchingAntsOverlay.vue';
|
|
9
|
+
export { default as InlineCellEditor } from './DataGridTable/InlineCellEditor.vue';
|
|
10
|
+
export * from '@alaarab/ogrid-vue';
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@alaarab/ogrid-vue-radix",
|
|
3
|
+
"version": "2.0.4",
|
|
4
|
+
"description": "OGrid Vue Radix – Lightweight data grid with sorting, filtering, pagination, column chooser, and CSV export. Built with Headless UI Vue.",
|
|
5
|
+
"main": "dist/esm/index.js",
|
|
6
|
+
"module": "dist/esm/index.js",
|
|
7
|
+
"types": "dist/types/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/types/index.d.ts",
|
|
11
|
+
"import": "./dist/esm/index.js",
|
|
12
|
+
"require": "./dist/esm/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "rimraf dist && tsc -p tsconfig.build.json && node scripts/compile-styles.js",
|
|
17
|
+
"test": "jest"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"ogrid",
|
|
21
|
+
"headlessui",
|
|
22
|
+
"vue",
|
|
23
|
+
"datatable",
|
|
24
|
+
"typescript",
|
|
25
|
+
"grid",
|
|
26
|
+
"headless"
|
|
27
|
+
],
|
|
28
|
+
"author": "Ala Arab",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"README.md",
|
|
33
|
+
"LICENSE"
|
|
34
|
+
],
|
|
35
|
+
"sideEffects": [
|
|
36
|
+
"**/*.css"
|
|
37
|
+
],
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=18"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@alaarab/ogrid-vue": "2.0.4",
|
|
43
|
+
"@headlessui/vue": "^1.7.0"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"vue": "^3.3.0"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"vue": "^3.5.28",
|
|
50
|
+
"@vitejs/plugin-vue": "^6.0.4",
|
|
51
|
+
"vite": "^7.0.0",
|
|
52
|
+
"vite-plugin-dts": "^4.5.0",
|
|
53
|
+
"typescript": "^5.7.3",
|
|
54
|
+
"sass": "^1.83.4",
|
|
55
|
+
"@types/jest": "^29.5.0",
|
|
56
|
+
"jest": "^29.7.0",
|
|
57
|
+
"jest-environment-jsdom": "^29.7.0",
|
|
58
|
+
"ts-jest": "^29.2.0"
|
|
59
|
+
},
|
|
60
|
+
"publishConfig": {
|
|
61
|
+
"access": "public"
|
|
62
|
+
}
|
|
63
|
+
}
|