bubble-toolbar 0.1.0 → 1.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/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ # Changelog
2
+
3
+ Release entries are generated automatically by semantic-release from Conventional Commits.
package/README.md CHANGED
@@ -1,195 +1,315 @@
1
1
  # 🫧 Bubble Toolbar
2
2
 
3
- > Componente de barra flotante responsiva con efecto **blurred glass** (glassmorphism) para **React 18 y 19** con **TypeScript**, organizado por grupos y con morfología adaptativa inteligente (círculo ↔ cápsula).
3
+ [![npm version](https://img.shields.io/npm/v/bubble-toolbar)](https://www.npmjs.com/package/bubble-toolbar)
4
+ [![React](https://img.shields.io/badge/React-18%20%7C%2019-61DAFB?logo=react&logoColor=white)](https://react.dev/)
5
+ [![TypeScript](https://img.shields.io/badge/TypeScript-strict-3178C6?logo=typescript&logoColor=white)](./tsconfig.json)
6
+ [![Storybook](https://img.shields.io/badge/Storybook-10-FF4785?logo=storybook&logoColor=white)](./.storybook/main.ts)
7
+ [![ESLint](https://img.shields.io/badge/ESLint-10-4B32C3?logo=eslint&logoColor=white)](./eslint.config.mjs)
8
+ [![Biome](https://img.shields.io/badge/Biome-2-60A5FA?logo=biome&logoColor=white)](./biome.json)
9
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green)](./LICENSE)
4
10
 
5
- ![Bubble Toolbar Morphology](https://raw.githubusercontent.com/ElJijuna/bubble-toolbar/main/assets/banner.png)
11
+ A responsive floating toolbar with a frosted glass effect for React 18 and 19. Built with TypeScript, it arranges content into groups that adapt from circles to pills based on their children.
6
12
 
7
- ---
13
+ ## Features
8
14
 
9
- ## ✨ Características
15
+ - **React 18 and 19 support:** typed components with forwarded refs.
16
+ - **Group-based composition:** pass arrays of nodes or use `BubbleToolbar.Group` children.
17
+ - **Adaptive shapes:** a single child creates a circle; multiple children create a pill.
18
+ - **Frosted glass:** background blur, subtle borders, soft shadows, and glass, light, dark, or automatic themes.
19
+ - **Responsive layout:** fixed or relative positioning, automatic centering, and horizontal scrolling on smaller screens.
20
+ - **Tests and examples:** Jest and React Testing Library tests, plus interactive Storybook stories.
10
21
 
11
- - ⚛️ **Compatibilidad Total**: Diseñado y probado con **React 18** y **React 19** usando `forwardRef` y tipado estricto en TypeScript.
12
- - 🔮 **Definición por Grupos**: Define la barra pasando arreglos de nodos o mediante sintaxis de componentes compuestos (`BubbleToolbar.Group`).
13
- - 🔘 **Morfología Adaptativa Dinámica**:
14
- - **1 solo nodo** $\rightarrow$ se renderiza como un **círculo** perfecto (`aspect-ratio: 1/1`).
15
- - **Más de 1 nodo** $\rightarrow$ se convierte en un **rectángulo redondeado (cápsula / pill)**, respetando la curvatura en los extremos para mantener el equilibrio visual exacto con las burbujas circulares.
16
- - 🪟 **Blurred Glass (Frosted Glassmorphism)**: Efecto translúcido con desenfoque de fondo (`backdrop-filter: blur(16px)`), bordes frosted, sombras suaves y soporte para temas claro, oscuro o automático.
17
- - 📱 **Flotante y Responsivo**: Posicionamiento fijo o relativo, centrado automático y desplazamiento horizontal suave sin barras de scroll invasivas en pantallas móviles.
18
- - 🧪 **100% Testeado con Jest**: Suite integral de pruebas unitarias y de integración con `@testing-library/react`.
19
-
20
- ---
21
-
22
- ## 📦 Instalación
22
+ ## Installation
23
23
 
24
24
  ```bash
25
25
  npm install bubble-toolbar
26
- # o con yarn / pnpm / bun
26
+ # Or use pnpm:
27
27
  pnpm add bubble-toolbar
28
28
  ```
29
29
 
30
- ---
30
+ ## Quick start
31
31
 
32
- ## 🚀 Uso Rápido
32
+ ### Group arrays
33
33
 
34
- ### Ejemplo con definición por grupos (prop `groups`)
34
+ ```tsx
35
+ import { BubbleArea, BubbleIconButton, BubbleToolbar } from 'bubble-toolbar';
36
+ import 'bubble-toolbar/styles.css';
37
+
38
+ const App = () => (
39
+ <BubbleToolbar
40
+ position="bottom"
41
+ theme="glass"
42
+ groups={[
43
+ // One child: circle.
44
+ [<BubbleIconButton key="logo" name="logo" />],
45
+
46
+ // Multiple children: pill.
47
+ [
48
+ <BubbleIconButton key="search" name="search" />,
49
+ <BubbleArea key="area">
50
+ <input aria-label="Search" placeholder="Search or type /" />
51
+ </BubbleArea>,
52
+ ],
53
+
54
+ // Each group below becomes a circle.
55
+ [<BubbleIconButton key="theme" name="theme" />],
56
+ [<BubbleIconButton key="chat" name="chat" />],
57
+ [<BubbleIconButton key="notifications" name="notifications" badge={3} />],
58
+ ]}
59
+ />
60
+ );
61
+ ```
35
62
 
36
- Tal como en tu especificación:
63
+ ### Compound components
37
64
 
38
65
  ```tsx
39
- import React from 'react';
40
- import { BubbleToolbar, BubbleIconButton, BubbleArea } from 'bubble-toolbar';
41
- import 'bubble-toolbar/styles.css'; // Opcional, los estilos inline glass ya vienen incluidos
66
+ import { BubbleArea, BubbleIconButton, BubbleToolbar } from 'bubble-toolbar';
67
+ import 'bubble-toolbar/styles.css';
68
+
69
+ const FloatingMenu = () => (
70
+ <BubbleToolbar position="bottom" theme="glass">
71
+ <BubbleToolbar.Group>
72
+ <BubbleIconButton name="logo" />
73
+ </BubbleToolbar.Group>
74
+
75
+ <BubbleToolbar.Group>
76
+ <BubbleIconButton name="search" />
77
+ <BubbleArea>
78
+ <input aria-label="Search" placeholder="Search…" />
79
+ </BubbleArea>
80
+ </BubbleToolbar.Group>
81
+
82
+ <BubbleToolbar.Group>
83
+ <BubbleIconButton name="theme" />
84
+ </BubbleToolbar.Group>
85
+ </BubbleToolbar>
86
+ );
87
+ ```
42
88
 
43
- function App() {
44
- return (
45
- <BubbleToolbar
46
- position="bottom"
47
- theme="glass"
48
- groups={[
49
- // Grupo 1: 1 solo nodo => Círculo
50
- [<Logo key="logo" />],
51
-
52
- // Grupo 2: Múltiples nodos => Rectángulo redondeado (Pill)
53
- [
54
- <BubbleIconButton key="search" name="search" />,
55
- <BubbleArea key="area">
56
- <input placeholder="Buscar o escribir /" />
57
- </BubbleArea>,
58
- ],
59
-
60
- // Grupos 3, 4 y 5: 1 solo nodo cada uno => Círculos
61
- [<BubbleIconButton key="theme" name="theme" />],
62
- [<BubbleIconButton key="chat" name="chat" />],
63
- [<BubbleIconButton key="notif" name="notifications" badge={3} />],
64
- ]}
65
- />
66
- );
67
- }
89
+ The components include inline glass styles. Import the stylesheet for additional styling, including automatic dark theme support and focus indicators.
90
+
91
+ ## Async activity
92
+
93
+ Import `bubble-toolbar/styles.css` to enable the rotating border and glow. Activity is indeterminate: it does not disable controls, cancel tasks, or display success or error states.
94
+
95
+ ### External control
96
+
97
+ Set `busy` on the toolbar to illuminate every group, or on a group to illuminate only that bubble:
98
+
99
+ ```tsx
100
+ <BubbleToolbar busy={isSaving}>
101
+ <BubbleToolbar.Group>
102
+ <BubbleIconButton name="logo" />
103
+ </BubbleToolbar.Group>
104
+ <BubbleToolbar.Group busy={isSearching}>
105
+ <BubbleIconButton name="search" />
106
+ <BubbleArea>Search results</BubbleArea>
107
+ </BubbleToolbar.Group>
108
+ </BubbleToolbar>
68
109
  ```
69
110
 
70
- ### Ejemplo con Componentes Compuestos (`BubbleToolbar.Group`)
111
+ ### Track tasks from inside a group
112
+
113
+ `useBubbleActivity()` returns `{ isBusy, run }`. It targets the nearest group, including groups created through the `groups` prop. Calling it outside a group throws a descriptive error.
71
114
 
72
115
  ```tsx
73
- import { BubbleToolbar, BubbleIconButton, BubbleArea } from 'bubble-toolbar';
116
+ import { useState } from 'react';
117
+ import { BubbleIconButton, BubbleToolbar, useBubbleActivity } from 'bubble-toolbar';
118
+ import 'bubble-toolbar/styles.css';
119
+
120
+ const SaveAction = ({ save }: { save: () => Promise<void> }) => {
121
+ const { isBusy, run } = useBubbleActivity();
122
+ const [error, setError] = useState<string | null>(null);
74
123
 
75
- function FloatingMenu() {
76
124
  return (
77
- <BubbleToolbar position="bottom" theme="glass">
78
- {/* 1 nodo: Círculo */}
79
- <BubbleToolbar.Group>
80
- <Logo />
81
- </BubbleToolbar.Group>
82
-
83
- {/* 2 nodos: Rectángulo redondeado (Cápsula) */}
84
- <BubbleToolbar.Group>
85
- <BubbleIconButton name="search" />
86
- <BubbleArea>
87
- <input placeholder="Buscar..." />
88
- </BubbleArea>
89
- </BubbleToolbar.Group>
90
-
91
- {/* 1 nodo: Círculo */}
92
- <BubbleToolbar.Group>
93
- <BubbleIconButton name="theme" />
94
- </BubbleToolbar.Group>
95
- </BubbleToolbar>
125
+ <BubbleIconButton
126
+ name="chat"
127
+ tooltip={error ?? (isBusy ? 'Saving…' : 'Save')}
128
+ onClick={async () => {
129
+ setError(null);
130
+ try {
131
+ await run(save);
132
+ } catch {
133
+ setError('Save failed. Try again.');
134
+ }
135
+ }}
136
+ />
96
137
  );
97
- }
138
+ };
139
+
140
+ // Render SaveAction inside a group:
141
+ // <BubbleToolbar.Group><SaveAction save={saveChanges} /></BubbleToolbar.Group>
98
142
  ```
99
143
 
100
- ---
144
+ `run<T>(task: () => Promise<T>): Promise<T>` returns the task's result and propagates synchronous exceptions and rejected promises. Handle errors in your application. Concurrent tasks keep the group busy until the last task settles. Unmounting the group does not cancel the consumer's task.
101
145
 
102
- ## 📐 Morfología Visual
146
+ A group is busy when **any** of the following is true: the toolbar's `busy` prop, the group's `busy` prop, or a pending local task. Passing `busy={false}` does not hide activity from another source. Each group exposes its effective state through `aria-busy`.
103
147
 
104
- | Contenido del Grupo | Forma Resultante | Comportamiento |
105
- | :--- | :---: | :--- |
106
- | **1 Nodo** (`[<Logo />]`) | 🟡 **Círculo** | Ancho y alto fijados a `--bubble-size` (por defecto `48px`). `aspect-ratio: 1 / 1`. Contenido centrado. |
107
- | **> 1 Nodo** (`[<Search />, <Area />]`) | 💊 **Cápsula / Pill** | Altura fijada a `--bubble-size`, ancho dinámico según contenido con padding lateral. Los extremos redondeados coinciden exactamente con el diámetro de las burbujas circulares adyacentes para una armonía visual simétrica. |
148
+ Both toolbar and group accept these props; group appearance overrides the toolbar defaults:
108
149
 
109
- ---
150
+ | Prop | Type | Default | Description |
151
+ | :--- | :--- | :--- | :--- |
152
+ | `busy` | `boolean` | `false` | Adds externally managed activity. |
153
+ | `activityColors` | `readonly [string, string, string]` | `['#22d3ee', '#8b5cf6', '#ec4899']` | Three CSS colors for the gradient. |
154
+ | `activityDuration` | `number` | `2.4` | Seconds per rotation; non-positive or non-finite values fall back to 2.4. |
110
155
 
111
- ## 🛠️ API Reference
156
+ The effect follows circular and pill shapes without changing their dimensions. With `prefers-reduced-motion: reduce`, the border remains visible and static. Browsers without animated custom property support retain a static gradient; browsers without CSS masking retain a static colored border.
157
+
158
+ ## Adaptive shapes
159
+
160
+ | Group content | Shape | Behavior |
161
+ | :--- | :--- | :--- |
162
+ | One child | Circle | Equal width and height, with centered content. The default diameter is 48px. |
163
+ | Multiple children | Pill | Fixed height, content-based width, horizontal spacing, and rounded ends. |
164
+
165
+ Use the `shape` prop on a group to override its automatic shape.
166
+
167
+ ## API reference
112
168
 
113
169
  ### `<BubbleToolbar />`
114
170
 
115
- | Prop | Tipo | Por defecto | Descripción |
116
- | :--- | :--- | :---: | :--- |
117
- | `groups` | `ReactNode[][]` | `undefined` | Arreglo de arreglos con los nodos agrupados. |
118
- | `children` | `ReactNode` | `undefined` | Elementos JSX hijos (ej. `<BubbleToolbar.Group>`). |
119
- | `position` | `'bottom' \| 'top' \| 'static' \| 'custom'` | `'bottom'` | Posicionamiento de la barra en pantalla. |
120
- | `theme` | `'glass' \| 'light' \| 'dark' \| 'auto'` | `'glass'` | Estilo visual y translucidez. |
121
- | `size` | `'sm' \| 'md' \| 'lg' \| number` | `'md'` | Altura de la barra / diámetro de los círculos (40px, 48px, 56px o px numérico). |
122
- | `blurred` | `boolean` | `true` | Activa el efecto frosted glass (`backdrop-filter: blur`). |
123
- | `blurAmount`| `number \| string` | `16` | Cantidad de desenfoque en píxeles. |
124
- | `groupGap` | `number \| string` | `8` | Espacio entre cada burbuja/grupo. |
125
- | `itemGap` | `number \| string` | `8` | Espacio entre elementos dentro de un grupo cápsula. |
126
- | `bordered` | `boolean` | `true` | Muestra bordes sutiles con brillo frosted. |
127
- | `shadow` | `boolean \| string` | `true` | Sombra flotante proyectada. |
128
-
129
- ### `<BubbleGroup />` (o `<BubbleToolbar.Group />`)
130
-
131
- | Prop | Tipo | Por defecto | Descripción |
132
- | :--- | :--- | :---: | :--- |
133
- | `children` | `ReactNode` | `undefined` | Nodos que contiene el grupo. |
134
- | `shape` | `'circle' \| 'pill'` | Automático | Fuerza una forma específica omitiendo el cálculo automático por cantidad de nodos. |
135
- | `size` | `'sm' \| 'md' \| 'lg' \| number` | Heredado | Permite sobrescribir el tamaño para este grupo. |
136
- | `itemGap` | `number \| string` | Heredado | Espaciado entre elementos del grupo. |
171
+ | Prop | Type | Default | Description |
172
+ | :--- | :--- | :--- | :--- |
173
+ | `groups` | `ReactNode[][]` | `undefined` | Arrays of grouped nodes. |
174
+ | `children` | `ReactNode` | `undefined` | Children such as `<BubbleToolbar.Group>`. |
175
+ | `position` | `'bottom' \| 'top' \| 'static' \| 'custom'` | `'bottom'` | Toolbar placement. Use `style` to set custom positioning. |
176
+ | `theme` | `'glass' \| 'light' \| 'dark' \| 'auto'` | `'glass'` | Visual theme. |
177
+ | `size` | `'sm' \| 'md' \| 'lg' \| number` | `'md'` | Group height and circle diameter: 40px, 48px, 56px, or a custom pixel value. |
178
+ | `blurred` | `boolean` | `true` | Enables background blur. |
179
+ | `blurAmount` | `number \| string` | `16` | Blur radius in pixels or a CSS length. |
180
+ | `groupGap` | `number \| string` | `8` | Space between groups, in pixels or a CSS length. |
181
+ | `itemGap` | `number \| string` | `8` | Space between children within a pill. |
182
+ | `bordered` | `boolean` | `true` | Shows subtle borders. |
183
+ | `shadow` | `boolean \| string` | `true` | Enables the default shadow or supplies a custom CSS shadow. |
184
+ | `ariaLabel` | `string` | `'Toolbar'` | Accessible toolbar label. |
185
+
186
+ ### `<BubbleGroup />` / `<BubbleToolbar.Group />`
187
+
188
+ | Prop | Type | Default | Description |
189
+ | :--- | :--- | :--- | :--- |
190
+ | `children` | `ReactNode` | `undefined` | Group content. |
191
+ | `shape` | `'circle' \| 'pill'` | Automatic | Overrides the shape inferred from the child count. |
192
+ | `size` | `'sm' \| 'md' \| 'lg' \| number` | Inherited | Overrides the toolbar's group size. |
193
+ | `itemGap` | `number \| string` | Inherited | Overrides spacing between children. |
137
194
 
138
195
  ### `<BubbleIconButton />`
139
196
 
140
- | Prop | Tipo | Por defecto | Descripción |
141
- | :--- | :--- | :---: | :--- |
142
- | `name` | `'search' \| 'theme' \| 'chat' \| 'notifications' \| 'logo' \| 'close'` | `undefined` | Nombre del icono vectorial SVG pre-integrado. |
143
- | `icon` | `ReactNode` | `undefined` | Icono SVG personalizado. |
144
- | `badge` | `ReactNode \| number \| string` | `undefined` | Indicador o contador de notificación. |
145
- | `active` | `boolean` | `false` | Estado activo / seleccionado. |
146
- | `tooltip` | `string` | `undefined` | Texto emergente y accesible. |
147
-
148
- ---
197
+ | Prop | Type | Default | Description |
198
+ | :--- | :--- | :--- | :--- |
199
+ | `name` | `string` | `undefined` | Built-in icon: `search`, `theme`, `chat`, `notifications`, `logo`, or `close`. |
200
+ | `icon` | `ReactNode` | `undefined` | Custom icon content. |
201
+ | `badge` | `ReactNode` | `undefined` | Notification indicator or count. |
202
+ | `active` | `boolean` | `false` | Applies active styling. |
203
+ | `tooltip` | `string` | `undefined` | Tooltip and fallback accessible label. |
204
+ | `ariaLabel` | `string` | Derived | Explicit accessible label; falls back to the tooltip or icon name. |
149
205
 
150
- ## 🧪 Pruebas con Jest
206
+ ### `<BubbleArea />`
151
207
 
152
- Ejecutar la suite completa de pruebas:
208
+ | Prop | Type | Default | Description |
209
+ | :--- | :--- | :--- | :--- |
210
+ | `children` | `ReactNode` | `undefined` | Content such as a search input, label, or slider. |
211
+ | `minWidth` | `number \| string` | `120` | Minimum width in pixels or a CSS length. |
212
+ | `maxWidth` | `number \| string` | `undefined` | Maximum width in pixels or a CSS length. |
153
213
 
154
- ```bash
155
- npm test
156
- ```
214
+ All components accept `className`, `style`, and standard attributes for their underlying element. Toolbar, group, and area refs point to a `div`; icon button refs point to a `button`.
157
215
 
158
- Ejecutar con reporte de cobertura de código:
216
+ ## Development
159
217
 
160
218
  ```bash
161
- npm run test:coverage
219
+ npm ci
162
220
  ```
163
221
 
164
- Ejecutar chequeo de tipos en TypeScript:
222
+ ESLint 10 uses `createEslintConfig` from `super-configs/eslint` with the React and Jest presets. Biome extends `super-configs/biome` for linting, formatting, and import organization.
165
223
 
166
224
  ```bash
167
- npm run typecheck
225
+ npm run lint # Run ESLint with zero warnings allowed
226
+ npm run lint:fix # Apply ESLint fixes
227
+ npm run format:check # Check with Biome
228
+ npm run format # Apply Biome fixes
229
+ npm run typecheck # Check TypeScript types
230
+ npm test # Run Jest tests
231
+ npm run test:coverage # Generate a coverage report
232
+ npm run check # Run ESLint, Biome, TypeScript, and Jest
168
233
  ```
169
234
 
170
- ### Ejecutar Storybook en local
235
+ ### Storybook
171
236
 
172
- Para explorar visualmente los componentes, probar el cambio dinámico de formas y los temas:
237
+ The story environment uses Storybook 10 and Vite 8.3.4. Explore components, themes, and adaptive shapes locally:
173
238
 
174
239
  ```bash
175
240
  npm run storybook
176
241
  ```
177
242
 
178
- Compilar Storybook estático:
243
+ Build the static Storybook site:
179
244
 
180
245
  ```bash
181
246
  npm run build-storybook
182
247
  ```
183
248
 
184
- Compilar la librería para distribución (ESM, CJS y `.d.ts`):
249
+ ### Library build
250
+
251
+ Build the library for distribution as ESM, CommonJS, and TypeScript declarations:
185
252
 
186
253
  ```bash
187
254
  npm run build
188
255
  ```
189
256
 
190
- ---
257
+ Preview the npm package contents:
258
+
259
+ ```bash
260
+ npm pack --dry-run
261
+ ```
262
+
263
+ ## Releases and deployment
264
+
265
+ The workflows follow the same release pattern as `react-dockbar` and `super-configs`:
266
+
267
+ - **CI:** pull requests and pushes to branches other than `main` run type checks, tests, and builds with React 18 and 19. React 19 also runs ESLint, Biome, Storybook, and a package-content check. Pull request commits are validated with commitlint.
268
+ - **npm:** after CI passes on `main`, semantic-release calculates the version, generates `CHANGELOG.md` and GitHub release notes, updates package versions, creates a `vX.Y.Z` tag, and publishes `bubble-toolbar` with provenance.
269
+ - **GitHub Packages:** each new npm release is mirrored as `@eljijuna/bubble-toolbar` from its release tag. The name change happens only in the publishing job.
270
+ - **GitHub Pages:** every validated push to `main` deploys Storybook to [eljijuna.github.io/bubble-toolbar](https://eljijuna.github.io/bubble-toolbar/), even when there is no new package release. Deployment is independent of npm publishing.
271
+
272
+ ### Conventional Commits
273
+
274
+ Use `type(optional-scope): description` for commits and squash-merge titles:
275
+
276
+ | Commit | Version change |
277
+ | :--- | :--- |
278
+ | `fix: correct activity cleanup` | Patch |
279
+ | `perf: reduce activity renders` | Patch |
280
+ | `feat: add activity colors` | Minor |
281
+ | `feat!: replace the group API` | Major |
282
+ | Any type with a `BREAKING CHANGE:` footer | Major |
283
+ | `docs:`, `test:`, `chore:`, `ci:`, `build:`, `refactor:`, `style:` without breaking changes | No release by themselves |
284
+
285
+ The highest applicable change determines the release. Without a previous release tag, semantic-release starts at **1.0.0**. Release commits use `chore(release): X.Y.Z [skip ci]` to avoid a release loop. Do not edit release versions or changelog entries manually.
286
+
287
+ ```bash
288
+ printf 'feat: add activity colors\n' | npm run commitlint
289
+ npm run release:dry-run
290
+ ```
291
+
292
+ The dry run needs access to the GitHub repository and publishing credentials to verify permissions. It does not publish a version. Release tooling uses Node.js 24 in CI; use Node.js 24.15 or newer for local release tooling.
293
+
294
+ ### Repository configuration
295
+
296
+ - Set the Actions secret `NPM_TOKEN` to an npm granular token with publish access to `bubble-toolbar`, allowing the CI publishing workflow to bypass 2FA. It must belong to an account authorized to publish the package name.
297
+ - Keep **Settings → Pages → Build and deployment → Source** set to **GitHub Actions**.
298
+ - The workflows declare the required `GITHUB_TOKEN` permissions for releases, package publishing, and Pages. If branch protection blocks release commits, allow the release automation to update `main` using an appropriate GitHub App token.
299
+ - The **Release** workflow can also be dispatched manually on `main`. To retry a failed mirror after npm succeeded, rerun the failed GitHub Packages job.
300
+
301
+ Install the mirror with an npm configuration that routes its scope to GitHub Packages:
302
+
303
+ ```ini
304
+ @eljijuna:registry=https://npm.pkg.github.com
305
+ ```
306
+
307
+ ```bash
308
+ npm install @eljijuna/bubble-toolbar
309
+ ```
191
310
 
192
- ## 📄 Licencia
311
+ GitHub Packages installation requires GitHub authentication with package read access. Publishing and deployment happen when the workflows run on GitHub; local setup does not publish packages.
193
312
 
194
- MIT © ElJijuna
313
+ ## License
195
314
 
315
+ [MIT](./LICENSE) © ElJijuna
package/dist/index.d.mts CHANGED
@@ -1,11 +1,26 @@
1
- import * as React from 'react';
2
- import React__default, { HTMLAttributes, ReactNode, CSSProperties, ButtonHTMLAttributes } from 'react';
1
+ import * as react from 'react';
2
+ import { HTMLAttributes, ReactNode, CSSProperties, ButtonHTMLAttributes, ForwardRefExoticComponent, RefAttributes } from 'react';
3
+
4
+ interface BubbleActivityValue {
5
+ isBusy: boolean;
6
+ run: <T>(task: () => Promise<T>) => Promise<T>;
7
+ }
8
+ declare const useBubbleActivity: () => BubbleActivityValue;
3
9
 
4
10
  type BubbleToolbarTheme = 'light' | 'dark' | 'auto' | 'glass';
5
11
  type BubbleToolbarPosition = 'bottom' | 'top' | 'static' | 'custom';
6
12
  type BubbleToolbarSize = 'sm' | 'md' | 'lg' | number;
7
13
  type BubbleShape = 'circle' | 'pill';
8
- interface BubbleToolbarProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
14
+ /** Activity appearance inherited by each bubble group. */
15
+ interface BubbleActivityProps {
16
+ /** Adds external activity; false does not hide pending local tasks. */
17
+ busy?: boolean;
18
+ /** Three CSS colors used by the rotating activity border. */
19
+ activityColors?: readonly [string, string, string];
20
+ /** Seconds per rotation. Defaults to 2.4. */
21
+ activityDuration?: number;
22
+ }
23
+ interface BubbleToolbarProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'>, BubbleActivityProps {
9
24
  /**
10
25
  * Definition of items by groups.
11
26
  * Example:
@@ -82,7 +97,7 @@ interface BubbleToolbarProps extends Omit<HTMLAttributes<HTMLDivElement>, 'child
82
97
  */
83
98
  ariaLabel?: string;
84
99
  }
85
- interface BubbleGroupProps extends HTMLAttributes<HTMLDivElement> {
100
+ interface BubbleGroupProps extends HTMLAttributes<HTMLDivElement>, BubbleActivityProps {
86
101
  /**
87
102
  * Elements inside this group.
88
103
  * 1 node renders as a circle.
@@ -152,18 +167,18 @@ interface BubbleAreaProps extends HTMLAttributes<HTMLDivElement> {
152
167
  maxWidth?: number | string;
153
168
  }
154
169
 
155
- declare const BubbleGroup: React__default.ForwardRefExoticComponent<BubbleGroupProps & React__default.RefAttributes<HTMLDivElement>>;
170
+ declare const BubbleGroup: react.ForwardRefExoticComponent<BubbleGroupProps & react.RefAttributes<HTMLDivElement>>;
156
171
 
157
- interface BubbleToolbarComponent extends React__default.ForwardRefExoticComponent<BubbleToolbarProps & React__default.RefAttributes<HTMLDivElement>> {
172
+ interface BubbleToolbarComponent extends ForwardRefExoticComponent<BubbleToolbarProps & RefAttributes<HTMLDivElement>> {
158
173
  Group: typeof BubbleGroup;
159
174
  }
160
175
  declare const BubbleToolbar: BubbleToolbarComponent;
161
176
 
162
- declare const BubbleIconButton: React__default.ForwardRefExoticComponent<BubbleIconButtonProps & React__default.RefAttributes<HTMLButtonElement>>;
177
+ declare const BubbleArea: react.ForwardRefExoticComponent<BubbleAreaProps & react.RefAttributes<HTMLDivElement>>;
163
178
 
164
- declare const BubbleArea: React__default.ForwardRefExoticComponent<BubbleAreaProps & React__default.RefAttributes<HTMLDivElement>>;
179
+ declare const BubbleIconButton: react.ForwardRefExoticComponent<BubbleIconButtonProps & react.RefAttributes<HTMLButtonElement>>;
165
180
 
166
- interface BubbleToolbarContextValue {
181
+ interface BubbleToolbarContextValue extends BubbleActivityProps {
167
182
  size: BubbleToolbarSize;
168
183
  theme: BubbleToolbarTheme;
169
184
  blurred: boolean;
@@ -172,12 +187,12 @@ interface BubbleToolbarContextValue {
172
187
  bordered: boolean;
173
188
  shadow: boolean | string;
174
189
  }
175
- declare const BubbleToolbarContext: React.Context<BubbleToolbarContextValue>;
190
+ declare const BubbleToolbarContext: react.Context<BubbleToolbarContextValue>;
176
191
  declare const useBubbleToolbar: () => BubbleToolbarContextValue;
177
192
 
178
193
  /**
179
194
  * Returns an array of non-null, non-undefined, non-boolean child elements.
180
- * Unpacks React.Fragment if encountered at the top level.
195
+ * Unpacks Fragment if encountered at the top level.
181
196
  */
182
197
  declare function getValidChildren(children: ReactNode): ReactNode[];
183
198
 
@@ -185,4 +200,4 @@ declare function resolveSize(size: BubbleToolbarSize): number;
185
200
  declare function formatPx(val: number | string): string;
186
201
  declare function getGlassStyles(theme: BubbleToolbarTheme, blurred: boolean, blurAmount: number | string, bordered: boolean, shadow: boolean | string): CSSProperties;
187
202
 
188
- export { BubbleArea, type BubbleAreaProps, BubbleGroup, type BubbleGroupProps, BubbleIconButton, type BubbleIconButtonProps, type BubbleShape, BubbleToolbar, type BubbleToolbarComponent, BubbleToolbarContext, type BubbleToolbarPosition, type BubbleToolbarProps, type BubbleToolbarSize, type BubbleToolbarTheme, formatPx, getGlassStyles, getValidChildren, resolveSize, useBubbleToolbar };
203
+ export { type BubbleActivityProps, type BubbleActivityValue, BubbleArea, type BubbleAreaProps, BubbleGroup, type BubbleGroupProps, BubbleIconButton, type BubbleIconButtonProps, type BubbleShape, BubbleToolbar, type BubbleToolbarComponent, BubbleToolbarContext, type BubbleToolbarPosition, type BubbleToolbarProps, type BubbleToolbarSize, type BubbleToolbarTheme, formatPx, getGlassStyles, getValidChildren, resolveSize, useBubbleActivity, useBubbleToolbar };
package/dist/index.d.ts CHANGED
@@ -1,11 +1,26 @@
1
- import * as React from 'react';
2
- import React__default, { HTMLAttributes, ReactNode, CSSProperties, ButtonHTMLAttributes } from 'react';
1
+ import * as react from 'react';
2
+ import { HTMLAttributes, ReactNode, CSSProperties, ButtonHTMLAttributes, ForwardRefExoticComponent, RefAttributes } from 'react';
3
+
4
+ interface BubbleActivityValue {
5
+ isBusy: boolean;
6
+ run: <T>(task: () => Promise<T>) => Promise<T>;
7
+ }
8
+ declare const useBubbleActivity: () => BubbleActivityValue;
3
9
 
4
10
  type BubbleToolbarTheme = 'light' | 'dark' | 'auto' | 'glass';
5
11
  type BubbleToolbarPosition = 'bottom' | 'top' | 'static' | 'custom';
6
12
  type BubbleToolbarSize = 'sm' | 'md' | 'lg' | number;
7
13
  type BubbleShape = 'circle' | 'pill';
8
- interface BubbleToolbarProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
14
+ /** Activity appearance inherited by each bubble group. */
15
+ interface BubbleActivityProps {
16
+ /** Adds external activity; false does not hide pending local tasks. */
17
+ busy?: boolean;
18
+ /** Three CSS colors used by the rotating activity border. */
19
+ activityColors?: readonly [string, string, string];
20
+ /** Seconds per rotation. Defaults to 2.4. */
21
+ activityDuration?: number;
22
+ }
23
+ interface BubbleToolbarProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'>, BubbleActivityProps {
9
24
  /**
10
25
  * Definition of items by groups.
11
26
  * Example:
@@ -82,7 +97,7 @@ interface BubbleToolbarProps extends Omit<HTMLAttributes<HTMLDivElement>, 'child
82
97
  */
83
98
  ariaLabel?: string;
84
99
  }
85
- interface BubbleGroupProps extends HTMLAttributes<HTMLDivElement> {
100
+ interface BubbleGroupProps extends HTMLAttributes<HTMLDivElement>, BubbleActivityProps {
86
101
  /**
87
102
  * Elements inside this group.
88
103
  * 1 node renders as a circle.
@@ -152,18 +167,18 @@ interface BubbleAreaProps extends HTMLAttributes<HTMLDivElement> {
152
167
  maxWidth?: number | string;
153
168
  }
154
169
 
155
- declare const BubbleGroup: React__default.ForwardRefExoticComponent<BubbleGroupProps & React__default.RefAttributes<HTMLDivElement>>;
170
+ declare const BubbleGroup: react.ForwardRefExoticComponent<BubbleGroupProps & react.RefAttributes<HTMLDivElement>>;
156
171
 
157
- interface BubbleToolbarComponent extends React__default.ForwardRefExoticComponent<BubbleToolbarProps & React__default.RefAttributes<HTMLDivElement>> {
172
+ interface BubbleToolbarComponent extends ForwardRefExoticComponent<BubbleToolbarProps & RefAttributes<HTMLDivElement>> {
158
173
  Group: typeof BubbleGroup;
159
174
  }
160
175
  declare const BubbleToolbar: BubbleToolbarComponent;
161
176
 
162
- declare const BubbleIconButton: React__default.ForwardRefExoticComponent<BubbleIconButtonProps & React__default.RefAttributes<HTMLButtonElement>>;
177
+ declare const BubbleArea: react.ForwardRefExoticComponent<BubbleAreaProps & react.RefAttributes<HTMLDivElement>>;
163
178
 
164
- declare const BubbleArea: React__default.ForwardRefExoticComponent<BubbleAreaProps & React__default.RefAttributes<HTMLDivElement>>;
179
+ declare const BubbleIconButton: react.ForwardRefExoticComponent<BubbleIconButtonProps & react.RefAttributes<HTMLButtonElement>>;
165
180
 
166
- interface BubbleToolbarContextValue {
181
+ interface BubbleToolbarContextValue extends BubbleActivityProps {
167
182
  size: BubbleToolbarSize;
168
183
  theme: BubbleToolbarTheme;
169
184
  blurred: boolean;
@@ -172,12 +187,12 @@ interface BubbleToolbarContextValue {
172
187
  bordered: boolean;
173
188
  shadow: boolean | string;
174
189
  }
175
- declare const BubbleToolbarContext: React.Context<BubbleToolbarContextValue>;
190
+ declare const BubbleToolbarContext: react.Context<BubbleToolbarContextValue>;
176
191
  declare const useBubbleToolbar: () => BubbleToolbarContextValue;
177
192
 
178
193
  /**
179
194
  * Returns an array of non-null, non-undefined, non-boolean child elements.
180
- * Unpacks React.Fragment if encountered at the top level.
195
+ * Unpacks Fragment if encountered at the top level.
181
196
  */
182
197
  declare function getValidChildren(children: ReactNode): ReactNode[];
183
198
 
@@ -185,4 +200,4 @@ declare function resolveSize(size: BubbleToolbarSize): number;
185
200
  declare function formatPx(val: number | string): string;
186
201
  declare function getGlassStyles(theme: BubbleToolbarTheme, blurred: boolean, blurAmount: number | string, bordered: boolean, shadow: boolean | string): CSSProperties;
187
202
 
188
- export { BubbleArea, type BubbleAreaProps, BubbleGroup, type BubbleGroupProps, BubbleIconButton, type BubbleIconButtonProps, type BubbleShape, BubbleToolbar, type BubbleToolbarComponent, BubbleToolbarContext, type BubbleToolbarPosition, type BubbleToolbarProps, type BubbleToolbarSize, type BubbleToolbarTheme, formatPx, getGlassStyles, getValidChildren, resolveSize, useBubbleToolbar };
203
+ export { type BubbleActivityProps, type BubbleActivityValue, BubbleArea, type BubbleAreaProps, BubbleGroup, type BubbleGroupProps, BubbleIconButton, type BubbleIconButtonProps, type BubbleShape, BubbleToolbar, type BubbleToolbarComponent, BubbleToolbarContext, type BubbleToolbarPosition, type BubbleToolbarProps, type BubbleToolbarSize, type BubbleToolbarTheme, formatPx, getGlassStyles, getValidChildren, resolveSize, useBubbleActivity, useBubbleToolbar };