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.
@@ -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
- }