innoboxrr-react-datatable 2.3.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -51
- package/index.js +5 -4
- package/package.json +6 -4
- package/src/DataTable.jsx +165 -96
- package/src/components/DataTableComponent.jsx +150 -91
- package/src/components/SelectPaginationComponent.jsx +56 -58
- package/src/table.js +251 -0
- package/src/useDataTable.js +310 -168
- package/src/useTheme.js +17 -0
- package/src/components/ActionListComponent.jsx +0 -61
- package/src/components/DatatableIcon.jsx +0 -39
- package/src/components/DisabledLinkComponent.jsx +0 -23
- package/src/components/IconLinkComponent.jsx +0 -25
- package/src/components/IconRouteComponent.jsx +0 -31
- package/src/components/NavDropdownComponent.jsx +0 -98
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { useSyncExternalStore } from 'react'
|
|
2
|
-
import { Icon } from '@iconify/react'
|
|
3
|
-
import { iconFor, onIconChange } from 'innoboxrr-form-core'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* El icono de una accion de la tabla.
|
|
7
|
-
*
|
|
8
|
-
* Antes cada componente pintaba `<span class="uk-icon" uk-icon="icon: fa-plus">`
|
|
9
|
-
* por su cuenta. Para que ese icono apareciera hacian falta tres dependencias
|
|
10
|
-
* que ningun package.json declaraba: uikit, fontawesome y uikit-custom-icons,
|
|
11
|
-
* que hacia de puente entre las dos.
|
|
12
|
-
*
|
|
13
|
-
* Ahora el nombre se resuelve contra el mapa de innoboxrr-form-core, el mismo
|
|
14
|
-
* que usa la rama Vue.
|
|
15
|
-
*
|
|
16
|
-
* @param {{ icon: string, ratio?: number }} props
|
|
17
|
-
*/
|
|
18
|
-
export default function DatatableIcon({ icon, ratio = 1 }) {
|
|
19
|
-
// useSyncExternalStore es la forma correcta de leer estado que vive fuera
|
|
20
|
-
// de React: un `setIcons()` en caliente repinta lo ya montado.
|
|
21
|
-
const resolved = useSyncExternalStore(
|
|
22
|
-
onIconChange,
|
|
23
|
-
() => iconFor(icon),
|
|
24
|
-
() => iconFor(icon)
|
|
25
|
-
)
|
|
26
|
-
|
|
27
|
-
// `ratio` era el multiplicador de UIkit sobre 16px. Se conserva para no
|
|
28
|
-
// romper a quien ya lo pasa.
|
|
29
|
-
const size = ratio * 16
|
|
30
|
-
|
|
31
|
-
return (
|
|
32
|
-
<Icon
|
|
33
|
-
className="fe-mr-sm"
|
|
34
|
-
icon={resolved}
|
|
35
|
-
width={size}
|
|
36
|
-
height={size}
|
|
37
|
-
aria-hidden="true" />
|
|
38
|
-
)
|
|
39
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import DatatableIcon from './DatatableIcon.jsx'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Gemelo de DisabledLinkComponent.vue: la acción que el usuario no puede
|
|
5
|
-
* ejecutar. Se sigue mostrando, deshabilitada, para que la interfaz no cambie
|
|
6
|
-
* de forma según los permisos.
|
|
7
|
-
*/
|
|
8
|
-
export default function DisabledLinkComponent({ icon = '', text }) {
|
|
9
|
-
const showIcon = icon !== '' && icon != null
|
|
10
|
-
|
|
11
|
-
return (
|
|
12
|
-
<a
|
|
13
|
-
href="#"
|
|
14
|
-
aria-disabled="true"
|
|
15
|
-
className="disabled-link block px-4 py-2 dark:hover:text-white dark:text-slate-400"
|
|
16
|
-
data-tooltip="This action is not authorized"
|
|
17
|
-
data-tooltip-pos="right"
|
|
18
|
-
onClick={(event) => event.preventDefault()}>
|
|
19
|
-
{showIcon ? <DatatableIcon icon={icon} /> : null}
|
|
20
|
-
<span>{text}</span>
|
|
21
|
-
</a>
|
|
22
|
-
)
|
|
23
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import DatatableIcon from './DatatableIcon.jsx'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Gemelo de IconLinkComponent.vue.
|
|
5
|
-
*/
|
|
6
|
-
export default function IconLinkComponent({
|
|
7
|
-
link = '#',
|
|
8
|
-
text,
|
|
9
|
-
icon,
|
|
10
|
-
ratio = 1,
|
|
11
|
-
textClass = '',
|
|
12
|
-
target = '_self',
|
|
13
|
-
onClick,
|
|
14
|
-
}) {
|
|
15
|
-
return (
|
|
16
|
-
<a
|
|
17
|
-
className="block px-4 py-2 dark:hover:text-white dark:text-slate-400"
|
|
18
|
-
href={link}
|
|
19
|
-
target={target}
|
|
20
|
-
onClick={onClick}>
|
|
21
|
-
<DatatableIcon icon={icon} ratio={ratio} />
|
|
22
|
-
<span className={textClass}>{text}</span>
|
|
23
|
-
</a>
|
|
24
|
-
)
|
|
25
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { Link } from 'react-router-dom'
|
|
2
|
-
import { buildPath } from '../routes.js'
|
|
3
|
-
import DatatableIcon from './DatatableIcon.jsx'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Gemelo de IconRouteComponent.vue.
|
|
7
|
-
*
|
|
8
|
-
* vue-router resuelve `{ name, params, query }`; React Router 7 no tiene rutas
|
|
9
|
-
* con nombre, así que la ruta se construye con el mapa que declare la
|
|
10
|
-
* aplicación (ver `registerRoutes` en `src/routes.js`). El contrato del modelo
|
|
11
|
-
* —`params.to.name`— no cambia, que es lo que importa: `models/<entity>/
|
|
12
|
-
* index.js` es el mismo archivo para Vue y para React.
|
|
13
|
-
*/
|
|
14
|
-
export default function IconRouteComponent({
|
|
15
|
-
name,
|
|
16
|
-
params = {},
|
|
17
|
-
query = {},
|
|
18
|
-
text,
|
|
19
|
-
icon,
|
|
20
|
-
ratio = 1,
|
|
21
|
-
textClass = '',
|
|
22
|
-
}) {
|
|
23
|
-
return (
|
|
24
|
-
<Link
|
|
25
|
-
to={buildPath(name, params, query)}
|
|
26
|
-
className="block px-4 py-2 dark:hover:text-white dark:text-slate-400">
|
|
27
|
-
<DatatableIcon icon={icon} ratio={ratio} />
|
|
28
|
-
<span className={textClass}>{text}</span>
|
|
29
|
-
</Link>
|
|
30
|
-
)
|
|
31
|
-
}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useRef } from 'react'
|
|
2
|
-
import { autoUpdate, computePosition, flip, offset as offsetMiddleware, shift } from '@floating-ui/dom'
|
|
3
|
-
import { classFor } from 'innoboxrr-form-core'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Gemelo de NavDropdownComponent.vue.
|
|
7
|
-
*
|
|
8
|
-
* Antes lo movía `uk-dropdown`, y como UIkit sólo procesa el atributo al
|
|
9
|
-
* montarse el nodo, había que avisarle con `UIkit.update()` — con guarda,
|
|
10
|
-
* porque UIkit lo aportaba la aplicación anfitriona sin que nadie lo
|
|
11
|
-
* declarara, y en una prueba no estaba.
|
|
12
|
-
*
|
|
13
|
-
* La mitad de lo que hacía ya lo hace el navegador. La **Popover API** —el
|
|
14
|
-
* atributo `popover` y el `popoverTarget` del botón— aporta de fábrica la capa
|
|
15
|
-
* superior, el cierre al pulsar fuera y el cierre con Escape. Lo único que
|
|
16
|
-
* falta es colocarlo, y de eso se ocupa Floating UI.
|
|
17
|
-
*
|
|
18
|
-
* El botón que lo abre sólo necesita `popoverTarget` con este mismo id:
|
|
19
|
-
*
|
|
20
|
-
* <button popoverTarget={`dropdown_${row.id}`}>…</button>
|
|
21
|
-
* <NavDropdownComponent id={`dropdown_${row.id}`} pos="left" />
|
|
22
|
-
*
|
|
23
|
-
* @param {{ id: string, pos?: string, offset?: number, children?: any }} props
|
|
24
|
-
*/
|
|
25
|
-
export default function NavDropdownComponent({
|
|
26
|
-
id,
|
|
27
|
-
// Se conservan los nombres que usaba UIkit para no romper a quien ya los
|
|
28
|
-
// pasa; `bottom-left` y compañía se traducen a los de Floating UI.
|
|
29
|
-
pos = 'bottom-left',
|
|
30
|
-
offset = 4,
|
|
31
|
-
children,
|
|
32
|
-
}) {
|
|
33
|
-
const panel = useRef(null)
|
|
34
|
-
const stopFollowing = useRef(null)
|
|
35
|
-
|
|
36
|
-
const placement = (() => {
|
|
37
|
-
const [side, align] = pos.split('-')
|
|
38
|
-
|
|
39
|
-
if (! align) {
|
|
40
|
-
return side
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// UIkit dice `bottom-left` para «debajo, alineado a la izquierda»;
|
|
44
|
-
// Floating UI lo llama `bottom-start`.
|
|
45
|
-
return `${side}-${align === 'left' ? 'start' : 'end'}`
|
|
46
|
-
})()
|
|
47
|
-
|
|
48
|
-
const onBeforeToggle = useCallback((event) => {
|
|
49
|
-
if (event.newState !== 'open') {
|
|
50
|
-
stopFollowing.current?.()
|
|
51
|
-
stopFollowing.current = null
|
|
52
|
-
|
|
53
|
-
return
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// El botón es quien apunta a este panel, así que se encuentra por el
|
|
57
|
-
// atributo y no hace falta que nadie lo pase como prop.
|
|
58
|
-
const trigger = document.querySelector(`[popovertarget="${id}"]`)
|
|
59
|
-
|
|
60
|
-
if (! trigger || ! panel.current) {
|
|
61
|
-
return
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// autoUpdate recoloca al hacer scroll o redimensionar: un menú abierto
|
|
65
|
-
// que se queda flotando donde estaba es peor que uno mal colocado.
|
|
66
|
-
stopFollowing.current = autoUpdate(trigger, panel.current, () => {
|
|
67
|
-
computePosition(trigger, panel.current, {
|
|
68
|
-
placement,
|
|
69
|
-
middleware: [
|
|
70
|
-
offsetMiddleware(offset),
|
|
71
|
-
// Si no cabe abajo, se va arriba; si se sale por un lado,
|
|
72
|
-
// se desplaza para caber.
|
|
73
|
-
flip(),
|
|
74
|
-
shift({ padding: 8 }),
|
|
75
|
-
],
|
|
76
|
-
}).then(({ x, y }) => {
|
|
77
|
-
Object.assign(panel.current.style, { left: `${x}px`, top: `${y}px` })
|
|
78
|
-
})
|
|
79
|
-
})
|
|
80
|
-
}, [id, offset, placement])
|
|
81
|
-
|
|
82
|
-
useEffect(() => {
|
|
83
|
-
const node = panel.current
|
|
84
|
-
|
|
85
|
-
node?.addEventListener('beforetoggle', onBeforeToggle)
|
|
86
|
-
|
|
87
|
-
return () => {
|
|
88
|
-
node?.removeEventListener('beforetoggle', onBeforeToggle)
|
|
89
|
-
stopFollowing.current?.()
|
|
90
|
-
}
|
|
91
|
-
}, [onBeforeToggle])
|
|
92
|
-
|
|
93
|
-
return (
|
|
94
|
-
<div ref={panel} id={id} popover="" className={classFor('menu')}>
|
|
95
|
-
<ul className="fe-menu-list">{children}</ul>
|
|
96
|
-
</div>
|
|
97
|
-
)
|
|
98
|
-
}
|