@bluerobotics/bluevue 0.1.1 → 0.2.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.
Files changed (91) hide show
  1. package/README.md +97 -18
  2. package/bin/create-blueos-extension.mjs +72 -0
  3. package/dist/bluevue.js +2464 -778
  4. package/dist/components/BlueApp.vue.d.ts +50 -0
  5. package/dist/components/BlueBanner.vue.d.ts +49 -0
  6. package/dist/components/BlueBannerGroup.vue.d.ts +20 -0
  7. package/dist/components/BlueButton.vue.d.ts +59 -0
  8. package/dist/components/BlueCard.vue.d.ts +45 -0
  9. package/dist/components/BlueCheckbox.vue.d.ts +34 -0
  10. package/dist/components/BlueChip.vue.d.ts +47 -0
  11. package/dist/components/BlueConfirmDialog.vue.d.ts +193 -0
  12. package/dist/components/BlueDialog.vue.d.ts +60 -0
  13. package/dist/components/BlueFileDrop.vue.d.ts +32 -0
  14. package/dist/components/BlueIcon.vue.d.ts +26 -0
  15. package/dist/components/BlueInput.vue.d.ts +1 -0
  16. package/dist/components/BlueProgressBar.vue.d.ts +28 -0
  17. package/dist/components/BlueRadioGroup.vue.d.ts +40 -0
  18. package/dist/components/BlueSection.vue.d.ts +36 -0
  19. package/dist/components/BlueSnackbar.vue.d.ts +19 -0
  20. package/dist/components/BlueSpinner.vue.d.ts +25 -0
  21. package/dist/components/BlueStat.vue.d.ts +21 -0
  22. package/dist/components/BlueStepsDialog.vue.d.ts +62 -0
  23. package/dist/components/BlueTable.vue.d.ts +62 -0
  24. package/dist/components/BlueTabs.vue.d.ts +37 -0
  25. package/dist/components/BlueTextarea.vue.d.ts +40 -0
  26. package/dist/components/BlueTooltip.vue.d.ts +44 -0
  27. package/dist/components/BlueWindRose.vue.d.ts +37 -0
  28. package/dist/composables/useBlueLoading.d.ts +22 -0
  29. package/dist/composables/useBlueOs.d.ts +39 -0
  30. package/dist/composables/useBlueSnackbar.d.ts +30 -0
  31. package/dist/index.d.ts +30 -0
  32. package/dist/services/blueos.d.ts +37 -0
  33. package/dist/style.css +1 -1
  34. package/dist/types/banner.d.ts +16 -0
  35. package/dist/utils/files.d.ts +13 -0
  36. package/dist/utils/theme.d.ts +22 -0
  37. package/package.json +8 -2
  38. package/src/components/BlueApp.vue +84 -0
  39. package/src/components/BlueBanner.vue +196 -0
  40. package/src/components/BlueBannerGroup.vue +47 -0
  41. package/src/components/BlueButton.vue +134 -0
  42. package/src/components/BlueButtonGroup.vue +28 -26
  43. package/src/components/BlueCard.vue +62 -0
  44. package/src/components/BlueCheckbox.vue +100 -0
  45. package/src/components/BlueChip.vue +77 -0
  46. package/src/components/BlueConfirmDialog.vue +73 -0
  47. package/src/components/BlueDialog.vue +135 -0
  48. package/src/components/BlueFileDrop.vue +94 -0
  49. package/src/components/BlueIcon.vue +37 -0
  50. package/src/components/BlueInput.vue +23 -13
  51. package/src/components/BlueLoadingDialog.vue +22 -53
  52. package/src/components/BlueProgressBar.vue +46 -0
  53. package/src/components/BluePromptDialog.vue +51 -99
  54. package/src/components/BlueRadioGroup.vue +104 -0
  55. package/src/components/BlueSection.vue +33 -0
  56. package/src/components/BlueSelect.vue +13 -11
  57. package/src/components/BlueSlider.vue +1 -3
  58. package/src/components/BlueSnackbar.vue +79 -0
  59. package/src/components/BlueSpinner.vue +37 -0
  60. package/src/components/BlueStat.vue +26 -0
  61. package/src/components/BlueStepsDialog.vue +180 -0
  62. package/src/components/BlueSwitch.vue +18 -15
  63. package/src/components/BlueTable.vue +154 -0
  64. package/src/components/BlueTabs.vue +77 -0
  65. package/src/components/BlueTextarea.vue +102 -0
  66. package/src/components/BlueTooltip.vue +88 -0
  67. package/src/components/BlueWindRose.vue +269 -0
  68. package/src/composables/useBlueLoading.ts +38 -0
  69. package/src/composables/useBlueOs.ts +154 -0
  70. package/src/composables/useBlueSnackbar.ts +89 -0
  71. package/src/index.ts +40 -0
  72. package/src/services/blueos.ts +95 -0
  73. package/src/styles/bluevue.css +144 -0
  74. package/src/types/banner.ts +17 -0
  75. package/src/utils/files.ts +44 -0
  76. package/src/utils/theme.ts +37 -0
  77. package/templates/extension/.github/workflows/deploy.yml +23 -0
  78. package/templates/extension/Dockerfile +56 -0
  79. package/templates/extension/README.md +35 -0
  80. package/templates/extension/backend/main.py +45 -0
  81. package/templates/extension/backend/requirements.txt +2 -0
  82. package/templates/extension/frontend/.eslintrc.cjs +20 -0
  83. package/templates/extension/frontend/env.d.ts +7 -0
  84. package/templates/extension/frontend/index.html +12 -0
  85. package/templates/extension/frontend/package.json +30 -0
  86. package/templates/extension/frontend/src/App.vue +51 -0
  87. package/templates/extension/frontend/src/main.ts +8 -0
  88. package/templates/extension/frontend/src/styles/global.css +11 -0
  89. package/templates/extension/frontend/tsconfig.json +25 -0
  90. package/templates/extension/frontend/tsconfig.node.json +11 -0
  91. package/templates/extension/frontend/vite.config.ts +24 -0
package/README.md CHANGED
@@ -1,9 +1,13 @@
1
1
  # BlueVue
2
2
 
3
- Blue Robotics Vue components for common UI.
3
+ The Blue Robotics look, for BlueOS extensions.
4
4
 
5
- A small set of Vue 3 controls built with Tailwind, with no UI-framework dependency: they need
6
- Vue, the Material Design Icons font, and nothing else.
5
+ A Vue 3 kit that gives an extension the page it is laid out on, the controls that go on it, the
6
+ dialogs and notices it talks through, and the calls that reach BlueOS itself. It is built with
7
+ Tailwind and depends on no UI framework: Vue, the Material Design Icons font, and nothing else.
8
+
9
+ Every component has a page of its own, with a live demo and its API, in the
10
+ [component gallery](https://bluerobotics.github.io/BlueVue/).
7
11
 
8
12
  ## Install
9
13
 
@@ -25,22 +29,31 @@ import '@mdi/font/css/materialdesignicons.css'
25
29
  import '@bluerobotics/bluevue/style.css'
26
30
  ```
27
31
 
32
+ An extension is a `BlueApp` with your panels inside it:
33
+
28
34
  ```vue
29
35
  <script setup lang="ts">
30
- import { BlueInput, BlueSelect } from '@bluerobotics/bluevue'
36
+ import { BlueApp, BlueExpansiblePanel, BlueInput, BlueSwitch, useBlueOs, useBlueSnackbar } from '@bluerobotics/bluevue'
31
37
  import { ref } from 'vue'
32
38
 
39
+ const { vehicleName } = useBlueOs()
40
+ const { notify } = useBlueSnackbar()
41
+
33
42
  const name = ref('BlueBoat')
34
- const vehicle = ref('boat')
43
+ const armed = ref(false)
35
44
  </script>
36
45
 
37
46
  <template>
38
- <BlueInput v-model="name" label="Name" theme="dark" />
39
- <BlueSelect v-model="vehicle" label="Vehicle" theme="dark" :items="[{ name: 'Boat', value: 'boat' }]" />
47
+ <BlueApp :title="vehicleName ?? 'My extension'">
48
+ <BlueExpansiblePanel title="Vehicle" theme="dark" :expanded="true">
49
+ <BlueInput v-model="name" name="name" label="Name" theme="dark" />
50
+ <BlueSwitch v-model="armed" name="armed" label="Start armed" label-on="Armed" label-off="Safe" theme="dark" />
51
+ </BlueExpansiblePanel>
52
+ </BlueApp>
40
53
  </template>
41
54
  ```
42
55
 
43
- Every control takes a `theme` of `light` or `dark`.
56
+ Every control takes a `theme` of `light` or `dark`. Extensions are dark.
44
57
 
45
58
  ## Styles
46
59
 
@@ -71,7 +84,8 @@ Doing both works and costs only the duplicated utility bytes.
71
84
 
72
85
  ### Theming
73
86
 
74
- The colours and shadows are CSS custom properties, so an app can restate them:
87
+ The colours and shadows are CSS custom properties, so an app can restate them, either in CSS or
88
+ through `applyBlueTheme({ primary: '#0B5087' })`:
75
89
 
76
90
  ```css
77
91
  :root {
@@ -82,22 +96,76 @@ The colours and shadows are CSS custom properties, so an app can restate them:
82
96
  }
83
97
  ```
84
98
 
85
- ## Components
99
+ ## What is in it
100
+
101
+ **The page**
86
102
 
87
103
  | Component | What it is |
88
104
  | --- | --- |
89
- | `BlueButtonGroup` | Segmented switch or multi-toggle, with an optional overflow menu |
105
+ | `BlueApp` | The whole page: backdrop, card, and the notices and waits mounted once |
106
+ | `BlueCard` | The sheet a page is laid out on, with a title bar and a footer |
90
107
  | `BlueExpansiblePanel` | Titled section that collapses |
108
+ | `BlueSection` | A subject inside a panel |
109
+
110
+ **Controls**
111
+
112
+ | Component | What it is |
113
+ | --- | --- |
114
+ | `BlueButton` | Filled, tonal, text or icon, with a loading state |
115
+ | `BlueButtonGroup` | Segmented switch or multi-toggle, with an optional overflow menu |
116
+ | `BlueCheckbox` | A plain yes or no, indeterminate included |
117
+ | `BlueFileDrop` | A place to drop a file on, or press to choose one |
91
118
  | `BlueInput` | Text or number field, with a suffix, bounds and validation messages |
92
- | `BlueLoadingDialog` | Blocking overlay, under a turning propeller, for an operation in progress |
93
- | `BlueMenu` | Action dropdown, anchored to an activator or to a pointer position |
94
- | `BluePromptDialog` | Asks for a name, and optionally a note |
119
+ | `BlueWindRose` | A heading, typed or pointed on a compass rose |
120
+ | `BlueRadioGroup` | One choice out of a few, each spelled out |
95
121
  | `BlueSelect` | Single or multiple selection from a list |
96
122
  | `BlueSlider` | Range with a value pill, editable on double-click |
97
123
  | `BlueSwitch` | Two-state switch with its own labels |
124
+ | `BlueTabs` | The strip that switches between the pages of a view |
125
+ | `BlueTextarea` | A `BlueInput` with room for several lines |
98
126
 
99
- `useBluePopover` is also exported, for building further dropdowns on the same top-layer
100
- placement the menus use.
127
+ **Display**
128
+
129
+ | Component | What it is |
130
+ | --- | --- |
131
+ | `BlueBanner` / `BlueBannerGroup` | A one-line note that expands, and a stack of them |
132
+ | `BlueChip` | A small standing label: a state, a tag, a count |
133
+ | `BlueIcon` | A Material Design Icon in a square box |
134
+ | `BlueStat` | One read-only fact, in a row of them |
135
+ | `BlueTable` | Rows of records under a header that stays put, ordered by pressing it |
136
+
137
+ **Overlays**
138
+
139
+ | Component | What it is |
140
+ | --- | --- |
141
+ | `BlueDialog` | The base every dialog is built on, over the native `<dialog>` |
142
+ | `BlueConfirmDialog` | Asks before something irreversible |
143
+ | `BlueStepsDialog` | Reports work of several steps, with its log |
144
+ | `BlueMenu` | Action dropdown, anchored to an activator or to a pointer position |
145
+ | `BluePromptDialog` | Asks for a name, and optionally a note |
146
+ | `BlueTooltip` | A hint on hover or focus |
147
+
148
+ **Feedback**
149
+
150
+ | Component | What it is |
151
+ | --- | --- |
152
+ | `BlueLoadingDialog` | Blocking overlay, under a turning propeller, for an operation in progress |
153
+ | `BlueProgressBar` | How far along something is, or that it is going at all |
154
+ | `BlueSnackbar` | The corner notices are shown in, raised from anywhere |
155
+ | `BlueSpinner` | A turning ring, for a wait with no measure |
156
+
157
+ **Composables and helpers**
158
+
159
+ | Export | What it does |
160
+ | --- | --- |
161
+ | `useBlueSnackbar` | Raises a notice, or reports a failed call, from anywhere |
162
+ | `useBlueLoading` | Blocks the page while something is running |
163
+ | `useBlueOs` | What the vehicle calls itself, its version, and its look |
164
+ | `useBlueOsSetting` | A value kept on the vehicle rather than in this browser |
165
+ | `blueOsService` | Any BlueOS service by name, with JSON and errors handled |
166
+ | `useBluePopover` | The top-layer placement the menus and tooltips are built on |
167
+ | `applyBlueTheme` | Restates the tokens at runtime |
168
+ | `downloadJson` / `pickJsonFile` | Saves a profile to disk, and reads one back |
101
169
 
102
170
  ## Browser support
103
171
 
@@ -108,6 +176,17 @@ dialogs use `<dialog>`, so they need Chrome 114, Edge 114, Firefox 125 or Safari
108
176
 
109
177
  ```bash
110
178
  npm install
111
- npm run dev # playground on http://localhost:8090
112
- npm run build # dist/bluevue.js, dist/index.d.ts and dist/style.css
179
+ npm run dev # the gallery on http://localhost:8090
180
+ npm run build # dist/bluevue.js, dist/index.d.ts and dist/style.css
181
+ npm run docs:build # the gallery as a static site, in docs-dist
182
+ ```
183
+
184
+ ## Start an extension
185
+
186
+ ```bash
187
+ npx @bluerobotics/bluevue my-extension
113
188
  ```
189
+
190
+ That copies a Vite + Vue frontend already wired to this package, a FastAPI backend that answers
191
+ `/register_service` with `works_in_relative_paths`, a Dockerfile carrying the labels BlueOS
192
+ installs from, and a GitHub workflow that builds the image.
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env node
2
+ import { cpSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs'
3
+ import { dirname, join, resolve } from 'node:path'
4
+ import { fileURLToPath } from 'node:url'
5
+
6
+ const here = dirname(fileURLToPath(import.meta.url))
7
+ const template = join(here, '..', 'templates', 'extension')
8
+
9
+ const raw = process.argv[2]
10
+ if (!raw || raw === '--help' || raw === '-h') {
11
+ console.error('Usage: npx @bluerobotics/bluevue <name>')
12
+ console.error('Creates a BlueOS extension in ./<name>, wired to this package.')
13
+ process.exit(raw ? 0 : 1)
14
+ }
15
+
16
+ const slug = raw
17
+ .trim()
18
+ .toLowerCase()
19
+ .replace(/[^a-z0-9]+/g, '-')
20
+ .replace(/^-+|-+$/g, '')
21
+
22
+ if (!slug) {
23
+ console.error('Give the extension a name made of letters, numbers or hyphens.')
24
+ process.exit(1)
25
+ }
26
+
27
+ const title = slug
28
+ .split('-')
29
+ .filter(Boolean)
30
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
31
+ .join(' ')
32
+
33
+ const dest = resolve(process.cwd(), slug)
34
+
35
+ try {
36
+ mkdirSync(dest)
37
+ } catch (error) {
38
+ if (/** @type {NodeJS.ErrnoException} */ (error).code === 'EEXIST') {
39
+ console.error(`${dest} already exists.`)
40
+ process.exit(1)
41
+ }
42
+ throw error
43
+ }
44
+
45
+ cpSync(template, dest, { recursive: true })
46
+
47
+ const replacements = {
48
+ __SLUG__: slug,
49
+ __TITLE__: title,
50
+ }
51
+
52
+ const walk = (dir) => {
53
+ for (const entry of readdirSync(dir)) {
54
+ const path = join(dir, entry)
55
+ if (statSync(path).isDirectory()) {
56
+ walk(path)
57
+ continue
58
+ }
59
+ const before = readFileSync(path, 'utf8')
60
+ const after = Object.entries(replacements).reduce(
61
+ (text, [token, value]) => text.replaceAll(token, value),
62
+ before
63
+ )
64
+ if (after !== before) writeFileSync(path, after)
65
+ }
66
+ }
67
+
68
+ walk(dest)
69
+
70
+ console.log(`Created ${slug}/
71
+ cd ${slug}/frontend && npm install && npm run dev
72
+ The backend is PORT=8000 python backend/main.py, after pip install -r backend/requirements.txt.`)