nitro-web 0.0.86 → 0.0.87

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 (41) hide show
  1. package/client/globals.ts +10 -6
  2. package/package.json +14 -6
  3. package/types/{required-globals.d.ts → globals.d.ts} +3 -1
  4. package/.editorconfig +0 -9
  5. package/components/auth/auth.api.js +0 -411
  6. package/components/auth/reset.tsx +0 -86
  7. package/components/auth/signin.tsx +0 -76
  8. package/components/auth/signup.tsx +0 -62
  9. package/components/billing/stripe.api.js +0 -268
  10. package/components/dashboard/dashboard.tsx +0 -32
  11. package/components/partials/element/accordion.tsx +0 -102
  12. package/components/partials/element/avatar.tsx +0 -40
  13. package/components/partials/element/button.tsx +0 -98
  14. package/components/partials/element/calendar.tsx +0 -125
  15. package/components/partials/element/dropdown.tsx +0 -248
  16. package/components/partials/element/filters.tsx +0 -194
  17. package/components/partials/element/github-link.tsx +0 -16
  18. package/components/partials/element/initials.tsx +0 -66
  19. package/components/partials/element/message.tsx +0 -141
  20. package/components/partials/element/modal.tsx +0 -90
  21. package/components/partials/element/sidebar.tsx +0 -195
  22. package/components/partials/element/tooltip.tsx +0 -154
  23. package/components/partials/element/topbar.tsx +0 -15
  24. package/components/partials/form/checkbox.tsx +0 -150
  25. package/components/partials/form/drop-handler.tsx +0 -68
  26. package/components/partials/form/drop.tsx +0 -141
  27. package/components/partials/form/field-color.tsx +0 -86
  28. package/components/partials/form/field-currency.tsx +0 -158
  29. package/components/partials/form/field-date.tsx +0 -252
  30. package/components/partials/form/field.tsx +0 -231
  31. package/components/partials/form/form-error.tsx +0 -27
  32. package/components/partials/form/location.tsx +0 -225
  33. package/components/partials/form/select.tsx +0 -360
  34. package/components/partials/is-first-render.ts +0 -14
  35. package/components/partials/not-found.tsx +0 -7
  36. package/components/partials/styleguide.tsx +0 -407
  37. package/semver-updater.cjs +0 -13
  38. package/tsconfig.json +0 -38
  39. package/tsconfig.types.json +0 -15
  40. package/types/core-only-globals.d.ts +0 -9
  41. package/types.ts +0 -60
@@ -1,141 +0,0 @@
1
- // Todo: show correct message type, e.g. error, warning, info, success `${store.message.type || 'success'}`
2
- import { isObject, isString, queryObject } from 'nitro-web/util'
3
- import { X, CircleCheck } from 'lucide-react'
4
- import { MessageObject } from 'nitro-web/types'
5
- import { twMerge } from 'nitro-web'
6
-
7
- type MessageProps = {
8
- className?: string
9
- classNameWrapper?: string
10
- position?: 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right'
11
- }
12
- /**
13
- * Shows a message
14
- * Triggered by navigating to a link with a valid query string, or by setting store.message to a string or more explicitly, to an object
15
- **/
16
- export function Message({ className, classNameWrapper, position='top-right' }: MessageProps) {
17
- const devDontHide = false
18
- const [store, setStore] = useTracked()
19
- const [visible, setVisible] = useState(false)
20
- const location = useLocation()
21
- const messageQueryMap = {
22
- 'added': { type: 'success', text: 'Added successfully 👍️' },
23
- 'created': { type: 'success', text: 'Created successfully 👍️' },
24
- 'error': { type: 'error', text: 'Sorry, there was an error' },
25
- 'oauth-error': { type: 'error', text: 'There was an error trying to signin, please try again' },
26
- 'removed': { type: 'success', text: 'Removed' },
27
- 'signin': { type: 'error', text: 'Please sign in to access this page' },
28
- 'updated': { type: 'success', text: 'Updated successfully' },
29
- 'unauth': { type: 'error', text: 'You are unauthorised' },
30
- }
31
- const colorMap = {
32
- 'error': 'text-danger',
33
- 'warning': 'text-warning',
34
- 'info': 'text-info',
35
- 'success': 'text-success',
36
- }
37
- const positionMap = {
38
- 'top-left': ['sm:items-start sm:justify-start', 'sm:translate-y-0 sm:translate-x-[-0.5rem]'],
39
- 'top-center': ['sm:items-start sm:justify-center', 'sm:translate-y-[-0.5rem]'],
40
- 'top-right': ['sm:items-start sm:justify-end', 'sm:translate-y-0 sm:translate-x-1'],
41
- 'bottom-left': ['sm:items-end sm:justify-start', 'sm:translate-y-0 sm:translate-x-[-0.5rem]'],
42
- 'bottom-center': ['sm:items-end sm:justify-center', 'sm:translate-y-1'],
43
- 'bottom-right': ['sm:items-end sm:justify-end', 'sm:translate-y-0 sm:translate-x-1'],
44
- }
45
- const color = colorMap[(store.message as MessageObject)?.type || 'success']
46
- const positionArr = positionMap[(position as keyof typeof positionMap)]
47
-
48
- useEffect(() => {
49
- return () => {
50
- setStore(s => ({ ...s, message: '' }))
51
- }
52
- }, [])
53
-
54
- useEffect(() => {
55
- // Finds a message in a query string and show it
56
- let message
57
- const query = queryObject(location.search, true)
58
- for (const key in query) {
59
- if (!query.hasOwnProperty(key)) continue
60
- for (const key2 in messageQueryMap) {
61
- if (key != key2) continue
62
- // @ts-expect-error
63
- message = { ...messageQueryMap[key] }
64
- if (query[key] !== true) message.text = decodeURIComponent(query[key])
65
- }
66
- }
67
- if (message) setStore(s => ({ ...s, message: message }))
68
- }, [location.search])
69
-
70
- useEffect(() => {
71
- // Message detection and autohiding
72
- const now = new Date().getTime()
73
- const messageObject = store.message as MessageObject
74
-
75
- if (!store.message) {
76
- return
77
- // Convert a string into a message object
78
- } else if (isString(store.message)) {
79
- setStore(s => ({ ...s, message: { type: 'success', text: store.message as string, date: now }}))
80
- // Add a date to the message
81
- } else if (!messageObject.date) {
82
- setStore(s => ({ ...s, message: { ...messageObject, date: now }}))
83
- // Show message and hide it again after some time. Send back cleanup if store.message changes
84
- } else if (messageObject && now - 500 < messageObject.date) {
85
- const timeout1 = setTimeout(() => setVisible(true), 50)
86
- if (messageObject.timeout !== 0 && !devDontHide) var timeout2 = setTimeout(hide, messageObject.timeout || 5000)
87
- return () => {
88
- clearTimeout(timeout1)
89
- clearTimeout(timeout2)
90
- }
91
- }
92
- }, [JSON.stringify(store.message)])
93
-
94
- function hide() {
95
- setVisible(false)
96
- setTimeout(() => setStore(s => ({ ...s, message: undefined })), 250)
97
- }
98
-
99
- return (
100
- <>
101
- {/* Global notification live region, render this permanently at the end of the document */}
102
- <div
103
- aria-live="assertive"
104
- className={`${twMerge(`pointer-events-none items-end justify-center fixed inset-0 flex px-4 py-6 sm:p-6 z-[101] nitro-message ${positionArr[0]} ${classNameWrapper || ''}`)}`}
105
- >
106
- <div className="flex flex-col items-center space-y-4">
107
- {isObject(store.message) && (
108
- <div className={twMerge(
109
- 'overflow-hidden translate-y-[0.5rem] opacity-0 pointer-events-auto max-w-[350px] rounded-md bg-white shadow-lg ring-1 ring-black/5 transition text-sm font-medium text-gray-900',
110
- positionArr[1],
111
- (visible ? 'translate-x-0 translate-y-0 sm:translate-x-0 sm:translate-y-0 opacity-100' : ''),
112
- className
113
- )}>
114
- <div className="p-3">
115
- <div className="flex items-start gap-3 leading-[1.4em]">
116
- <div className="flex items-center shrink-0 min-h-[1.4em]">
117
- <CircleCheck aria-hidden="true" size={19} className={`${color}`} />
118
- </div>
119
- <div className="flex flex-1 items-center min-h-[1.4em]">
120
- <p>{typeof store.message === 'object' && store.message?.text}</p>
121
- </div>
122
- <div className="flex items-center shrink-0 min-h-[1.4em]">
123
- <button
124
- type="button"
125
- onClick={hide}
126
- className="inline-flex rounded-md bg-white text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2
127
- focus:ring-indigo-500 focus:ring-offset-2"
128
- >
129
- <span className="sr-only">Close</span>
130
- <X aria-hidden="true" size={19} />
131
- </button>
132
- </div>
133
- </div>
134
- </div>
135
- </div>
136
- )}
137
- </div>
138
- </div>
139
- </>
140
- )
141
- }
@@ -1,90 +0,0 @@
1
- import { IsFirstRender, twMerge } from 'nitro-web'
2
- import SvgX1 from 'nitro-web/client/imgs/icons/x1.svg'
3
-
4
- type ModalProps = {
5
- show: boolean
6
- setShow: (show: boolean) => void
7
- children: React.ReactNode
8
- className?: string
9
- rootClassName?: string
10
- dismissable?: boolean
11
- maxWidth?: string
12
- minHeight?: string
13
- [key: string]: unknown
14
- }
15
-
16
- export function Modal({ show, setShow, children, maxWidth, minHeight, dismissable = true, className, rootClassName }: ModalProps) {
17
- const [state, setState] = useState(show ? 'open' : 'close')
18
- const containerEl = useRef<HTMLDivElement>(null)
19
- const isFirst = IsFirstRender()
20
-
21
- const states = {
22
- 'close': {
23
- root: 'left-[-100vw] transition-[left] duration-0 delay-200',
24
- bg: 'opacity-0',
25
- container: 'opacity-0 scale-[0.97]',
26
- },
27
- 'close-now': {
28
- root: '',
29
- bg: '',
30
- container: 'opacity-0 !transition-none',
31
- },
32
- 'open': {
33
- root: 'left-0 transition-none model-open',
34
- bg: 'opacity-100 duration-200',
35
- container: 'opacity-100 scale-[1] duration-200',
36
- },
37
- }
38
- const stateObj = states[state as keyof typeof states]
39
-
40
- useEffect(() => {
41
- if (isFirst) return
42
- if (show) {
43
- setState('open')
44
- } else {
45
- setTimeout(() => {
46
- // If another modal is being opened, force close the container for a smoother transition
47
- if (document.getElementsByClassName('modal-open').length > 1) {
48
- setState('close-now')
49
- } else {
50
- setState('close')
51
- }
52
- }, 10)
53
- }
54
- // There is a bug during hot-reloading where the modal does't open if we don't ensure
55
- // the same truthy/falsey type is used.
56
- }, [!!show])
57
-
58
- function onClick(e: React.MouseEvent) {
59
- const clickedOnModal = containerEl.current && containerEl.current.contains(e.target as Node)
60
- if (!clickedOnModal && dismissable) {
61
- setShow(false)
62
- }
63
- }
64
-
65
- return (
66
- <div
67
- onClick={(e) => e.stopPropagation()}
68
- class={`${twMerge(`fixed top-0 w-[100vw] h-[100vh] z-[100] ${stateObj.root} ${rootClassName||''}`)} nitro-modal`}
69
- >
70
- <div class={`!absolute inset-0 box-content bg-gray-500/70 transition-opacity ${stateObj.bg}`}></div>
71
- <div class={`relative h-[100vh] overflow-y-auto transition-[opacity,transform] ${stateObj.container}`}>
72
- <div class="flex items-center justify-center min-h-full" onMouseDown={onClick}>
73
- <div
74
- ref={containerEl}
75
- style={{ maxWidth: maxWidth || '550px', minHeight: minHeight }}
76
- class={`relative w-full mx-6 mt-4 mb-8 bg-white rounded-lg shadow-lg p-9 ${className||''}`}
77
- >
78
- <div
79
- class="absolute top-0 right-0 p-3 m-1 cursor-pointer"
80
- onClick={() => { if (dismissable) { setShow(false) }}}
81
- >
82
- <SvgX1 />
83
- </div>
84
- {children}
85
- </div>
86
- </div>
87
- </div>
88
- </div>
89
- )
90
- }
@@ -1,195 +0,0 @@
1
- // Component: https://tailwindui.com/components/application-ui/application-shells/sidebar#component-a69d85b6237ea2ad506c00ef1cd39a38
2
- import { css } from 'twin.macro'
3
- import avatarImg from 'nitro-web/client/imgs/avatar.jpg'
4
- import { injectedConfig } from 'nitro-web'
5
- import {
6
- Bars3Icon,
7
- HomeIcon,
8
- UsersIcon,
9
- ArrowLeftCircleIcon,
10
- PaintBrushIcon,
11
- } from '@heroicons/react/24/outline'
12
- import { XIcon } from 'lucide-react'
13
-
14
- const sidebarWidth = 'w-80'
15
-
16
- export type SidebarProps = {
17
- Logo?: React.FC<{ width?: string, height?: string }>;
18
- menu?: { name: string; to: string; Icon: React.FC<{ className?: string }> }[]
19
- links?: { name: string; to: string; initial: string }[]
20
- }
21
-
22
- function classNames(...classes: string[]) {
23
- return classes.filter(Boolean).join(' ')
24
- }
25
-
26
- export function Sidebar({ Logo, menu, links }: SidebarProps) {
27
- const [sidebarOpen, setSidebarOpen] = useState(false)
28
- return (
29
- <>
30
- {/* desktop sidebar */}
31
- <div css={style} className={
32
- 'fixed inset-y-0 z-50 flex flex-col ease-in-out lg:left-0 lg:translate-x-0 lg:!delay-0 lg:!duration-0 ' +
33
- (
34
- sidebarOpen
35
- ? 'left-0 translate-x-[0px] sidebar-transition '
36
- : 'left-[-100%] translate-x-[-100%] sidebar-transition-delay '
37
- ) +
38
- sidebarWidth
39
- }>
40
- <div className={
41
- 'absolute left-full top-0 flex w-16 justify-center pt-5 lg:hidden duration-300 ease ' +
42
- (sidebarOpen ? 'opacity-100' : 'opacity-0')
43
- }>
44
- <button type="button" onClick={() => setSidebarOpen(false)} className="-m-2.5 p-2.5">
45
- <XIcon aria-hidden="true" strokeWidth={1.5} size={24} className="text-white" />
46
- </button>
47
- </div>
48
- <SidebarContents Logo={Logo} menu={menu} links={links} />
49
- </div>
50
-
51
- {/* mobile backdrop */}
52
- <div
53
- css={style}
54
- onClick={() => setSidebarOpen(false)}
55
- className={'fixed w-full z-[49] inset-0 bg-gray-900/70 ease-linear lg:hidden ' +
56
- (
57
- sidebarOpen
58
- ? 'left-0 opacity-100 sidebar-transition '
59
- : 'left-[-100%] opacity-0 sidebar-transition-delay '
60
- )
61
- }
62
- />
63
-
64
- {/* mobile sidebar topbar */}
65
- <div className="sticky top-0 z-40 flex items-center gap-x-6 bg-white px-4 py-4 shadow-sm sm:px-6 lg:hidden">
66
- <button type="button" onClick={() => setSidebarOpen(true)} className="-m-2.5 p-2.5 text-gray-700 lg:hidden">
67
- <Bars3Icon aria-hidden="true" className="size-6" />
68
- </button>
69
- <div className="flex-1 text-sm/6 font-semibold text-gray-900">Dashboard</div>
70
- <Link to="#">
71
- <img alt="" src={avatarImg} className="size-8 rounded-full bg-gray-50" />
72
- </Link>
73
- </div>
74
-
75
- <div class={`${sidebarWidth}`} />
76
- </>
77
- )
78
- }
79
-
80
- function SidebarContents ({ Logo, menu, links }: SidebarProps) {
81
- const location = useLocation()
82
- const [store] = useTracked()
83
- const user = store.user
84
-
85
- function isActive(path: string) {
86
- if (path == '/' && location.pathname == path) return 'is-active'
87
- else if (path != '/' && location.pathname.match(`^${path}`)) return 'is-active'
88
- else return ''
89
- }
90
-
91
- const _menu = menu || [
92
- { name: 'Dashboard', to: '/', Icon: HomeIcon },
93
- { name: injectedConfig.isDemo ? 'Design System' : 'Style Guide', to: '/styleguide', Icon: PaintBrushIcon },
94
- { name: 'Pricing', to: '/pricing', Icon: UsersIcon },
95
- { name: 'Signout', to: '/signout', Icon: ArrowLeftCircleIcon },
96
- ]
97
-
98
- const _links = links || [
99
- { name: 'Nitro on Github', to: 'https://github.com/boycce/nitro-web', initial: 'G' },
100
- ]
101
-
102
- // Sidebar component, swap this element with another sidebar if you like
103
- return (
104
- <div className="flex grow flex-col gap-y-8 overflow-y-auto bg-white py-5 px-10 lg:border-r lg:border-gray-200">
105
- {Logo && (
106
- <div className="flex h-16 shrink-0 items-center gap-2 justify-bedtween">
107
- <Link to="/">
108
- <Logo width="70" height={undefined} />
109
- </Link>
110
- <span className="text-[9px] text-gray-900 font-semibold mt-4">{injectedConfig.version}</span>
111
- </div>
112
- )}
113
- <nav className="flex flex-1 flex-col">
114
- <ul role="list" className="flex flex-1 flex-col gap-y-7">
115
- <li>
116
- <ul role="list" className="-mx-2 space-y-1">
117
- {_menu.map((item) => (
118
- <li key={item.name}>
119
- <Link
120
- to={item.to}
121
- className={classNames(
122
- isActive(item.to)
123
- ? 'bg-gray-50 text-indigo-600'
124
- : 'text-gray-700 hover:bg-gray-50 hover:text-indigo-600',
125
- 'group flex gap-x-3 items-center rounded-md p-2 text-md/6 font-semibold'
126
- )}
127
- >
128
- { item.Icon &&
129
- <item.Icon
130
- className={classNames(
131
- isActive(item.to) ? 'text-indigo-600' : 'text-gray-400 group-hover:text-indigo-600',
132
- 'size-5 shrink-0'
133
- )}
134
- />
135
- }
136
- {item.name}
137
- </Link>
138
- </li>
139
- ))}
140
- </ul>
141
- </li>
142
- <li>
143
- <div className="text-xs/6 font-semibold text-gray-400">Other Links</div>
144
- <ul role="list" className="-mx-2 mt-2 space-y-1">
145
- {_links.map((team) => (
146
- <li key={team.name}>
147
- <Link
148
- to={team.to}
149
- className={classNames(
150
- isActive(team.to)
151
- ? 'bg-gray-50 text-indigo-600'
152
- : 'text-gray-700 hover:bg-gray-50 hover:text-indigo-600',
153
- 'group flex gap-x-3 rounded-md p-2 text-md/6 font-semibold'
154
- )}
155
- >
156
- <span
157
- className={classNames(
158
- isActive(team.to)
159
- ? 'border-indigo-600 text-indigo-600'
160
- : 'border-gray-200 text-gray-400 group-hover:border-indigo-600 group-hover:text-indigo-600',
161
- 'flex size-6 shrink-0 items-center justify-center rounded-lg border bg-white text-[0.625rem] font-medium'
162
- )}
163
- >
164
- {team.initial}
165
- </span>
166
- <span className="truncate">{team.name}</span>
167
- </Link>
168
- </li>
169
- ))}
170
- </ul>
171
- </li>
172
-
173
- <li className="-mx-6 mt-auto hidden lg:block">
174
- <Link
175
- to="#"
176
- className="flex items-center gap-x-4 px-6 py-3 text-sm/6 font-semibold text-gray-900 hover:bg-gray-50"
177
- >
178
- <img alt="" src={avatarImg} className="size-8 rounded-full bg-gray-50" />
179
- <span aria-hidden="true" class="truncate1 flex-1">{user?.name || 'Guest'}</span>
180
- </Link>
181
- </li>
182
- </ul>
183
- </nav>
184
- </div>
185
- )
186
- }
187
-
188
- const style = css`
189
- &.sidebar-transition-delay {
190
- transition: transform 300ms, opacity 300ms, left 0ms 300ms;
191
- }
192
- &.sidebar-transition {
193
- transition: transform 300ms, opacity 300ms, left 0ms 0ms;
194
- }
195
- `
@@ -1,154 +0,0 @@
1
- // todo: finish tailwind conversion
2
- import { css } from 'twin.macro'
3
-
4
- type TooltipProps = {
5
- children: React.ReactNode
6
- className?: string
7
- classNamePopup?: string
8
- isSmall?: boolean
9
- text?: React.ReactNode
10
- }
11
-
12
- export function Tooltip({ text, children, className, classNamePopup, isSmall }: TooltipProps) {
13
- return (
14
- <div class={`${className} relative inline-block align-middle nitro-tooltip`} css={style}>
15
- {
16
- text
17
- ? <>
18
- <div class="tooltip-trigger ">{children}</div>
19
- <div class={`tooltip-popup ${classNamePopup||''} ${isSmall ? 'is-small' : ''}`}>{text}</div>
20
- </>
21
- : children
22
- }
23
- </div>
24
- )
25
- }
26
-
27
- const style = css`
28
- .tooltip-popup {
29
- position: absolute;
30
- display: block;
31
- margin-top: -10000px;
32
- width: 200px;
33
- padding: 14px;
34
- font-weight: 400;
35
- font-size: 11.5px;
36
- line-height: 1.3;
37
- letter-spacing: 0.5px;
38
- text-align: center;
39
- border-radius: 6px;
40
- background: black;
41
- color: white;
42
- opacity: 0;
43
- transition: opacity 0.15s ease, transform 0.15s ease, margin-top 0s 0.15s;
44
- white-space: break-spaces;
45
- overflow-wrap: break-word;
46
- pointer-events: none;
47
- z-index: 9999;
48
- &:after {
49
- content: '';
50
- position: absolute;
51
- border-width: 6px;
52
- border-style: solid;
53
- }
54
- // Variation
55
- &.is-small {
56
- width: 160px;
57
- padding: 10px;
58
- font-size: 11px;
59
- }
60
- // Positions
61
- &.is-top,
62
- &.is-top-left,
63
- &:not(.is-top-left):not(.is-left):not(.is-right):not(.is-bottom):not(.is-bottom-left) {
64
- bottom: 100%;
65
- left: 50%;
66
- transform: translateX(-50%) translateY(-15px);
67
- &:after {
68
- top: 100%;
69
- left: 50%;
70
- margin-left: -6px;
71
- border-color: black transparent transparent transparent;
72
- }
73
- &.is-top-left {
74
- left: 0px;
75
- transform: translateX(0%) translateY(-15px);
76
- &:after {
77
- left: 28px;
78
- }
79
- }
80
- }
81
- &.is-bottom,
82
- &.is-bottom-left {
83
- top: 100%;
84
- left: 50%;
85
- transform: translateX(-50%) translateY(15px) ;
86
- &:after {
87
- bottom: 100%;
88
- left: 50%;
89
- margin-left: -6px;
90
- border-color: transparent transparent black transparent;
91
- }
92
- &.is-bottom-left {
93
- left: 0px;
94
- transform: translateX(0%) translateY(15px);
95
- &:after {
96
- left: 28px;
97
- }
98
- }
99
- }
100
- &.is-left {
101
- top: 50%;
102
- right: 100%;
103
- transform: translateX(-15px) translateY(-50%);
104
- &:after {
105
- top: 50%;
106
- right: -12px;
107
- margin-top: -6px;
108
- border-color: transparent transparent transparent black;
109
- }
110
- }
111
- &.is-right {
112
- top: 50%;
113
- left: 100%;
114
- transform: translateX(15px) translateY(-50%);
115
- &:after {
116
- top: 50%;
117
- left: -12px;
118
- margin-top: -6px;
119
- border-color: transparent black transparent transparent;
120
- }
121
- }
122
- }
123
- .tooltip-trigger {
124
- /* trigger can come before tooltip-popup or wrap it */
125
- position: relative !important;
126
- &:hover .tooltip-popup,
127
- &:hover + .tooltip-popup,
128
- .tooltip-popup.is-active,
129
- & + .tooltip-popup.is-active {
130
- opacity: 1;
131
- margin-top: 0;
132
- transition: opacity 0.15s ease, transform 0.15s ease, margin-top 0s 0s;
133
- &.is-top,
134
- &:not(.is-top-left):not(.is-left):not(.is-right):not(.is-bottom):not(.is-bottom-left) {
135
- transform: translateX(-50%) translateY(-10px);
136
- }
137
- &.is-top-left {
138
- transform: translateX(0%) translateY(-10px);
139
- }
140
- &.is-bottom {
141
- transform: translateX(-50%) translateY(10px);
142
- }
143
- &.is-bottom-left {
144
- transform: translateX(0%) translateY(10px);
145
- }
146
- &.is-left {
147
- transform: translateX(-10px) translateY(-50%);
148
- }
149
- &.is-right {
150
- transform: translateX(10px) translateY(-50%);
151
- }
152
- }
153
- }
154
- `
@@ -1,15 +0,0 @@
1
- type TopbarProps = {
2
- title: React.ReactNode
3
- subtitle?: React.ReactNode
4
- className?: string
5
- }
6
-
7
- export function Topbar({ title, subtitle, className }: TopbarProps) {
8
- return (
9
- <div class={`flex flex-col min-h-12 gap-0.5 mb-6 nitro-topbar ${className||''}`}>
10
- <div class="text-2xl font-bold">{title}</div>
11
- { subtitle && <div class="text-sm text-muted-foreground">{subtitle}</div>}
12
- {/* { submenu && <div class="pt-2 text-large weight-500">{submenu}</div> } */}
13
- </div>
14
- )
15
- }