@mframework/ui 0.1.0-beta.1 → 0.1.0-beta.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mframework/ui",
3
- "version": "0.1.0-beta.1",
3
+ "version": "0.1.0-beta.3",
4
4
  "type": "module",
5
5
  "description": "Opiniated UI module for the M Framework.",
6
6
  "keywords": [
@@ -11,11 +11,10 @@
11
11
  "mframework",
12
12
  "meeovi"
13
13
  ],
14
- "main": "dist/src/module.cjs",
15
- "module": "dist/src/module.mjs",
14
+ "main": "dist/module.js",
15
+ "module": "dist/module.mjs",
16
16
  "files": [
17
- "dist",
18
- "src"
17
+ "dist"
19
18
  ],
20
19
  "scripts": {
21
20
  "build": "tsc -p tsconfig.json",
package/src/module.ts DELETED
@@ -1,105 +0,0 @@
1
- import {
2
- defineNuxtModule,
3
- addPlugin,
4
- createResolver,
5
- addComponentsDir,
6
- addImportsDir
7
- } from 'nuxt/kit'
8
-
9
- export interface MFrameworkUiOptions {
10
- theme ? : 'light' | 'dark'
11
- builder ? : boolean | {
12
- enabled ? : boolean
13
- }
14
- }
15
-
16
- export default defineNuxtModule < MFrameworkUiOptions > ({
17
- meta: {
18
- name: '@mframework/ui',
19
- configKey: 'mframeworkUi'
20
- },
21
-
22
- defaults: {
23
- theme: 'light',
24
- builder: true
25
- },
26
-
27
- setup(options, nuxt) {
28
- const resolver = createResolver(import.meta.url)
29
-
30
- //
31
- // 1. Ensure Vuetify loads FIRST
32
- //
33
- addPlugin({
34
- src: resolver.resolve('./runtime/plugins/vuetify'),
35
- mode: 'all'
36
- })
37
-
38
- //
39
- // 2. Global styles (Tailwind, Vuetify overrides, tokens)
40
- //
41
- nuxt.options.css = nuxt.options.css || []
42
- nuxt.options.css.push(resolver.resolve('./runtime/styles/index.scss'))
43
-
44
- //
45
- // 3. Register core UI plugins
46
- //
47
- const plugins = [
48
- 'formkit',
49
- 'fontawesome',
50
- 'lightgallery',
51
- 'motion',
52
- 'theme'
53
- ]
54
-
55
- plugins.forEach((name) => {
56
- addPlugin({
57
- src: resolver.resolve(`./runtime/plugins/${name}`),
58
- mode: 'all'
59
- })
60
- })
61
-
62
- //
63
- // 4. Register GrapesJS builder (client‑only)
64
- //
65
- if (options.builder) {
66
- addPlugin({
67
- src: resolver.resolve('./runtime/plugins/builder'),
68
- mode: 'client'
69
- })
70
- }
71
-
72
- //
73
- // 5. Auto‑import components & composables
74
- //
75
- addComponentsDir({
76
- path: resolver.resolve('./runtime/components'),
77
- prefix: 'M'
78
- })
79
-
80
- addImportsDir(resolver.resolve('./runtime/composables'))
81
-
82
- //
83
- // 6. Expose runtime config
84
- //
85
- // Normalize builder option
86
- const normalizedBuilder =
87
- typeof options.builder === 'boolean' ? {
88
- enabled: options.builder
89
- } :
90
- options.builder || {
91
- enabled: true
92
- }
93
-
94
- nuxt.options.runtimeConfig.public.mframeworkUi = {
95
- theme: options.theme,
96
- builder: normalizedBuilder
97
- }
98
-
99
- // Safe assignment without TS errors
100
- ;(nuxt.options as any).tailwindcss = (nuxt.options as any).tailwindcss || {}
101
- ;(nuxt.options as any).tailwindcss.configPath = resolver.resolve(
102
- './runtime/assets/config/tailwind.config.js'
103
- )
104
- }
105
- })
@@ -1,42 +0,0 @@
1
- /** @type {import('tailwindcss').Config} */
2
- export default {
3
- content: [], // Nuxt will inject its own paths
4
-
5
- theme: {
6
- extend: {
7
- colors: {
8
- primary: 'var(--m-primary)',
9
- secondary: 'var(--m-secondary)',
10
- accent: 'var(--m-accent)',
11
- muted: 'var(--m-muted)',
12
- surface: 'var(--m-surface)',
13
- border: 'var(--m-border)'
14
- },
15
-
16
- fontFamily: {
17
- sans: ['Inter', 'system-ui', 'sans-serif'],
18
- heading: ['Inter', 'system-ui', 'sans-serif']
19
- },
20
-
21
- borderRadius: {
22
- sm: '4px',
23
- DEFAULT: '8px',
24
- lg: '12px',
25
- xl: '16px'
26
- },
27
-
28
- spacing: {
29
- '1/2': '50%',
30
- 'full': '100%'
31
- },
32
-
33
- boxShadow: {
34
- soft: '0 2px 8px rgba(0,0,0,0.06)',
35
- medium: '0 4px 16px rgba(0,0,0,0.08)',
36
- strong: '0 8px 24px rgba(0,0,0,0.12)'
37
- }
38
- }
39
- },
40
-
41
- plugins: []
42
- }
@@ -1,66 +0,0 @@
1
- <template>
2
- <lightgallery :settings="{ speed: 500, plugins: plugins }" :onInit="onInit" :onBeforeSlide="onBeforeSlide">
3
- <a data-lg-size="1406-1390" class="gallery-item"
4
- data-src="https://images.unsplash.com/photo-1581894158358-5ecd2c518883?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1406&q=80"
5
- data-sub-html="<h4>Photo by - <a href='https://unsplash.com/@entrycube' >Diego Guzmán </a></h4> <p> Location - <a href='https://unsplash.com/s/photos/fushimi-inari-taisha-shrine-senbontorii%2C-68%E7%95%AA%E5%9C%B0-fukakusa-yabunouchicho%2C-fushimi-ward%2C-kyoto%2C-japan'>Fushimi Ward, Kyoto, Japan</a></p>">
6
- <img class="img-responsive"
7
- src="https://images.unsplash.com/photo-1581894158358-5ecd2c518883?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=240&q=80" />
8
- </a>
9
- <a data-lg-size="1400-1400" class="gallery-item"
10
- data-src="https://images.unsplash.com/photo-1544550285-f813152fb2fd?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1400&q=80"
11
- data-sub-html="<h4>Photo by - <a href='https://unsplash.com/@asoshiation' >Shah </a></h4><p> Location - <a href='https://unsplash.com/s/photos/shinimamiya%2C-osaka%2C-japan'>Shinimamiya, Osaka, Japan</a></p>">
12
- <img class="img-responsive"
13
- src="https://images.unsplash.com/photo-1544550285-f813152fb2fd?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=240&q=80" />
14
- </a>
15
- <a data-lg-size="1400-1400" class="gallery-item"
16
- data-src="https://images.unsplash.com/photo-1584592740039-cddf0671f3d4?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1400&q=80"
17
- data-sub-html="<h4>Photo by - <a href='https://unsplash.com/@katherine_xx11' >Katherine Gu </a></h4><p> For all those years we were alone and helpless.</p>">
18
- <img style="width: 200px" class="img-responsive"
19
- src="https://images.unsplash.com/photo-1584592740039-cddf0671f3d4?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=240&q=80" />
20
- </a>
21
- <a class="gallery-item"
22
- data-video='{"source": [{"src":"https://www.lightgalleryjs.com/videos/video1.mp4", "type":"video/mp4"}], "attributes": {"preload": false, "controls": true}}'
23
- data-poster=""
24
- data-sub-html="<h4>Photo by - <a href='https://unsplash.com/@katherine_xx11' >Katherine Gu </a></h4><p> For all those years we were alone and helpless.</p>">
25
- <img width="200" class="img-responsive"
26
- src="https://www.lightgalleryjs.com/images/demo/html5-video-poster.jpg" />
27
- </a>
28
- </lightgallery>
29
- </template>
30
-
31
- <script>
32
- import Lightgallery from 'lightgallery/vue';
33
- import lgZoom from 'lightgallery/plugins/zoom';
34
- import lgVideo from 'lightgallery/plugins/video';
35
-
36
- export default {
37
- name: 'App',
38
- components: {
39
- Lightgallery,
40
- },
41
- data: () => ({
42
- plugins: [lgZoom, lgVideo],
43
- }),
44
- methods: {
45
- onInit: () => {
46
- console.log('lightGallery has been initialized');
47
- },
48
- onBeforeSlide: () => {
49
- console.log('calling before slide');
50
- },
51
- },
52
- };
53
- </script>
54
- <style lang="css">
55
- @import url('https://cdn.jsdelivr.net/npm/lightgallery@2.0.0-beta.4/css/lightgallery.css');
56
- @import url('https://cdn.jsdelivr.net/npm/lightgallery@2.0.0-beta.4/css/lg-zoom.css');
57
- @import url('https://cdn.jsdelivr.net/npm/lightgallery@2.0.0-beta.4/css/lg-video.css');
58
-
59
- body {
60
- margin: 0;
61
- }
62
-
63
- .gallery-item {
64
- margin: 5px;
65
- }
66
- </style>
@@ -1,17 +0,0 @@
1
- <template>
2
- <v-btn :color="color" class="m-btn" v-bind="$attrs">
3
- <slot />
4
- </v-btn>
5
- </template>
6
-
7
- <script setup lang="ts">
8
- defineProps < {
9
- color ? : string
10
- } > ()
11
- </script>
12
-
13
- <style scoped>
14
- .m-btn {
15
- @apply px-4 py-2 rounded-md;
16
- }
17
- </style>
@@ -1,14 +0,0 @@
1
- import iziToast from 'izitoast'
2
- import 'izitoast/dist/css/iziToast.min.css'
3
-
4
- export const useMToast = () => {
5
- const success = (message: string, title = 'Success') => {
6
- iziToast.success({ title, message })
7
- }
8
-
9
- const error = (message: string, title = 'Error') => {
10
- iziToast.error({ title, message })
11
- }
12
-
13
- return { success, error }
14
- }
@@ -1,22 +0,0 @@
1
- export const tokens = {
2
- colors: {
3
- primary: '#4f46e5',
4
- secondary: '#64748b',
5
- success: '#22c55e',
6
- warning: '#f59e0b',
7
- error: '#ef4444',
8
- surface: '#ffffff',
9
- background: '#f8fafc'
10
- },
11
- radius: {
12
- sm: '4px',
13
- md: '8px',
14
- lg: '12px'
15
- },
16
- spacing: {
17
- xs: '4px',
18
- sm: '8px',
19
- md: '16px',
20
- lg: '24px'
21
- }
22
- }
@@ -1,326 +0,0 @@
1
- import {
2
- defineNuxtPlugin
3
- } from 'nuxt/app'
4
- import 'grapesjs/dist/css/grapes.min.css'
5
- import grapesjs from 'grapesjs'
6
-
7
- export default defineNuxtPlugin((nuxtApp) => {
8
- if (process.server) return
9
-
10
- const mount = () => {
11
- const el = document.querySelector('#gjs')
12
- if (!el) return null
13
-
14
- const editor = grapesjs.init({
15
- container: `${el}`,
16
- fromElement: true,
17
- height: '100%',
18
- width: 'auto',
19
- storageManager: {
20
- type: 'local',
21
- autosave: true,
22
- autoload: true,
23
- stepsBeforeSave: 1,
24
- options: {
25
- local: {
26
- key: 'gjsProject'
27
- }
28
- }
29
- },
30
-
31
- // Avoid any default panel
32
- layerManager: {
33
- appendTo: '.layers-container',
34
- },
35
-
36
- mediaCondition: 'min-width', // default is `max-width`
37
- deviceManager: {
38
- devices: [{
39
- name: 'Desktop',
40
- width: '', // default size
41
- widthMedia: '1024px', // this value will be used in CSS @media
42
- },
43
- {
44
- name: 'Mobile',
45
- width: '320px', // this value will be used on canvas width
46
- widthMedia: '480px', // this value will be used in CSS @media
47
- },
48
- ],
49
- },
50
-
51
- blockManager: {
52
- appendTo: '#blocks',
53
- blocks: [{
54
- id: 'section', // id is mandatory
55
- label: '<b>Section</b>', // You can use HTML/SVG inside labels
56
- category: 'Basic',
57
- attributes: {
58
- class: 'gjs-block-section'
59
- },
60
- content: {
61
- tagName: 'div',
62
- draggable: false,
63
- attributes: {
64
- 'some-attribute': 'some-value'
65
- },
66
- components: [{
67
- tagName: 'span',
68
- content: '<b>Some static content</b>',
69
- },
70
- {
71
- tagName: 'div',
72
- // use `content` for static strings, `components` string will be parsed
73
- // and transformed in Components
74
- components: '<span>HTML at some point</span>',
75
- },
76
- ],
77
- },
78
- },
79
- {
80
- id: 'text',
81
- label: 'Text',
82
- content: '<div data-gjs-type="text">Insert your text here</div>',
83
- },
84
- {
85
- id: 'image',
86
- label: 'Image',
87
- // Select the component once it's dropped
88
- select: true,
89
- // You can pass components as a JSON instead of a simple HTML string,
90
- // in this case we also use a defined component type `image`
91
- content: {
92
- type: 'image'
93
- },
94
- // This triggers `active` event on dropped components and the `image`
95
- // reacts by opening the AssetManager
96
- activate: true,
97
- },
98
- ],
99
- },
100
- panels: {
101
- defaults: [{
102
- id: 'layers',
103
- el: '.panel__right',
104
- // Make the panel resizable
105
- resizable: {
106
- maxDim: 350,
107
- minDim: 200,
108
- tc: false, // Top handler
109
- cl: true, // Left handler
110
- cr: false, // Right handler
111
- bc: false, // Bottom handler
112
- // Being a flex child we need to change `flex-basis` property
113
- // instead of the `width` (default)
114
- keyWidth: 'flex-basis',
115
- },
116
- },
117
- {
118
- id: 'panel-switcher',
119
- el: '.panel__switcher',
120
- buttons: [{
121
- id: 'show-layers',
122
- active: true,
123
- label: 'Layers',
124
- command: 'show-layers',
125
- // Once activated disable the possibility to turn it off
126
- togglable: false,
127
- },
128
- {
129
- id: 'show-style',
130
- active: true,
131
- label: 'Styles',
132
- command: 'show-styles',
133
- togglable: false,
134
- },
135
- {
136
- id: 'show-traits',
137
- active: true,
138
- label: 'Traits',
139
- command: 'show-traits',
140
- togglable: false,
141
- },
142
- ],
143
- },
144
- {
145
- id: 'panel-devices',
146
- el: '.panel__devices',
147
- buttons: [{
148
- id: 'device-desktop',
149
- label: 'D',
150
- command: 'set-device-desktop',
151
- active: true,
152
- togglable: false,
153
- },
154
- {
155
- id: 'device-mobile',
156
- label: 'M',
157
- command: 'set-device-mobile',
158
- togglable: false,
159
- },
160
- ],
161
- },
162
- ],
163
- },
164
- // The Selector Manager allows to assign classes and
165
- // different states (eg. :hover) on components.
166
- // Generally, it's used in conjunction with Style Manager
167
- // but it's not mandatory
168
- selectorManager: {
169
- appendTo: '.styles-container',
170
- },
171
- styleManager: {
172
- appendTo: '.styles-container',
173
- sectors: [{
174
- name: 'Dimension',
175
- open: false,
176
- // Use built-in properties
177
- buildProps: ['width', 'min-height', 'padding'],
178
- // Use `properties` to define/override single property
179
- properties: [{
180
- // Type of the input,
181
- // options: integer | radio | select | color | slider | file | composite | stack
182
- type: 'integer',
183
- name: 'The width', // Label for the property
184
- property: 'width', // CSS property (if buildProps contains it will be extended)
185
- units: ['px', '%'], // Units, available only for 'integer' types
186
- defaults: 'auto', // Default value
187
- min: 0, // Min value, available only for 'integer' types
188
- }, ],
189
- },
190
- {
191
- name: 'Extra',
192
- open: false,
193
- buildProps: ['background-color', 'box-shadow', 'custom-prop'],
194
- properties: [{
195
- id: 'custom-prop',
196
- name: 'Custom Label',
197
- property: 'font-size',
198
- type: 'select',
199
- defaults: '32px',
200
- // List of options, available only for 'select' and 'radio' types
201
- options: [{
202
- id: 'tiny',
203
- value: '12px',
204
- name: 'Tiny'
205
- },
206
- {
207
- id: 'medium',
208
- value: '18px',
209
- name: 'Medium'
210
- },
211
- {
212
- id: 'big',
213
- value: '32px',
214
- name: 'Big'
215
- },
216
- ],
217
- }, ],
218
- },
219
- ],
220
- },
221
- });
222
-
223
- // Define commands
224
- editor.Commands.add('show-layers', {
225
- getRowEl(editor: any) {
226
- return editor.getContainer().closest('.editor-row');
227
- },
228
- getLayersEl(row: any) {
229
- return row.querySelector('.layers-container');
230
- },
231
-
232
- run(editor: any, sender: any) {
233
- const lmEl = this.getLayersEl(this.getRowEl(editor));
234
- lmEl.style.display = '';
235
- },
236
- stop(editor: any, sender: any) {
237
- const lmEl = this.getLayersEl(this.getRowEl(editor));
238
- lmEl.style.display = 'none';
239
- },
240
- });
241
- editor.Commands.add('show-styles', {
242
- getRowEl(editor: any) {
243
- return editor.getContainer().closest('.editor-row');
244
- },
245
- getStyleEl(row: any) {
246
- return row.querySelector('.styles-container');
247
- },
248
-
249
- run(editor: any, sender: any) {
250
- const smEl = this.getStyleEl(this.getRowEl(editor));
251
- smEl.style.display = '';
252
- },
253
- stop(editor: any, sender: any) {
254
- const smEl = this.getStyleEl(this.getRowEl(editor));
255
- smEl.style.display = 'none';
256
- },
257
- });
258
-
259
- editor.Commands.add('show-traits', {
260
- getTraitsEl(editor: any) {
261
- const row = editor.getContainer().closest('.editor-row');
262
- return row.querySelector('.traits-container');
263
- },
264
- run(editor: any, sender: any) {
265
- this.getTraitsEl(editor).style.display = '';
266
- },
267
- stop(editor: any, sender: any) {
268
- this.getTraitsEl(editor).style.display = 'none';
269
- },
270
- });
271
-
272
- editor.Panels.addPanel({
273
- id: 'panel-top',
274
- el: '.panel__top',
275
- });
276
- editor.Panels.addPanel({
277
- id: 'basic-actions',
278
- el: '.panel__basic-actions',
279
- buttons: [{
280
- id: 'visibility',
281
- active: true, // active by default
282
- className: 'btn-toggle-borders',
283
- label: '<u>B</u>',
284
- command: 'sw-visibility', // Built-in command
285
- },
286
- {
287
- id: 'export',
288
- className: 'btn-open-export',
289
- label: 'Exp',
290
- command: 'export-template',
291
- context: 'export-template', // For grouping context of buttons from the same panel
292
- },
293
- {
294
- id: 'show-json',
295
- className: 'btn-show-json',
296
- label: 'JSON',
297
- context: 'show-json',
298
- command(editor: any) {
299
- editor.Modal.setTitle('Components JSON')
300
- .setContent(
301
- `<textarea style="width:100%; height: 250px;">
302
- ${JSON.stringify(editor.getComponents())}
303
- </textarea>`,
304
- )
305
- .open();
306
- },
307
- },
308
- ],
309
- });
310
- editor.Commands.add('set-device-desktop', {
311
- run: (editor) => editor.setDevice('Desktop'),
312
- });
313
- editor.Commands.add('set-device-mobile', {
314
- run: (editor) => editor.setDevice('Mobile'),
315
- });
316
- editor.on('change:device', () => console.log('Current device: ', editor.getDevice()));
317
- // Set initial device as Mobile
318
- editor.setDevice('Mobile');
319
-
320
- nuxtApp.provide('grapesStudio', editor)
321
- return editor
322
- }
323
-
324
- // Auto‑mount when page loads
325
- window.addEventListener('DOMContentLoaded', mount)
326
- })
@@ -1,11 +0,0 @@
1
- import { defineNuxtPlugin } from 'nuxt/app'
2
- import { library } from '@fortawesome/fontawesome-svg-core'
3
- import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
4
- import { fas } from '@fortawesome/free-solid-svg-icons'
5
- import '@fortawesome/fontawesome-free/css/all.min.css'
6
-
7
- export default defineNuxtPlugin((nuxtApp) => {
8
- library.add(fas)
9
-
10
- nuxtApp.vueApp.component('Fa', FontAwesomeIcon)
11
- })
@@ -1,9 +0,0 @@
1
- import { defineNuxtPlugin } from 'nuxt/app'
2
- import { plugin as formKitPlugin, defaultConfig } from '@formkit/vue'
3
- import '@formkit/themes/genesis'
4
-
5
- export default defineNuxtPlugin((nuxtApp) => {
6
- nuxtApp.vueApp.use(formKitPlugin, defaultConfig({
7
- // You can inject your own inputs, locales, etc. here
8
- }))
9
- })
@@ -1,21 +0,0 @@
1
- import { defineNuxtPlugin } from 'nuxt/app'
2
- import lightGallery from 'lightgallery'
3
- import lgZoom from 'lg-zoom'
4
- import lgVideo from 'lg-video'
5
- import lgThumbnail from 'lg-thumbnail'
6
- import 'lightgallery/css/lightgallery.css'
7
- import 'lightgallery/css/lg-zoom.css'
8
- import 'lightgallery/css/lg-thumbnail.css'
9
- import 'lightgallery/css/lg-video.css'
10
-
11
- export default defineNuxtPlugin(() => {
12
- // You can expose a composable or directive later if you want.
13
- // For now, this ensures CSS + plugins are bundled.
14
- // Example directive could be added here if needed.
15
- document.querySelectorAll('.lightgallery').forEach((element) => {
16
- lightGallery(element as HTMLElement, {
17
- plugins: [lgZoom, lgVideo, lgThumbnail],
18
- speed: 500,
19
- })
20
- })
21
- })
@@ -1,6 +0,0 @@
1
- import { defineNuxtPlugin } from 'nuxt/app'
2
- import { MotionPlugin } from '@vueuse/motion'
3
-
4
- export default defineNuxtPlugin((nuxtApp) => {
5
- nuxtApp.vueApp.use(MotionPlugin)
6
- })
@@ -1,32 +0,0 @@
1
- import { defineNuxtPlugin } from 'nuxt/app'
2
- import { useTheme } from 'vuetify'
3
-
4
- export default defineNuxtPlugin((nuxtApp) => {
5
- if (process.server) return
6
-
7
- const theme = useTheme()
8
-
9
- const setTheme = (mode: 'light' | 'dark') => {
10
- theme.global.name.value = mode
11
- document.documentElement.classList.toggle('dark', mode === 'dark')
12
- localStorage.setItem('mframework-theme', mode)
13
- }
14
-
15
- const toggleTheme = () => {
16
- const current = theme.global.name.value === 'light' ? 'dark' : 'light'
17
- setTheme(current)
18
- }
19
-
20
- // Load saved theme
21
- const saved = localStorage.getItem('mframework-theme')
22
- if (saved === 'light' || saved === 'dark') {
23
- setTheme(saved)
24
- }
25
-
26
- // Expose composable
27
- nuxtApp.provide('mTheme', {
28
- setTheme,
29
- toggleTheme,
30
- current: () => theme.global.name.value
31
- })
32
- })
@@ -1,57 +0,0 @@
1
- import {
2
- defineNuxtPlugin,
3
- useRuntimeConfig
4
- } from 'nuxt/app'
5
- import {
6
- useTheme
7
- } from 'vuetify'
8
-
9
- export default defineNuxtPlugin(() => {
10
- if (process.server) return
11
-
12
- const theme = useTheme()
13
- const config = useRuntimeConfig()
14
-
15
- const baseColors = {
16
- primary: 'var(--m-primary)',
17
- secondary: 'var(--m-secondary)',
18
- accent: 'var(--m-accent)',
19
- surface: 'var(--m-surface)',
20
- background: 'var(--m-muted)',
21
- border: 'var(--m-border)',
22
-
23
- // Required Vuetify system colors
24
- success: '#22c55e',
25
- warning: '#f59e0b',
26
- error: '#ef4444',
27
- info: '#3b82f6',
28
-
29
-
30
- // Vuetify internal required fields
31
- 'on-primary': '#ffffff',
32
- 'on-secondary': '#ffffff',
33
- 'on-surface': '#ffffff',
34
- 'on-background': '#ffffff',
35
- 'on-success': '#ffffff',
36
- 'on-warning': '#ffffff',
37
- 'on-error': '#ffffff',
38
- 'on-info': '#ffffff'
39
- }
40
-
41
- const defaultTheme = config.public.mframeworkUi?.theme || 'light'
42
-
43
- theme.global.name.value = defaultTheme
44
-
45
- theme.themes.value = {
46
- light: {
47
- dark: false,
48
- colors: baseColors,
49
- variables: {}
50
- },
51
- dark: {
52
- dark: true,
53
- colors: baseColors,
54
- variables: {}
55
- }
56
- }
57
- })
@@ -1,36 +0,0 @@
1
- import { defineNuxtPlugin, useRuntimeConfig } from 'nuxt/app'
2
- import { createVuetify } from 'vuetify'
3
- import * as components from 'vuetify/components'
4
- import * as directives from 'vuetify/directives'
5
- import 'vuetify/styles'
6
-
7
- export default defineNuxtPlugin((nuxtApp) => {
8
- const config = useRuntimeConfig()
9
- const theme = config.public.mframeworkUi?.theme || 'light'
10
-
11
- const vuetify = createVuetify({
12
- components,
13
- directives,
14
- theme: {
15
- defaultTheme: theme,
16
- themes: {
17
- light: {
18
- dark: false,
19
- colors: {
20
- primary: '#3b82f6',
21
- secondary: '#64748b'
22
- }
23
- },
24
- dark: {
25
- dark: true,
26
- colors: {
27
- primary: '#3b82f6',
28
- secondary: '#64748b'
29
- }
30
- }
31
- }
32
- }
33
- })
34
-
35
- nuxtApp.vueApp.use(vuetify)
36
- })
@@ -1,25 +0,0 @@
1
- import type { SearchClient } from 'instantsearch.js'
2
-
3
- export function getSearchClient(): SearchClient | null {
4
- let mod: any
5
- try {
6
- mod = require('@searchkit/instantsearch-client')
7
- } catch {
8
- console.warn('[mframework-ui] Searchkit client missing')
9
- return null
10
- }
11
-
12
- const candidate =
13
- mod?.default?.default ||
14
- mod?.default?.createClient ||
15
- mod?.default ||
16
- mod?.createClient ||
17
- mod
18
-
19
- if (typeof candidate !== 'function') return null
20
-
21
- const host = process.env.NUXT_PUBLIC_SEARCHKIT_HOST
22
- if (!host) return null
23
-
24
- return candidate({ host })
25
- }
@@ -1,19 +0,0 @@
1
- <template>
2
- <ais-instant-search :search-client="client" :index-name="index">
3
- <ais-search-box />
4
- <ais-hits>
5
- <template #item="{ item }">
6
- <slot name="hit" :item="item">
7
- <div class="p-2 border rounded">
8
- <ais-highlight attribute="title" :hit="item" />
9
- </div>
10
- </slot>
11
- </template>
12
- </ais-hits>
13
- </ais-instant-search>
14
- </template>
15
-
16
- <script setup lang="ts">
17
- const client = inject('mSearchClient')
18
- const index = inject('mSearchIndex')
19
- </script>
@@ -1,9 +0,0 @@
1
- import { getSearchClient } from './client'
2
-
3
- export default (nuxtApp: any) => {
4
- const client = getSearchClient()
5
- const indexName = process.env.NUXT_PUBLIC_SEARCH_INDEX || 'default'
6
-
7
- nuxtApp.provide('mSearchClient', client)
8
- nuxtApp.provide('mSearchIndex', indexName)
9
- }
@@ -1,101 +0,0 @@
1
- @import './tailwind.css';
2
-
3
- // Your design tokens, utilities, etc.
4
- :root {
5
- --m-primary: #3b82f6;
6
- --m-secondary: #64748b;
7
- --m-accent: #10b981;
8
- --m-muted: #f3f4f6;
9
- --m-surface: #ffffff;
10
- --m-border: #e5e7eb;
11
- }
12
-
13
- .dark {
14
- --m-primary: #60a5fa;
15
- --m-secondary: #94a3b8;
16
- --m-accent: #34d399;
17
- --m-muted: #1f2937;
18
- --m-surface: #111827;
19
- --m-border: #374151;
20
- }
21
-
22
- // GrapesJS canvas and editor styling
23
- /* Let's highlight canvas boundaries */
24
- #gjs {
25
- border: 3px solid #444;
26
- }
27
-
28
- /* Theming */
29
-
30
- /* Primary color for the background */
31
- .gjs-one-bg {
32
- background-color: #78366a;
33
- }
34
-
35
- /* Secondary color for the text color */
36
- .gjs-two-color {
37
- color: rgba(255, 255, 255, 0.7);
38
- }
39
-
40
- /* Tertiary color for the background */
41
- .gjs-three-bg {
42
- background-color: #ec5896;
43
- color: white;
44
- }
45
-
46
- /* Quaternary color for the text color */
47
- .gjs-four-color,
48
- .gjs-four-color-h:hover {
49
- color: #ec5896;
50
- }
51
-
52
- /* Reset some default styling */
53
- .gjs-cv-canvas {
54
- top: 0;
55
- width: 100%;
56
- height: 100%;
57
- }
58
-
59
- .gjs-block {
60
- width: auto;
61
- height: auto;
62
- min-height: auto;
63
- }
64
-
65
- .panel__top {
66
- padding: 0;
67
- width: 100%;
68
- display: flex;
69
- position: initial;
70
- justify-content: center;
71
- justify-content: space-between;
72
- }
73
- .panel__basic-actions {
74
- position: initial;
75
- }
76
-
77
- .editor-row {
78
- display: flex;
79
- justify-content: flex-start;
80
- align-items: stretch;
81
- flex-wrap: nowrap;
82
- height: 300px;
83
- }
84
-
85
- .editor-canvas {
86
- flex-grow: 1;
87
- }
88
-
89
- .panel__right {
90
- flex-basis: 230px;
91
- position: relative;
92
- overflow-y: auto;
93
- }
94
-
95
- .panel__switcher {
96
- position: initial;
97
- }
98
-
99
- .panel__devices {
100
- position: initial;
101
- }
@@ -1,9 +0,0 @@
1
- /* @mframework/ui/runtime/assets/css/tailwind.css */
2
-
3
- /* Tailwind base (if using Tailwind 3 style) */
4
- @import "tailwindcss/preflight";
5
-
6
- /* Vuetify + Tailwind coexistence tips:
7
- - Avoid global resets that fight Vuetify
8
- - Prefer utility classes for layout, spacing, etc.
9
- */
@@ -1,5 +0,0 @@
1
- // Override Vuetify variables or add global Vuetify-related styles here.
2
-
3
- body {
4
- font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
5
- }
@@ -1,12 +0,0 @@
1
- declare module 'nuxt/schema' {
2
- interface PublicRuntimeConfig {
3
- mframeworkUi?: {
4
- theme?: 'light' | 'dark'
5
- builder?: {
6
- enabled?: boolean
7
- }
8
- }
9
- }
10
- }
11
-
12
- export {}
@@ -1,11 +0,0 @@
1
- function getContrastColor(hexColor) {
2
- // Convert hex color to RGB format
3
- const red = parseInt(hexColor.slice(1, 3), 16);
4
- const green = parseInt(hexColor.slice(3, 5), 16);
5
- const blue = parseInt(hexColor.slice(5, 7), 16);
6
- // Calculate relative luminance of the color
7
- const luminance = (0.2126 * red + 0.7152 * green + 0.0722 * blue) / 255;
8
- // Return black or white depending on the luminance value
9
- return luminance > 0.5 ? '#000000' : '#ffffff';
10
- }
11
- export { getContrastColor };
@@ -1,14 +0,0 @@
1
- function getContrastColor(hexColor: string) {
2
- // Convert hex color to RGB format
3
- const red = parseInt(hexColor.slice(1, 3), 16);
4
- const green = parseInt(hexColor.slice(3, 5), 16);
5
- const blue = parseInt(hexColor.slice(5, 7), 16);
6
-
7
- // Calculate relative luminance of the color
8
- const luminance = (0.2126 * red + 0.7152 * green + 0.0722 * blue) / 255;
9
-
10
- // Return black or white depending on the luminance value
11
- return luminance > 0.5 ? '#000000' : '#ffffff';
12
- }
13
-
14
- export { getContrastColor };
@@ -1,18 +0,0 @@
1
- export const formatFonts = (families, defaultWeights = [400]) => {
2
- const formatted = [];
3
- for (const font in families) {
4
- const formattedFont = font.replace(/ /g, '+'); // Replace spaces with '+'
5
- const weights = families[font];
6
- if (weights === true) {
7
- for (const weight of defaultWeights) {
8
- formatted.push(`${formattedFont}:${weight}`);
9
- }
10
- }
11
- else if (Array.isArray(weights)) {
12
- for (const weight of weights) {
13
- formatted.push(`${formattedFont}:${weight}`);
14
- }
15
- }
16
- }
17
- return formatted;
18
- };
@@ -1,24 +0,0 @@
1
- type FontFamilies = {
2
- [font: string]: boolean | number[];
3
- };
4
-
5
- export const formatFonts = (families: FontFamilies, defaultWeights: number[] = [400]): string[] => {
6
- const formatted: string[] = [];
7
-
8
- for (const font in families) {
9
- const formattedFont = font.replace(/ /g, '+'); // Replace spaces with '+'
10
- const weights = families[font];
11
-
12
- if (weights === true) {
13
- for (const weight of defaultWeights) {
14
- formatted.push(`${formattedFont}:${weight}`);
15
- }
16
- } else if (Array.isArray(weights)) {
17
- for (const weight of weights) {
18
- formatted.push(`${formattedFont}:${weight}`);
19
- }
20
- }
21
- }
22
-
23
- return formatted;
24
- };
@@ -1,59 +0,0 @@
1
- import { unref } from 'vue';
2
- function convertBoolean(value) {
3
- if (value === 'true' || value === 'false') {
4
- return value === 'true';
5
- }
6
- else {
7
- return value;
8
- }
9
- }
10
- function mapCondition(condition, target) {
11
- switch (condition) {
12
- case 'is_empty':
13
- return `!$get(${target}).value)`;
14
- case 'is_filled':
15
- return `$get(${target}).value)`;
16
- default:
17
- return undefined;
18
- }
19
- }
20
- export function mapConditions(conditions) {
21
- // Both $el and $cmp schema nodes can leverage an if property that roughly equates to a v-if in Vue. If the expression assigned to the if property is truthy, the node is rendered, otherwise it is not:
22
- }
23
- export function transformSchema(schema) {
24
- // Loop through the form schema from Directus
25
- // This is required for FormKit to work
26
- const items = unref(schema);
27
- return items.map((item) => {
28
- const { conditions, field, name, children, ...props } = item;
29
- // console.log('conditions', conditions);
30
- // console.log('mapCondition', mapCondition(conditions[0].condition, field));
31
- const cmpSchema = {
32
- $cmp: item.$el ? item.$el : 'FormKit',
33
- children: children,
34
- // if: conditions ? mapCondition(conditions[0].condition, field) : undefined,
35
- props: {
36
- id: name,
37
- ...props,
38
- },
39
- };
40
- // Switch statement to handle item widths
41
- switch (item.width) {
42
- case '33':
43
- cmpSchema.props.outerClass = 'md:col-span-2';
44
- break;
45
- case '50':
46
- cmpSchema.props.outerClass = 'md:col-span-3';
47
- break;
48
- case '67':
49
- cmpSchema.props.outerClass = 'md:col-span-4';
50
- break;
51
- case '100':
52
- cmpSchema.props.outerClass = 'md:col-span-6';
53
- break;
54
- default:
55
- cmpSchema.props.outerClass = 'md:col-span-6';
56
- }
57
- return cmpSchema;
58
- });
59
- }
@@ -1,75 +0,0 @@
1
- import { unref } from 'vue';
2
-
3
- export interface Condition {
4
- field: string;
5
- action: 'show' | 'hide';
6
- condition: 'is_empty' | 'is_filled' | 'contains' | 'not_contains' | 'equals' | 'not_equal';
7
- value: string;
8
- }
9
-
10
- function convertBoolean(value: string) {
11
- if (value === 'true' || value === 'false') {
12
- return value === 'true';
13
- } else {
14
- return value;
15
- }
16
- }
17
-
18
- function mapCondition(condition: Condition['condition'], target: string) {
19
- switch (condition) {
20
- case 'is_empty':
21
- return `!$get(${target}).value)`;
22
- case 'is_filled':
23
- return `$get(${target}).value)`;
24
- default:
25
- return undefined;
26
- }
27
- }
28
-
29
- export function mapConditions(conditions: Condition[]) {
30
- // Both $el and $cmp schema nodes can leverage an if property that roughly equates to a v-if in Vue. If the expression assigned to the if property is truthy, the node is rendered, otherwise it is not:
31
- }
32
-
33
- export function transformSchema(schema: Array<object>) {
34
- // Loop through the form schema from Directus
35
- // This is required for FormKit to work
36
- const items = unref(schema);
37
- return items.map((item: any) => {
38
- const { conditions, field, name, children, ...props } = item;
39
-
40
- // console.log('conditions', conditions);
41
- // console.log('mapCondition', mapCondition(conditions[0].condition, field));
42
-
43
- const cmpSchema = {
44
- $cmp: item.$el ? item.$el : 'FormKit',
45
-
46
- children: children,
47
- // if: conditions ? mapCondition(conditions[0].condition, field) : undefined,
48
-
49
- props: {
50
- id: name,
51
- ...props,
52
- },
53
- };
54
-
55
- // Switch statement to handle item widths
56
- switch (item.width) {
57
- case '33':
58
- cmpSchema.props.outerClass = 'md:col-span-2';
59
- break;
60
- case '50':
61
- cmpSchema.props.outerClass = 'md:col-span-3';
62
- break;
63
- case '67':
64
- cmpSchema.props.outerClass = 'md:col-span-4';
65
- break;
66
- case '100':
67
- cmpSchema.props.outerClass = 'md:col-span-6';
68
- break;
69
- default:
70
- cmpSchema.props.outerClass = 'md:col-span-6';
71
- }
72
-
73
- return cmpSchema;
74
- });
75
- }
@@ -1,36 +0,0 @@
1
- export const fileIconMap = {
2
- folder: 'material-symbols:folder',
3
- // Images
4
- 'image/jpeg': 'material-symbols:image',
5
- 'image/png': 'material-symbols:image',
6
- 'image/gif': 'material-symbols:image',
7
- 'image/svg+xml': 'material-symbols:image',
8
- 'image/webp': 'material-symbols:image',
9
- // Videos
10
- 'video/mp4': 'material-symbols:smart-display',
11
- 'video/quicktime': 'material-symbols:smart-display',
12
- // Audio
13
- 'audio/mp3': 'material-symbols:audio-file',
14
- 'audio/aac': 'material-symbols:audio-file',
15
- 'audio/wav': 'material-symbols:audio-file',
16
- 'audio/ogg': 'material-symbols:audio-file',
17
- // Text
18
- 'text/csv': 'material-symbols:csv',
19
- 'text/plain': 'material-symbols:text-snippet',
20
- // Files
21
- 'application/pdf': 'material-symbols:picture-as-pdf-sharp',
22
- 'application/vnd.ms-excel': 'material-symbols:sheets',
23
- 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'material-symbols:sheets',
24
- 'application/msword': 'material-symbols:docs',
25
- 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'material-symbols:docs',
26
- 'application/vnd.ms-powerpoint': 'material-symbols:slides',
27
- 'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'material-symbols:slides',
28
- };
29
- export function getFileIcon(filetype) {
30
- if (!filetype)
31
- return 'material-symbols:attachment';
32
- if (typeof filetype === 'string' && !(filetype in fileIconMap)) {
33
- return 'material-symbols:attachment';
34
- }
35
- return fileIconMap[filetype];
36
- }
@@ -1,62 +0,0 @@
1
- type FileType =
2
- | 'folder'
3
- | 'image/jpeg'
4
- | 'image/png'
5
- | 'image/gif'
6
- | 'image/svg+xml'
7
- | 'image/webp'
8
- | 'video/mp4'
9
- | 'video/quicktime'
10
- | 'audio/mp3'
11
- | 'audio/aac'
12
- | 'audio/wav'
13
- | 'audio/ogg'
14
- | 'text/csv'
15
- | 'text/plain'
16
- | 'application/pdf'
17
- | 'application/vnd.ms-excel'
18
- | 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
19
- | 'application/msword'
20
- | 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
21
- | 'application/vnd.ms-powerpoint'
22
- | 'application/vnd.openxmlformats-officedocument.presentationml.presentation';
23
-
24
- export const fileIconMap: Record<FileType, string> = {
25
- folder: 'material-symbols:folder',
26
- // Images
27
- 'image/jpeg': 'material-symbols:image',
28
- 'image/png': 'material-symbols:image',
29
- 'image/gif': 'material-symbols:image',
30
- 'image/svg+xml': 'material-symbols:image',
31
- 'image/webp': 'material-symbols:image',
32
- // Videos
33
- 'video/mp4': 'material-symbols:smart-display',
34
- 'video/quicktime': 'material-symbols:smart-display',
35
- // Audio
36
- 'audio/mp3': 'material-symbols:audio-file',
37
- 'audio/aac': 'material-symbols:audio-file',
38
- 'audio/wav': 'material-symbols:audio-file',
39
- 'audio/ogg': 'material-symbols:audio-file',
40
- // Text
41
- 'text/csv': 'material-symbols:csv',
42
- 'text/plain': 'material-symbols:text-snippet',
43
-
44
- // Files
45
- 'application/pdf': 'material-symbols:picture-as-pdf-sharp',
46
- 'application/vnd.ms-excel': 'material-symbols:sheets',
47
- 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'material-symbols:sheets',
48
- 'application/msword': 'material-symbols:docs',
49
- 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'material-symbols:docs',
50
- 'application/vnd.ms-powerpoint': 'material-symbols:slides',
51
- 'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'material-symbols:slides',
52
- };
53
-
54
- export function getFileIcon(filetype: FileType | string | null | undefined) {
55
- if (!filetype) return 'material-symbols:attachment';
56
-
57
- if (typeof filetype === 'string' && !(filetype in fileIconMap)) {
58
- return 'material-symbols:attachment';
59
- }
60
-
61
- return fileIconMap[filetype as FileType];
62
- }