@tekkare/romulus 0.1.0 → 0.1.5

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/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">=3.16.0"
6
6
  },
7
- "version": "0.1.0",
7
+ "version": "0.1.4",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "0.8.4",
10
10
  "unbuild": "2.0.0"
package/dist/module.mjs CHANGED
@@ -10,13 +10,14 @@ const module = defineNuxtModule({
10
10
  }
11
11
  },
12
12
  defaults: {
13
- prefix: "R"
13
+ prefix: "Rm"
14
14
  },
15
15
  async setup(options, nuxt) {
16
16
  const { resolve } = createResolver(import.meta.url);
17
17
  const runtimeDir = fileURLToPath(new URL("./runtime", import.meta.url));
18
18
  nuxt.options.css.push(resolve(runtimeDir, "assets/styles/tokens.css"));
19
19
  nuxt.options.css.push(resolve(runtimeDir, "assets/styles/base.css"));
20
+ nuxt.options.css.push(resolve(runtimeDir, "assets/styles/utilities.css"));
20
21
  await addComponentsDir({
21
22
  path: resolve(runtimeDir, "components"),
22
23
  prefix: options.prefix
@@ -1 +1 @@
1
- :root{--r-color-primary-50:#eef2ff;--r-color-primary-100:#e0e7ff;--r-color-primary-200:#c7d2fe;--r-color-primary-300:#a5b4fc;--r-color-primary-400:#818cf8;--r-color-primary-500:#6366f1;--r-color-primary-600:#4f46e5;--r-color-primary-700:#4338ca;--r-color-primary-800:#3730a3;--r-color-primary-900:#312e81;--r-color-gray-50:#f9fafb;--r-color-gray-100:#f3f4f6;--r-color-gray-200:#e5e7eb;--r-color-gray-300:#d1d5db;--r-color-gray-400:#9ca3af;--r-color-gray-500:#6b7280;--r-color-gray-600:#4b5563;--r-color-gray-700:#374151;--r-color-gray-800:#1f2937;--r-color-gray-900:#111827;--r-color-gray-950:#030712;--r-color-success-500:#22c55e;--r-color-success-600:#16a34a;--r-color-warning-500:#f59e0b;--r-color-warning-600:#d97706;--r-color-danger-500:#ef4444;--r-color-danger-600:#dc2626;--r-color-white:#fff;--r-color-black:#000;--r-font-family:"Inter",ui-sans-serif,system-ui,-apple-system,sans-serif;--r-font-mono:"JetBrains Mono",ui-monospace,"Cascadia Code",monospace;--r-text-xs:0.75rem;--r-text-sm:0.875rem;--r-text-base:1rem;--r-text-lg:1.125rem;--r-text-xl:1.25rem;--r-text-2xl:1.5rem;--r-text-3xl:1.875rem;--r-text-4xl:2.25rem;--r-font-weight-normal:400;--r-font-weight-medium:500;--r-font-weight-semibold:600;--r-font-weight-bold:700;--r-leading-tight:1.25;--r-leading-normal:1.5;--r-leading-relaxed:1.75;--r-space-0:0;--r-space-1:0.25rem;--r-space-2:0.5rem;--r-space-3:0.75rem;--r-space-4:1rem;--r-space-5:1.25rem;--r-space-6:1.5rem;--r-space-8:2rem;--r-space-10:2.5rem;--r-space-12:3rem;--r-space-16:4rem;--r-space-20:5rem;--r-radius-none:0;--r-radius-sm:0.25rem;--r-radius-md:0.375rem;--r-radius-lg:0.5rem;--r-radius-xl:0.75rem;--r-radius-2xl:1rem;--r-radius-full:9999px;--r-shadow-xs:0 1px 2px 0 rgba(0,0,0,.05);--r-shadow-sm:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);--r-shadow-md:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);--r-shadow-lg:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--r-shadow-xl:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);--r-transition-fast:150ms cubic-bezier(0.4,0,0.2,1);--r-transition-base:200ms cubic-bezier(0.4,0,0.2,1);--r-transition-slow:300ms cubic-bezier(0.4,0,0.2,1)}
1
+ :root{--r-color-primary-50:#f0f4ff;--r-color-primary-100:#dce5fd;--r-color-primary-200:#b9cbfb;--r-color-primary-300:#96b0f9;--r-color-primary-400:#7395f5;--r-color-primary-500:#5a7ef2;--r-color-primary-600:#4164ee;--r-color-primary-700:#3451d4;--r-color-primary-800:#283fb0;--r-color-primary-900:#1c2d8a;--r-color-gray-50:#f9fafb;--r-color-gray-100:#f3f4f6;--r-color-gray-200:#e5e7eb;--r-color-gray-300:#d1d5db;--r-color-gray-400:#9ca3af;--r-color-gray-500:#6b7280;--r-color-gray-600:#4b5563;--r-color-gray-700:#374151;--r-color-gray-800:#1f2937;--r-color-gray-900:#111827;--r-color-gray-950:#030712;--r-color-success-500:#22c55e;--r-color-success-600:#16a34a;--r-color-warning-500:#f59e0b;--r-color-warning-600:#d97706;--r-color-danger-500:#ef4444;--r-color-danger-600:#dc2626;--r-color-white:#fff;--r-color-black:#000;--r-font-family:"Figtree",ui-sans-serif,system-ui,-apple-system,sans-serif;--r-font-mono:"JetBrains Mono",ui-monospace,"Cascadia Code",monospace;--r-text-xs:0.75rem;--r-text-sm:0.875rem;--r-text-base:1rem;--r-text-lg:1.125rem;--r-text-xl:1.25rem;--r-text-2xl:1.5rem;--r-text-3xl:1.875rem;--r-text-4xl:2.25rem;--r-font-weight-normal:400;--r-font-weight-medium:500;--r-font-weight-semibold:600;--r-font-weight-bold:700;--r-leading-tight:1.25;--r-leading-normal:1.5;--r-leading-relaxed:1.75;--r-space-0:0;--r-space-1:0.25rem;--r-space-2:0.5rem;--r-space-3:0.75rem;--r-space-4:1rem;--r-space-5:1.25rem;--r-space-6:1.5rem;--r-space-8:2rem;--r-space-10:2.5rem;--r-space-12:3rem;--r-space-16:4rem;--r-space-20:5rem;--r-radius-none:0;--r-radius-sm:0.25rem;--r-radius-md:0.375rem;--r-radius-lg:0.5rem;--r-radius-xl:0.75rem;--r-radius-2xl:1rem;--r-radius-full:9999px;--r-shadow-xs:0 1px 2px 0 rgba(0,0,0,.05);--r-shadow-sm:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);--r-shadow-md:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);--r-shadow-lg:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--r-shadow-xl:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);--r-transition-fast:150ms cubic-bezier(0.4,0,0.2,1);--r-transition-base:200ms cubic-bezier(0.4,0,0.2,1);--r-transition-slow:300ms cubic-bezier(0.4,0,0.2,1)}
@@ -0,0 +1 @@
1
+ .row{display:flex;flex-direction:row}.column{display:flex;flex-direction:column}.row-reverse{display:flex;flex-direction:row-reverse}.column-reverse{display:flex;flex-direction:column-reverse}.x-start{justify-content:flex-start}.x-center{justify-content:center}.x-end{justify-content:flex-end}.x-between{justify-content:space-between}.x-around{justify-content:space-around}.x-evenly{justify-content:space-evenly}.y-start{align-items:flex-start}.y-center{align-items:center}.y-end{align-items:flex-end}.y-between{align-content:space-between}.y-stretch{align-items:stretch}.y-baseline{align-items:baseline}.wrap{flex-wrap:wrap}.nowrap{flex-wrap:nowrap}.grow{flex-grow:1}.grow-0{flex-grow:0}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.flex-1{flex:1}.flex-auto{flex:auto}.flex-none{flex:none}.self-start{align-self:flex-start}.self-center{align-self:center}.self-end{align-self:flex-end}.self-stretch{align-self:stretch}.w-full{width:100%}.w-auto{width:auto}.w-fit{width:-moz-fit-content;width:fit-content}.min-w-0{min-width:0}.h-full{height:100%}.h-auto{height:auto}.h-screen{height:100vh}.min-h-0{min-height:0}.min-h-screen{min-height:100vh}.overflow-hidden{overflow:hidden}.overflow-auto{overflow:auto}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.relative{position:relative}.absolute{position:absolute}.fixed{position:fixed}.sticky{position:sticky}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.text-ellipsis{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.block{display:block}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.gap-4{gap:4px}.gap-6{gap:6px}.gap-8{gap:8px}.gap-10{gap:10px}.gap-12{gap:12px}.gap-14{gap:14px}.gap-16{gap:16px}.gap-18{gap:18px}.gap-20{gap:20px}.gap-22{gap:22px}.gap-24{gap:24px}.gap-26{gap:26px}.gap-28{gap:28px}.gap-30{gap:30px}.gap-32{gap:32px}.gap-34{gap:34px}.gap-36{gap:36px}.gap-38{gap:38px}.gap-40{gap:40px}.gap-42{gap:42px}.gap-44{gap:44px}.gap-46{gap:46px}.gap-48{gap:48px}.gap-50{gap:50px}.gap-52{gap:52px}.gap-54{gap:54px}.gap-56{gap:56px}.gap-58{gap:58px}.gap-60{gap:60px}.gap-62{gap:62px}.gap-64{gap:64px}.gap-66{gap:66px}.gap-68{gap:68px}.gap-70{gap:70px}.gap-72{gap:72px}.gap-74{gap:74px}.gap-76{gap:76px}.gap-78{gap:78px}.gap-80{gap:80px}.p-0{padding:0}.p-4{padding:4px}.p-8{padding:8px}.p-12{padding:12px}.p-16{padding:16px}.p-20{padding:20px}.p-24{padding:24px}.p-32{padding:32px}.p-40{padding:40px}.p-48{padding:48px}.px-4{padding-left:4px;padding-right:4px}.px-8{padding-left:8px;padding-right:8px}.px-12{padding-left:12px;padding-right:12px}.px-16{padding-left:16px;padding-right:16px}.px-20{padding-left:20px;padding-right:20px}.px-24{padding-left:24px;padding-right:24px}.px-32{padding-left:32px;padding-right:32px}.py-4{padding-bottom:4px;padding-top:4px}.py-8{padding-bottom:8px;padding-top:8px}.py-12{padding-bottom:12px;padding-top:12px}.py-16{padding-bottom:16px;padding-top:16px}.py-20{padding-bottom:20px;padding-top:20px}.py-24{padding-bottom:24px;padding-top:24px}.py-32{padding-bottom:32px;padding-top:32px}.m-0{margin:0}.m-auto{margin:auto}.mx-auto{margin-right:auto}.ml-auto,.mx-auto{margin-left:auto}.mr-auto{margin-right:auto}.mt-auto{margin-top:auto}.mb-auto{margin-bottom:auto}.border{border:1px solid var(--r-color-gray-200)}.border-t{border-top:1px solid var(--r-color-gray-200)}.border-b{border-bottom:1px solid var(--r-color-gray-200)}.border-l{border-left:1px solid var(--r-color-gray-200)}.border-r{border-right:1px solid var(--r-color-gray-200)}.border-none{border:none}.rounded-sm{border-radius:var(--r-radius-sm)}.rounded-md{border-radius:var(--r-radius-md)}.rounded-lg{border-radius:var(--r-radius-lg)}.rounded-xl{border-radius:var(--r-radius-xl)}.rounded-full{border-radius:var(--r-radius-full)}.cursor-pointer{cursor:pointer}.cursor-default{cursor:default}
@@ -0,0 +1,95 @@
1
+ <script setup lang="ts">
2
+ export interface RmActionBarProps {
3
+ /** Width of the action panel in px */
4
+ panelWidth?: number
5
+ /** Width of the drawer when open in px */
6
+ drawerWidth?: number
7
+ /** Whether the drawer is open */
8
+ drawerOpen?: boolean
9
+ }
10
+
11
+ const props = withDefaults(defineProps<RmActionBarProps>(), {
12
+ panelWidth: 280,
13
+ drawerWidth: 640,
14
+ drawerOpen: false,
15
+ })
16
+
17
+ const emit = defineEmits<{
18
+ 'update:drawerOpen': [value: boolean]
19
+ 'closeDrawer': []
20
+ }>()
21
+
22
+ const totalWidth = computed(() => {
23
+ return props.drawerOpen
24
+ ? props.panelWidth + props.drawerWidth
25
+ : props.panelWidth
26
+ })
27
+
28
+ function closeDrawer() {
29
+ emit('update:drawerOpen', false)
30
+ emit('closeDrawer')
31
+ }
32
+ </script>
33
+
34
+
35
+ <template>
36
+ <div
37
+ class="rm-action-bar"
38
+ :style="{ width: totalWidth + 'px' }"
39
+ >
40
+ <!-- Panel -->
41
+ <div class="rm-action-bar__panel" :style="{ width: panelWidth + 'px' }">
42
+ <div class="rm-action-bar__header">
43
+ <slot name="header" />
44
+ </div>
45
+
46
+ <div v-if="$slots.tabs" class="rm-action-bar__tabs">
47
+ <slot name="tabs" />
48
+ </div>
49
+
50
+ <div class="rm-action-bar__content">
51
+ <slot name="actions" />
52
+ </div>
53
+
54
+ <div v-if="$slots.footer" class="rm-action-bar__footer">
55
+ <slot name="footer" />
56
+ </div>
57
+ </div>
58
+
59
+ <!-- Overlay -->
60
+ <Teleport to="body">
61
+ <Transition name="rm-action-bar-overlay">
62
+ <div
63
+ v-if="drawerOpen"
64
+ class="rm-action-bar__overlay"
65
+ @click="closeDrawer"
66
+ />
67
+ </Transition>
68
+ </Teleport>
69
+
70
+ <!-- Drawer -->
71
+ <Transition name="rm-action-bar-drawer">
72
+ <div
73
+ v-if="drawerOpen"
74
+ class="rm-action-bar__drawer"
75
+ >
76
+ <div class="rm-action-bar__drawer-header">
77
+ <slot name="drawer-header" />
78
+ <button class="rm-action-bar__drawer-close" @click="closeDrawer">
79
+ <RmIcon name="X" :size="20" />
80
+ </button>
81
+ </div>
82
+ <div class="rm-action-bar__drawer-body">
83
+ <slot name="drawer" />
84
+ </div>
85
+ <div v-if="$slots['drawer-footer']" class="rm-action-bar__drawer-footer">
86
+ <slot name="drawer-footer" />
87
+ </div>
88
+ </div>
89
+ </Transition>
90
+ </div>
91
+ </template>
92
+
93
+ <style scoped>
94
+ .rm-action-bar{bottom:0;display:flex;position:fixed;right:0;top:0;transition:width .3s cubic-bezier(.4,0,.2,1);z-index:61}.rm-action-bar__panel{background-color:var(--r-color-white);border-left:1px solid var(--r-color-gray-200);display:flex;flex-direction:column;flex-shrink:0;height:100%}.rm-action-bar__header{align-items:center;display:flex;gap:12px;justify-content:space-between;min-height:64px;padding:0 16px}.rm-action-bar__tabs{border-bottom:1px solid var(--r-color-gray-200);padding:8px 16px 12px}.rm-action-bar__content{display:flex;flex:1;flex-direction:column;overflow-y:auto}.rm-action-bar__footer{border-top:1px solid var(--r-color-gray-200);padding:16px}.rm-action-bar__overlay{backdrop-filter:blur(2px);background-color:rgba(0,0,0,.2);inset:0;position:fixed;z-index:60}.rm-action-bar-overlay-enter-active,.rm-action-bar-overlay-leave-active{transition:opacity .25s ease}.rm-action-bar-overlay-enter-from,.rm-action-bar-overlay-leave-to{opacity:0}.rm-action-bar__drawer{background-color:var(--r-color-white);border-left:1px solid var(--r-color-gray-200);display:flex;flex:1;flex-direction:column;min-width:0;overflow:hidden}.rm-action-bar__drawer-header{align-items:center;border-bottom:1px solid var(--r-color-gray-200);display:flex;justify-content:space-between;min-height:64px;padding:0 24px}.rm-action-bar__drawer-close{align-items:center;background:none;border:none;border-radius:var(--r-radius-md);color:var(--r-color-gray-500);cursor:pointer;display:flex;flex-shrink:0;height:32px;justify-content:center;transition:all var(--r-transition-fast);width:32px}.rm-action-bar__drawer-close:hover{background-color:var(--r-color-gray-100);color:var(--r-color-gray-700)}.rm-action-bar__drawer-body{flex:1;overflow-y:auto;padding:24px}.rm-action-bar__drawer-footer{border-top:1px solid var(--r-color-gray-200);padding:16px 24px}.rm-action-bar-drawer-enter-active,.rm-action-bar-drawer-leave-active{transition:opacity .25s ease}.rm-action-bar-drawer-enter-from,.rm-action-bar-drawer-leave-to{opacity:0}
95
+ </style>
@@ -0,0 +1,110 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ PhTrash,
4
+ PhPlus,
5
+ PhLineVertical,
6
+ PhMinus,
7
+ } from '@phosphor-icons/vue'
8
+
9
+ interface SearchRow {
10
+ id: number
11
+ type: 'and' | 'or' | 'not'
12
+ value: string
13
+ }
14
+
15
+ defineProps<{
16
+ visible: boolean
17
+ }>()
18
+
19
+ defineEmits<{
20
+ close: []
21
+ saveSearch: []
22
+ }>()
23
+
24
+ let nextId = 1
25
+
26
+ const rows = ref<SearchRow[]>([
27
+ { id: nextId++, type: 'and', value: '' },
28
+ ])
29
+
30
+ function addRow(type: 'and' | 'or' | 'not') {
31
+ rows.value.push({ id: nextId++, type, value: '' })
32
+ }
33
+
34
+ function removeRow(id: number) {
35
+ rows.value = rows.value.filter(r => r.id !== id)
36
+ if (rows.value.length === 0) {
37
+ rows.value.push({ id: nextId++, type: 'and', value: '' })
38
+ }
39
+ }
40
+
41
+ function clearAll() {
42
+ rows.value = [{ id: nextId++, type: 'and', value: '' }]
43
+ }
44
+
45
+ const typeColors: Record<string, string> = {
46
+ and: '#4164EE',
47
+ or: '#07816F',
48
+ not: '#F7520E',
49
+ }
50
+ </script>
51
+
52
+ <template>
53
+ <div v-if="visible" class="advanced-search">
54
+ <div class="advanced-search__header">
55
+ <h2 class="advanced-search__title">Advanced search</h2>
56
+ <RmButton variant="outline" size="sm" @click="$emit('saveSearch')">
57
+ Save search
58
+ <PhLineVertical :size="14" />
59
+ </RmButton>
60
+ </div>
61
+
62
+ <div class="advanced-search__rows">
63
+ <div
64
+ v-for="row in rows"
65
+ :key="row.id"
66
+ class="advanced-search__row"
67
+ >
68
+ <span
69
+ class="advanced-search__type"
70
+ :style="{ color: typeColors[row.type] }"
71
+ >
72
+ {{ row.type.charAt(0).toUpperCase() + row.type.slice(1) }}
73
+ </span>
74
+ <input
75
+ v-model="row.value"
76
+ type="text"
77
+ :placeholder="row.value ? '' : 'Add a keyword'"
78
+ class="advanced-search__input"
79
+ :class="{ 'advanced-search__input--not': row.type === 'not' }"
80
+ >
81
+ <button class="advanced-search__delete" @click="removeRow(row.id)">
82
+ <PhTrash :size="16" />
83
+ </button>
84
+ </div>
85
+ </div>
86
+
87
+ <div class="advanced-search__actions">
88
+ <button class="advanced-search__add" @click="addRow('and')">
89
+ <PhPlus :size="12" />
90
+ and
91
+ </button>
92
+ <button class="advanced-search__add" @click="addRow('or')">
93
+ <PhLineVertical :size="12" />
94
+ or
95
+ </button>
96
+ <button class="advanced-search__add" @click="addRow('not')">
97
+ <PhMinus :size="12" />
98
+ not
99
+ </button>
100
+ </div>
101
+
102
+ <button class="advanced-search__clear" @click="clearAll">
103
+ Clear All
104
+ </button>
105
+ </div>
106
+ </template>
107
+
108
+ <style scoped>
109
+ .advanced-search{background-color:var(--r-color-white);border-bottom:1px solid var(--r-color-gray-200);padding:20px 24px}.advanced-search__header{align-items:center;display:flex;justify-content:space-between;margin-bottom:20px}.advanced-search__title{color:var(--r-color-gray-900);font-family:var(--r-font-family);font-size:16px;font-weight:700;margin:0}.advanced-search__rows{display:flex;flex-direction:column;gap:12px;margin-bottom:16px}.advanced-search__row{align-items:center;display:flex;gap:12px}.advanced-search__type{flex-shrink:0;font-weight:600;min-width:32px}.advanced-search__input,.advanced-search__type{font-family:var(--r-font-family);font-size:var(--r-text-sm)}.advanced-search__input{background:#fff;border:1px solid #dbdef0;border-radius:6px;color:var(--r-color-gray-900);flex:1;height:36px;outline:none;padding:0 12px;transition:border-color var(--r-transition-fast)}.advanced-search__input:focus{border-color:#4164ee}.advanced-search__input--not,.advanced-search__input--not:focus{border-color:#f7520e}.advanced-search__input::-moz-placeholder{color:var(--r-color-gray-400)}.advanced-search__input::placeholder{color:var(--r-color-gray-400)}.advanced-search__delete{align-items:center;background:none;border:none;border-radius:4px;color:var(--r-color-gray-400);cursor:pointer;display:flex;justify-content:center;padding:4px;transition:color var(--r-transition-fast)}.advanced-search__delete:hover{color:var(--r-color-danger-500)}.advanced-search__actions{display:flex;gap:8px;margin-bottom:16px}.advanced-search__add{align-items:center;background:#fff;border:1px solid #dbdef0;border-radius:6px;color:var(--r-color-gray-600);cursor:pointer;display:inline-flex;font-family:var(--r-font-family);font-size:var(--r-text-sm);font-weight:var(--r-font-weight-medium);gap:4px;height:32px;padding:4px 12px;transition:all var(--r-transition-fast)}.advanced-search__add:hover{border-color:var(--r-color-gray-300);box-shadow:0 1px 2px 0 rgba(30,41,59,.1)}.advanced-search__clear{background:none;border:1px solid var(--r-color-danger-500);border-radius:6px;color:var(--r-color-danger-500);cursor:pointer;font-family:var(--r-font-family);font-size:var(--r-text-sm);font-weight:var(--r-font-weight-medium);padding:4px 12px;transition:all var(--r-transition-fast)}.advanced-search__clear:hover{background-color:var(--r-color-danger-500);color:#fff}
110
+ </style>
@@ -0,0 +1,46 @@
1
+ <script setup lang="ts">
2
+ export interface RmBreadcrumbItem {
3
+ /** Display label */
4
+ label: string
5
+ /** Optional icon name (Phosphor) — only shown on first item by default */
6
+ icon?: string
7
+ /** Navigation path */
8
+ to?: string
9
+ }
10
+
11
+ export interface RmBreadcrumbProps {
12
+ /** Breadcrumb items */
13
+ items: RmBreadcrumbItem[]
14
+ }
15
+
16
+ defineProps<RmBreadcrumbProps>()
17
+ </script>
18
+
19
+ <template>
20
+ <nav class="rm-breadcrumb">
21
+ <template v-for="(item, idx) in items" :key="idx">
22
+ <span v-if="idx > 0" class="rm-breadcrumb__sep">/</span>
23
+ <NuxtLink
24
+ v-if="item.to"
25
+ :to="item.to"
26
+ class="rm-breadcrumb__item"
27
+ :class="{ 'rm-breadcrumb__item--first': idx === 0 }"
28
+ >
29
+ <RmIcon v-if="item.icon" :name="item.icon" :size="16" weight="duotone" class="rm-breadcrumb__icon" />
30
+ {{ item.label }}
31
+ </NuxtLink>
32
+ <span
33
+ v-else
34
+ class="rm-breadcrumb__item"
35
+ :class="{ 'rm-breadcrumb__item--first': idx === 0 }"
36
+ >
37
+ <RmIcon v-if="item.icon" :name="item.icon" :size="16" weight="duotone" class="rm-breadcrumb__icon" />
38
+ {{ item.label }}
39
+ </span>
40
+ </template>
41
+ </nav>
42
+ </template>
43
+
44
+ <style scoped>
45
+ .rm-breadcrumb{align-items:center;color:var(--r-color-gray-400);display:flex;font-family:var(--r-font-family);font-size:14px;gap:8px}.rm-breadcrumb__sep{color:var(--r-color-gray-300)}.rm-breadcrumb__item{align-items:center;color:var(--r-color-gray-400);display:inline-flex;gap:6px;text-decoration:none;transition:color var(--r-transition-fast)}.rm-breadcrumb__item--first{color:var(--r-color-gray-700);font-weight:500}a.rm-breadcrumb__item:hover{color:var(--r-color-primary-600)}.rm-breadcrumb__icon{color:var(--r-color-primary-500)}
46
+ </style>
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
- export interface RButtonProps {
2
+ export interface RmButtonProps {
3
3
  /** Visual style variant */
4
- variant?: 'primary' | 'secondary' | 'outline' | 'ghost'
4
+ variant?: 'primary-fill' | 'primary-stroke' | 'discovery-fill' | 'discovery-stroke' | 'databank-fill' | 'databank-stroke' | 'pharma-fill' | 'pharma-stroke' | 'secondary' | 'outline' | 'ghost'
5
5
  /** Size of the button */
6
6
  size?: 'sm' | 'md' | 'lg'
7
7
  /** Whether the button is disabled */
@@ -10,14 +10,20 @@ export interface RButtonProps {
10
10
  block?: boolean
11
11
  /** HTML button type attribute */
12
12
  type?: 'button' | 'submit' | 'reset'
13
+ /** Phosphor icon name before the label */
14
+ prefixIcon?: string
15
+ /** Phosphor icon name after the label */
16
+ suffixIcon?: string
13
17
  }
14
18
 
15
- withDefaults(defineProps<RButtonProps>(), {
16
- variant: 'primary',
19
+ withDefaults(defineProps<RmButtonProps>(), {
20
+ variant: 'primary-fill',
17
21
  size: 'md',
18
22
  disabled: false,
19
23
  block: false,
20
24
  type: 'button',
25
+ prefixIcon: undefined,
26
+ suffixIcon: undefined,
21
27
  })
22
28
 
23
29
  defineEmits<{
@@ -30,17 +36,19 @@ defineEmits<{
30
36
  :type="type"
31
37
  :disabled="disabled"
32
38
  :class="[
33
- 'r-button',
34
- `r-button--${variant}`,
35
- `r-button--${size}`,
36
- { 'r-button--block': block },
39
+ 'rm-button',
40
+ `rm-button--${variant}`,
41
+ `rm-button--${size}`,
42
+ { 'rm-button--block': block },
37
43
  ]"
38
44
  @click="$emit('click', $event)"
39
45
  >
46
+ <RmIcon v-if="prefixIcon" :name="prefixIcon" :size="size === 'sm' ? 14 : 16" />
40
47
  <slot />
48
+ <RmIcon v-if="suffixIcon" :name="suffixIcon" :size="size === 'sm' ? 14 : 16" />
41
49
  </button>
42
50
  </template>
43
51
 
44
52
  <style scoped>
45
- .r-button{align-items:center;border:1px solid transparent;border-radius:var(--r-radius-lg);cursor:pointer;display:inline-flex;font-weight:var(--r-font-weight-medium);gap:var(--r-space-2);justify-content:center;transition:all var(--r-transition-fast);-webkit-user-select:none;-moz-user-select:none;user-select:none;white-space:nowrap}.r-button:focus-visible{outline:2px solid var(--r-color-primary-500);outline-offset:2px}.r-button:disabled{cursor:not-allowed;opacity:.5}.r-button--sm{font-size:var(--r-text-sm);height:2rem;padding:var(--r-space-1) var(--r-space-3)}.r-button--md{font-size:var(--r-text-sm);height:2.5rem;padding:var(--r-space-2) var(--r-space-4)}.r-button--lg{font-size:var(--r-text-base);height:3rem;padding:var(--r-space-3) var(--r-space-6)}.r-button--primary{background-color:var(--r-color-primary-600);color:var(--r-color-white)}.r-button--primary:hover:not(:disabled){background-color:var(--r-color-primary-700)}.r-button--primary:active:not(:disabled){background-color:var(--r-color-primary-800)}.r-button--secondary{background-color:var(--r-color-gray-100);color:var(--r-color-gray-800)}.r-button--secondary:hover:not(:disabled){background-color:var(--r-color-gray-200)}.r-button--secondary:active:not(:disabled){background-color:var(--r-color-gray-300)}.r-button--outline{background-color:transparent;border-color:var(--r-color-gray-300);color:var(--r-color-gray-700)}.r-button--outline:hover:not(:disabled){background-color:var(--r-color-gray-50);border-color:var(--r-color-gray-400)}.r-button--outline:active:not(:disabled){background-color:var(--r-color-gray-100)}.r-button--ghost{background-color:transparent;color:var(--r-color-gray-700)}.r-button--ghost:hover:not(:disabled){background-color:var(--r-color-gray-100)}.r-button--ghost:active:not(:disabled){background-color:var(--r-color-gray-200)}.r-button--block{width:100%}
53
+ .rm-button{align-items:center;border:1px solid transparent;border-radius:6px;cursor:pointer;display:inline-flex;font-family:var(--r-font-family);font-weight:500;gap:8px;justify-content:center;transition:all var(--r-transition-fast);-webkit-user-select:none;-moz-user-select:none;user-select:none;white-space:nowrap}.rm-button:focus-visible{outline:2px solid var(--r-color-primary-500);outline-offset:2px}.rm-button:disabled{cursor:not-allowed;opacity:.5}.rm-button--sm{font-size:13px;height:32px;padding:6px 16px}.rm-button--md{font-size:14px;height:40px;padding:10px 24px}.rm-button--lg{font-size:15px;height:48px;padding:12px 28px}.rm-button--primary-fill{background:linear-gradient(180deg,rgba(65,100,238,.8),rgba(65,100,238,.9));border-color:#4164ee;box-shadow:0 2px 3px 0 rgba(65,100,238,.5);color:var(--r-color-white)}.rm-button--primary-fill:hover:not(:disabled){background:linear-gradient(180deg,#4164ee,#4164ee);box-shadow:0 2px 4px 0 rgba(65,100,238,.6)}.rm-button--primary-fill:active:not(:disabled){background:#3451d4;box-shadow:0 1px 2px 0 rgba(65,100,238,.4)}.rm-button--primary-stroke{box-shadow:0 2px 3px 0 rgba(65,100,238,.5);color:#4164ee}.rm-button--primary-stroke,.rm-button--primary-stroke:hover:not(:disabled){background:linear-gradient(180deg,hsla(0,0%,100%,.5),hsla(0,0%,100%,.9));border-color:#4164ee}.rm-button--primary-stroke:hover:not(:disabled){box-shadow:none}.rm-button--primary-stroke:active:not(:disabled){background:var(--r-color-primary-50);box-shadow:none}.rm-button--discovery-fill{background:linear-gradient(180deg,rgba(7,129,111,.8),rgba(7,129,111,.9));border-color:#07816f;box-shadow:0 2px 3px 0 rgba(7,129,111,.5);color:var(--r-color-white)}.rm-button--discovery-fill:hover:not(:disabled){background:linear-gradient(180deg,#07816f,#07816f);border-color:#07816f;box-shadow:none}.rm-button--discovery-fill:active:not(:disabled){background:linear-gradient(180deg,#066b5c,#066b5c);box-shadow:none}.rm-button--discovery-stroke{box-shadow:0 2px 3px 0 rgba(7,129,111,.5);color:#07816f}.rm-button--discovery-stroke,.rm-button--discovery-stroke:hover:not(:disabled){background:linear-gradient(180deg,hsla(0,0%,100%,.5),hsla(0,0%,100%,.9));border-color:#07816f}.rm-button--discovery-stroke:hover:not(:disabled){box-shadow:none}.rm-button--discovery-stroke:active:not(:disabled){background:rgba(7,129,111,.05);box-shadow:none}.rm-button--databank-fill{background:linear-gradient(180deg,rgba(224,67,128,.8),rgba(224,67,128,.9));border-color:#e04380;box-shadow:0 2px 3px 0 rgba(224,67,128,.5);color:var(--r-color-white)}.rm-button--databank-fill:hover:not(:disabled){background:linear-gradient(180deg,#e04380,#e04380);border-color:#e04380;box-shadow:none}.rm-button--databank-fill:active:not(:disabled){background:linear-gradient(180deg,#c73a6f,#c73a6f);box-shadow:none}.rm-button--databank-stroke{box-shadow:0 2px 3px 0 rgba(224,67,128,.5);color:#e04380}.rm-button--databank-stroke,.rm-button--databank-stroke:hover:not(:disabled){background:linear-gradient(180deg,hsla(0,0%,100%,.5),hsla(0,0%,100%,.9));border-color:#e04380}.rm-button--databank-stroke:hover:not(:disabled){box-shadow:none}.rm-button--databank-stroke:active:not(:disabled){background:rgba(224,67,128,.05);box-shadow:none}.rm-button--pharma-fill{background:linear-gradient(180deg,#ff7942,#f7520e);border-color:#f7520e;box-shadow:0 2px 3px 0 rgba(247,82,14,.5);color:var(--r-color-white)}.rm-button--pharma-fill:hover:not(:disabled){background:linear-gradient(180deg,#f7520e,#f7520e);border-color:#f7520e;box-shadow:none}.rm-button--pharma-fill:active:not(:disabled){background:linear-gradient(180deg,#d9470c,#d9470c);box-shadow:none}.rm-button--pharma-stroke{box-shadow:0 2px 3px 0 rgba(247,82,14,.5);color:#f7520e}.rm-button--pharma-stroke,.rm-button--pharma-stroke:hover:not(:disabled){background:linear-gradient(180deg,hsla(0,0%,100%,.5),hsla(0,0%,100%,.9));border-color:#f7520e}.rm-button--pharma-stroke:hover:not(:disabled){box-shadow:none}.rm-button--pharma-stroke:active:not(:disabled){background:rgba(247,82,14,.05);box-shadow:none}.rm-button--secondary{background-color:var(--r-color-gray-100);color:var(--r-color-gray-800)}.rm-button--secondary:hover:not(:disabled){background-color:var(--r-color-gray-200)}.rm-button--secondary:active:not(:disabled){background-color:var(--r-color-gray-300)}.rm-button--outline{background-color:transparent;border-color:var(--r-color-gray-300);color:var(--r-color-gray-700)}.rm-button--outline:hover:not(:disabled){background-color:var(--r-color-gray-50);border-color:var(--r-color-gray-400)}.rm-button--outline:active:not(:disabled){background-color:var(--r-color-gray-100)}.rm-button--ghost{background-color:transparent;color:var(--r-color-gray-700)}.rm-button--ghost:hover:not(:disabled){background-color:var(--r-color-gray-100)}.rm-button--ghost:active:not(:disabled){background-color:var(--r-color-gray-200)}.rm-button--block{width:100%}
46
54
  </style>
@@ -0,0 +1,79 @@
1
+ <script setup lang="ts">
2
+ export interface RmDrawerProps {
3
+ /** Whether the drawer is open */
4
+ modelValue: boolean
5
+ /** Drawer size */
6
+ size?: 'sm' | 'md' | 'lg' | 'xl' | 'full'
7
+ /** Position */
8
+ side?: 'right' | 'left'
9
+ /** Show overlay behind the drawer */
10
+ overlay?: boolean
11
+ /** Offset from the edge (e.g. '280px' to keep a panel visible) */
12
+ offset?: string
13
+ }
14
+
15
+ const props = withDefaults(defineProps<RmDrawerProps>(), {
16
+ size: 'lg',
17
+ side: 'right',
18
+ overlay: true,
19
+ offset: '0px',
20
+ })
21
+
22
+ const emit = defineEmits<{
23
+ 'update:modelValue': [value: boolean]
24
+ }>()
25
+
26
+ function close() {
27
+ emit('update:modelValue', false)
28
+ }
29
+
30
+ const sizeMap: Record<string, string> = {
31
+ sm: '360px',
32
+ md: '480px',
33
+ lg: '640px',
34
+ xl: '860px',
35
+ full: '100%',
36
+ }
37
+
38
+ const drawerWidth = computed(() => sizeMap[props.size] || props.size)
39
+ </script>
40
+
41
+ <template>
42
+ <Teleport to="body">
43
+ <Transition name="rm-drawer-overlay">
44
+ <div
45
+ v-if="modelValue && overlay"
46
+ class="rm-drawer-overlay"
47
+ @click="close"
48
+ />
49
+ </Transition>
50
+
51
+ <Transition :name="`rm-drawer-slide-${side}`">
52
+ <div
53
+ v-if="modelValue"
54
+ class="rm-drawer"
55
+ :class="[`rm-drawer--${side}`]"
56
+ :style="{ width: drawerWidth, [side]: offset }"
57
+ >
58
+ <div class="rm-drawer__header">
59
+ <slot name="header" />
60
+ <button class="rm-drawer__close" @click="close">
61
+ <RmIcon name="X" :size="20" />
62
+ </button>
63
+ </div>
64
+
65
+ <div class="rm-drawer__body">
66
+ <slot />
67
+ </div>
68
+
69
+ <div v-if="$slots.footer" class="rm-drawer__footer">
70
+ <slot name="footer" />
71
+ </div>
72
+ </div>
73
+ </Transition>
74
+ </Teleport>
75
+ </template>
76
+
77
+ <style scoped>
78
+ .rm-drawer-overlay{backdrop-filter:blur(2px);background-color:rgba(0,0,0,.2);inset:0;position:fixed;z-index:90}.rm-drawer{background-color:var(--r-color-white);bottom:0;box-shadow:var(--r-shadow-xl);display:flex;flex-direction:column;max-width:100%;position:fixed;top:0;z-index:91}.rm-drawer--right{border-left:1px solid var(--r-color-gray-200)}.rm-drawer--left{border-right:1px solid var(--r-color-gray-200)}.rm-drawer__header{align-items:center;border-bottom:1px solid var(--r-color-gray-200);display:flex;justify-content:space-between;min-height:64px;padding:16px 24px}.rm-drawer__close{align-items:center;background:none;border:none;border-radius:var(--r-radius-md);color:var(--r-color-gray-500);cursor:pointer;display:flex;flex-shrink:0;height:32px;justify-content:center;transition:all var(--r-transition-fast);width:32px}.rm-drawer__close:hover{background-color:var(--r-color-gray-100);color:var(--r-color-gray-700)}.rm-drawer__body{flex:1;overflow-y:auto;padding:24px}.rm-drawer__footer{border-top:1px solid var(--r-color-gray-200);padding:16px 24px}.rm-drawer-overlay-enter-active,.rm-drawer-overlay-leave-active{transition:opacity .25s ease}.rm-drawer-overlay-enter-from,.rm-drawer-overlay-leave-to{opacity:0}.rm-drawer-slide-right-enter-active,.rm-drawer-slide-right-leave-active{transition:transform .3s cubic-bezier(.4,0,.2,1)}.rm-drawer-slide-right-enter-from,.rm-drawer-slide-right-leave-to{transform:translateX(100%)}.rm-drawer-slide-left-enter-active,.rm-drawer-slide-left-leave-active{transition:transform .3s cubic-bezier(.4,0,.2,1)}.rm-drawer-slide-left-enter-from,.rm-drawer-slide-left-leave-to{transform:translateX(-100%)}
79
+ </style>
@@ -0,0 +1,26 @@
1
+ <script setup lang="ts">
2
+ import { PhCheck } from '@phosphor-icons/vue'
3
+
4
+ defineProps<{
5
+ label: string
6
+ }>()
7
+
8
+ const checked = defineModel<boolean>({ default: false })
9
+ </script>
10
+
11
+ <template>
12
+ <button
13
+ class="filter-checkbox"
14
+ :class="{ 'filter-checkbox--active': checked }"
15
+ @click="checked = !checked"
16
+ >
17
+ <span class="filter-checkbox__box" :class="{ 'filter-checkbox__box--checked': checked }">
18
+ <PhCheck v-if="checked" :size="10" weight="bold" />
19
+ </span>
20
+ <span>{{ label }}</span>
21
+ </button>
22
+ </template>
23
+
24
+ <style scoped>
25
+ .filter-checkbox{align-items:center;background:#fff;border:1px solid #dbdef0;border-radius:6px;box-sizing:border-box;color:var(--r-color-gray-600);cursor:pointer;display:flex;font-family:var(--r-font-family);font-size:var(--r-text-sm);font-weight:var(--r-font-weight-medium);gap:8px;height:32px;padding:8px;transition:all var(--r-transition-fast);white-space:nowrap;width:193px}.filter-checkbox:hover{border-color:var(--r-color-gray-300)}.filter-checkbox--active{border-color:#4164ee;box-shadow:0 0 8px 0 rgba(65,100,238,.1)}.filter-checkbox__box{align-items:center;border:1.5px solid var(--r-color-gray-300);border-radius:3px;display:flex;flex-shrink:0;height:16px;justify-content:center;transition:all var(--r-transition-fast);width:16px}.filter-checkbox__box--checked{background-color:#4164ee;border-color:#4164ee;color:#fff}
26
+ </style>