mirror of
https://github.com/unraid/api.git
synced 2026-02-13 03:28:29 -06:00
feat: tailwind v4 (#1522)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Streamlined Tailwind CSS integration using Vite plugin, eliminating the need for separate Tailwind config files. * Updated theme and color variables for improved consistency and maintainability. * **Style** * Standardized spacing, sizing, and font classes across all components using Tailwind’s default scale. * Reduced excessive gaps, padding, and font sizes for a more compact and cohesive UI. * Updated gradient, border, and shadow classes to match Tailwind v4 conventions. * Replaced custom pixel-based classes with Tailwind’s bracketed arbitrary value syntax where needed. * Replaced focus outline styles from `outline-none` to `outline-hidden` for consistent focus handling. * Updated flex shrink/grow utility classes to use newer shorthand forms. * Converted several component templates to use self-closing tags for cleaner markup. * Adjusted icon sizes and spacing for improved visual balance. * **Chores** * Removed legacy Tailwind/PostCSS configuration files and related scripts. * Updated and cleaned up package dependencies for Tailwind v4 and related plugins. * Removed unused or redundant build scripts and configuration exports. * Updated documentation to reflect new Tailwind v4 usage. * Removed Prettier Tailwind plugin from formatting configurations. * Removed Nuxt Tailwind module in favor of direct Vite plugin integration. * Cleaned up ESLint config by removing Prettier integration. * **Bug Fixes** * Corrected invalid or outdated Tailwind class names and syntax. * Fixed issues with max-width and other utility classes for improved layout consistency. * **Tests** * Updated test assertions to match new class names and styling conventions. * **Documentation** * Revised README and internal notes to clarify Tailwind v4 adoption and configuration changes. * Added new development notes emphasizing Tailwind v4 usage and documentation references. * **UI Components** * Enhanced BrandButton stories with detailed variant, size, and padding showcases for better visual testing. * Improved theme store to apply dark mode class on both `<html>` and `<body>` elements for compatibility. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -14,7 +14,26 @@
|
||||
"Bash(mv:*)",
|
||||
"Bash(ls:*)",
|
||||
"mcp__ide__getDiagnostics",
|
||||
"Bash(pnpm --filter \"*connect*\" test connect-status-writer.service.spec)"
|
||||
"Bash(pnpm --filter \"*connect*\" test connect-status-writer.service.spec)",
|
||||
"Bash(pnpm storybook:*)",
|
||||
"Bash(rm:*)",
|
||||
"Bash(pnpm add:*)",
|
||||
"Bash(pnpm install:*)",
|
||||
"Bash(pkill:*)",
|
||||
"Bash(true)",
|
||||
"Bash(timeout 15 pnpm storybook)",
|
||||
"WebFetch(domain:tailwindcss.com)",
|
||||
"Bash(pnpm list:*)",
|
||||
"Bash(pnpm remove:*)",
|
||||
"WebFetch(domain:github.com)",
|
||||
"mcp__browsermcp__browser_navigate",
|
||||
"Bash(clear)",
|
||||
"Bash(git log:*)",
|
||||
"Bash(pnpm --filter ./unraid-ui build)",
|
||||
"Bash(pnpm --filter @unraid/ui build)",
|
||||
"Bash(pnpm --filter @unraid/web build)",
|
||||
"Bash(python3:*)",
|
||||
"Bash(pnpm tailwind:build:*)"
|
||||
]
|
||||
},
|
||||
"enableAllProjectMcpServers": false
|
||||
|
||||
@@ -135,3 +135,8 @@ Enables GraphQL playground at `http://tower.local/graphql`
|
||||
- Place all mock declarations at the top level
|
||||
- Use factory functions for module mocks to avoid hoisting issues
|
||||
- Clear mocks between tests to ensure isolation
|
||||
|
||||
## Development Memories
|
||||
|
||||
- We are using tailwind v4 we do not need a tailwind config anymore
|
||||
- always search the internet for tailwind v4 documentation when making tailwind related style changes
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "4.9.5",
|
||||
"version": "4.10.0",
|
||||
"extraOrigins": [
|
||||
"https://google.com",
|
||||
"https://test.com"
|
||||
|
||||
@@ -247,6 +247,347 @@ A field whose value conforms to the standard URL format as specified in RFC3986:
|
||||
"""
|
||||
scalar URL
|
||||
|
||||
type DiskPartition {
|
||||
"""The name of the partition"""
|
||||
name: String!
|
||||
|
||||
"""The filesystem type of the partition"""
|
||||
fsType: DiskFsType!
|
||||
|
||||
"""The size of the partition in bytes"""
|
||||
size: Float!
|
||||
}
|
||||
|
||||
"""The type of filesystem on the disk partition"""
|
||||
enum DiskFsType {
|
||||
XFS
|
||||
BTRFS
|
||||
VFAT
|
||||
ZFS
|
||||
EXT4
|
||||
NTFS
|
||||
}
|
||||
|
||||
type Disk implements Node {
|
||||
id: PrefixedID!
|
||||
|
||||
"""The device path of the disk (e.g. /dev/sdb)"""
|
||||
device: String!
|
||||
|
||||
"""The type of disk (e.g. SSD, HDD)"""
|
||||
type: String!
|
||||
|
||||
"""The model name of the disk"""
|
||||
name: String!
|
||||
|
||||
"""The manufacturer of the disk"""
|
||||
vendor: String!
|
||||
|
||||
"""The total size of the disk in bytes"""
|
||||
size: Float!
|
||||
|
||||
"""The number of bytes per sector"""
|
||||
bytesPerSector: Float!
|
||||
|
||||
"""The total number of cylinders on the disk"""
|
||||
totalCylinders: Float!
|
||||
|
||||
"""The total number of heads on the disk"""
|
||||
totalHeads: Float!
|
||||
|
||||
"""The total number of sectors on the disk"""
|
||||
totalSectors: Float!
|
||||
|
||||
"""The total number of tracks on the disk"""
|
||||
totalTracks: Float!
|
||||
|
||||
"""The number of tracks per cylinder"""
|
||||
tracksPerCylinder: Float!
|
||||
|
||||
"""The number of sectors per track"""
|
||||
sectorsPerTrack: Float!
|
||||
|
||||
"""The firmware revision of the disk"""
|
||||
firmwareRevision: String!
|
||||
|
||||
"""The serial number of the disk"""
|
||||
serialNum: String!
|
||||
|
||||
"""The interface type of the disk"""
|
||||
interfaceType: DiskInterfaceType!
|
||||
|
||||
"""The SMART status of the disk"""
|
||||
smartStatus: DiskSmartStatus!
|
||||
|
||||
"""The current temperature of the disk in Celsius"""
|
||||
temperature: Float
|
||||
|
||||
"""The partitions on the disk"""
|
||||
partitions: [DiskPartition!]!
|
||||
}
|
||||
|
||||
"""The type of interface the disk uses to connect to the system"""
|
||||
enum DiskInterfaceType {
|
||||
SAS
|
||||
SATA
|
||||
USB
|
||||
PCIE
|
||||
UNKNOWN
|
||||
}
|
||||
|
||||
"""
|
||||
The SMART (Self-Monitoring, Analysis and Reporting Technology) status of the disk
|
||||
"""
|
||||
enum DiskSmartStatus {
|
||||
OK
|
||||
UNKNOWN
|
||||
}
|
||||
|
||||
type KeyFile {
|
||||
location: String
|
||||
contents: String
|
||||
}
|
||||
|
||||
type Registration implements Node {
|
||||
id: PrefixedID!
|
||||
type: registrationType
|
||||
keyFile: KeyFile
|
||||
state: RegistrationState
|
||||
expiration: String
|
||||
updateExpiration: String
|
||||
}
|
||||
|
||||
enum registrationType {
|
||||
BASIC
|
||||
PLUS
|
||||
PRO
|
||||
STARTER
|
||||
UNLEASHED
|
||||
LIFETIME
|
||||
INVALID
|
||||
TRIAL
|
||||
}
|
||||
|
||||
enum RegistrationState {
|
||||
TRIAL
|
||||
BASIC
|
||||
PLUS
|
||||
PRO
|
||||
STARTER
|
||||
UNLEASHED
|
||||
LIFETIME
|
||||
EEXPIRED
|
||||
EGUID
|
||||
EGUID1
|
||||
ETRIAL
|
||||
ENOKEYFILE
|
||||
ENOKEYFILE1
|
||||
ENOKEYFILE2
|
||||
ENOFLASH
|
||||
ENOFLASH1
|
||||
ENOFLASH2
|
||||
ENOFLASH3
|
||||
ENOFLASH4
|
||||
ENOFLASH5
|
||||
ENOFLASH6
|
||||
ENOFLASH7
|
||||
EBLACKLISTED
|
||||
EBLACKLISTED1
|
||||
EBLACKLISTED2
|
||||
ENOCONN
|
||||
}
|
||||
|
||||
type Vars implements Node {
|
||||
id: PrefixedID!
|
||||
|
||||
"""Unraid version"""
|
||||
version: String
|
||||
maxArraysz: Int
|
||||
maxCachesz: Int
|
||||
|
||||
"""Machine hostname"""
|
||||
name: String
|
||||
timeZone: String
|
||||
comment: String
|
||||
security: String
|
||||
workgroup: String
|
||||
domain: String
|
||||
domainShort: String
|
||||
hideDotFiles: Boolean
|
||||
localMaster: Boolean
|
||||
enableFruit: String
|
||||
|
||||
"""Should a NTP server be used for time sync?"""
|
||||
useNtp: Boolean
|
||||
|
||||
"""NTP Server 1"""
|
||||
ntpServer1: String
|
||||
|
||||
"""NTP Server 2"""
|
||||
ntpServer2: String
|
||||
|
||||
"""NTP Server 3"""
|
||||
ntpServer3: String
|
||||
|
||||
"""NTP Server 4"""
|
||||
ntpServer4: String
|
||||
domainLogin: String
|
||||
sysModel: String
|
||||
sysArraySlots: Int
|
||||
sysCacheSlots: Int
|
||||
sysFlashSlots: Int
|
||||
useSsl: Boolean
|
||||
|
||||
"""Port for the webui via HTTP"""
|
||||
port: Int
|
||||
|
||||
"""Port for the webui via HTTPS"""
|
||||
portssl: Int
|
||||
localTld: String
|
||||
bindMgt: Boolean
|
||||
|
||||
"""Should telnet be enabled?"""
|
||||
useTelnet: Boolean
|
||||
porttelnet: Int
|
||||
useSsh: Boolean
|
||||
portssh: Int
|
||||
startPage: String
|
||||
startArray: Boolean
|
||||
spindownDelay: String
|
||||
queueDepth: String
|
||||
spinupGroups: Boolean
|
||||
defaultFormat: String
|
||||
defaultFsType: String
|
||||
shutdownTimeout: Int
|
||||
luksKeyfile: String
|
||||
pollAttributes: String
|
||||
pollAttributesDefault: String
|
||||
pollAttributesStatus: String
|
||||
nrRequests: Int
|
||||
nrRequestsDefault: Int
|
||||
nrRequestsStatus: String
|
||||
mdNumStripes: Int
|
||||
mdNumStripesDefault: Int
|
||||
mdNumStripesStatus: String
|
||||
mdSyncWindow: Int
|
||||
mdSyncWindowDefault: Int
|
||||
mdSyncWindowStatus: String
|
||||
mdSyncThresh: Int
|
||||
mdSyncThreshDefault: Int
|
||||
mdSyncThreshStatus: String
|
||||
mdWriteMethod: Int
|
||||
mdWriteMethodDefault: String
|
||||
mdWriteMethodStatus: String
|
||||
shareDisk: String
|
||||
shareUser: String
|
||||
shareUserInclude: String
|
||||
shareUserExclude: String
|
||||
shareSmbEnabled: Boolean
|
||||
shareNfsEnabled: Boolean
|
||||
shareAfpEnabled: Boolean
|
||||
shareInitialOwner: String
|
||||
shareInitialGroup: String
|
||||
shareCacheEnabled: Boolean
|
||||
shareCacheFloor: String
|
||||
shareMoverSchedule: String
|
||||
shareMoverLogging: Boolean
|
||||
fuseRemember: String
|
||||
fuseRememberDefault: String
|
||||
fuseRememberStatus: String
|
||||
fuseDirectio: String
|
||||
fuseDirectioDefault: String
|
||||
fuseDirectioStatus: String
|
||||
shareAvahiEnabled: Boolean
|
||||
shareAvahiSmbName: String
|
||||
shareAvahiSmbModel: String
|
||||
shareAvahiAfpName: String
|
||||
shareAvahiAfpModel: String
|
||||
safeMode: Boolean
|
||||
startMode: String
|
||||
configValid: Boolean
|
||||
configError: ConfigErrorState
|
||||
joinStatus: String
|
||||
deviceCount: Int
|
||||
flashGuid: String
|
||||
flashProduct: String
|
||||
flashVendor: String
|
||||
regCheck: String
|
||||
regFile: String
|
||||
regGuid: String
|
||||
regTy: registrationType
|
||||
regState: RegistrationState
|
||||
|
||||
"""Registration owner"""
|
||||
regTo: String
|
||||
regTm: String
|
||||
regTm2: String
|
||||
regGen: String
|
||||
sbName: String
|
||||
sbVersion: String
|
||||
sbUpdated: String
|
||||
sbEvents: Int
|
||||
sbState: String
|
||||
sbClean: Boolean
|
||||
sbSynced: Int
|
||||
sbSyncErrs: Int
|
||||
sbSynced2: Int
|
||||
sbSyncExit: String
|
||||
sbNumDisks: Int
|
||||
mdColor: String
|
||||
mdNumDisks: Int
|
||||
mdNumDisabled: Int
|
||||
mdNumInvalid: Int
|
||||
mdNumMissing: Int
|
||||
mdNumNew: Int
|
||||
mdNumErased: Int
|
||||
mdResync: Int
|
||||
mdResyncCorr: String
|
||||
mdResyncPos: String
|
||||
mdResyncDb: String
|
||||
mdResyncDt: String
|
||||
mdResyncAction: String
|
||||
mdResyncSize: Int
|
||||
mdState: String
|
||||
mdVersion: String
|
||||
cacheNumDevices: Int
|
||||
cacheSbNumDisks: Int
|
||||
fsState: String
|
||||
|
||||
"""Human friendly string of array events happening"""
|
||||
fsProgress: String
|
||||
|
||||
"""
|
||||
Percentage from 0 - 100 while upgrading a disk or swapping parity drives
|
||||
"""
|
||||
fsCopyPrcnt: Int
|
||||
fsNumMounted: Int
|
||||
fsNumUnmountable: Int
|
||||
fsUnmountableMask: String
|
||||
|
||||
"""Total amount of user shares"""
|
||||
shareCount: Int
|
||||
|
||||
"""Total amount shares with SMB enabled"""
|
||||
shareSmbCount: Int
|
||||
|
||||
"""Total amount shares with NFS enabled"""
|
||||
shareNfsCount: Int
|
||||
|
||||
"""Total amount shares with AFP enabled"""
|
||||
shareAfpCount: Int
|
||||
shareMoverActive: Boolean
|
||||
csrfToken: String
|
||||
}
|
||||
|
||||
"""Possible error states for configuration"""
|
||||
enum ConfigErrorState {
|
||||
UNKNOWN_ERROR
|
||||
INELIGIBLE
|
||||
INVALID
|
||||
NO_KEY_SERVER
|
||||
WITHDRAWN
|
||||
}
|
||||
|
||||
type Permission {
|
||||
resource: Resource!
|
||||
actions: [String!]!
|
||||
@@ -620,102 +961,6 @@ enum ThemeName {
|
||||
white
|
||||
}
|
||||
|
||||
type DiskPartition {
|
||||
"""The name of the partition"""
|
||||
name: String!
|
||||
|
||||
"""The filesystem type of the partition"""
|
||||
fsType: DiskFsType!
|
||||
|
||||
"""The size of the partition in bytes"""
|
||||
size: Float!
|
||||
}
|
||||
|
||||
"""The type of filesystem on the disk partition"""
|
||||
enum DiskFsType {
|
||||
XFS
|
||||
BTRFS
|
||||
VFAT
|
||||
ZFS
|
||||
EXT4
|
||||
NTFS
|
||||
}
|
||||
|
||||
type Disk implements Node {
|
||||
id: PrefixedID!
|
||||
|
||||
"""The device path of the disk (e.g. /dev/sdb)"""
|
||||
device: String!
|
||||
|
||||
"""The type of disk (e.g. SSD, HDD)"""
|
||||
type: String!
|
||||
|
||||
"""The model name of the disk"""
|
||||
name: String!
|
||||
|
||||
"""The manufacturer of the disk"""
|
||||
vendor: String!
|
||||
|
||||
"""The total size of the disk in bytes"""
|
||||
size: Float!
|
||||
|
||||
"""The number of bytes per sector"""
|
||||
bytesPerSector: Float!
|
||||
|
||||
"""The total number of cylinders on the disk"""
|
||||
totalCylinders: Float!
|
||||
|
||||
"""The total number of heads on the disk"""
|
||||
totalHeads: Float!
|
||||
|
||||
"""The total number of sectors on the disk"""
|
||||
totalSectors: Float!
|
||||
|
||||
"""The total number of tracks on the disk"""
|
||||
totalTracks: Float!
|
||||
|
||||
"""The number of tracks per cylinder"""
|
||||
tracksPerCylinder: Float!
|
||||
|
||||
"""The number of sectors per track"""
|
||||
sectorsPerTrack: Float!
|
||||
|
||||
"""The firmware revision of the disk"""
|
||||
firmwareRevision: String!
|
||||
|
||||
"""The serial number of the disk"""
|
||||
serialNum: String!
|
||||
|
||||
"""The interface type of the disk"""
|
||||
interfaceType: DiskInterfaceType!
|
||||
|
||||
"""The SMART status of the disk"""
|
||||
smartStatus: DiskSmartStatus!
|
||||
|
||||
"""The current temperature of the disk in Celsius"""
|
||||
temperature: Float
|
||||
|
||||
"""The partitions on the disk"""
|
||||
partitions: [DiskPartition!]!
|
||||
}
|
||||
|
||||
"""The type of interface the disk uses to connect to the system"""
|
||||
enum DiskInterfaceType {
|
||||
SAS
|
||||
SATA
|
||||
USB
|
||||
PCIE
|
||||
UNKNOWN
|
||||
}
|
||||
|
||||
"""
|
||||
The SMART (Self-Monitoring, Analysis and Reporting Technology) status of the disk
|
||||
"""
|
||||
enum DiskSmartStatus {
|
||||
OK
|
||||
UNKNOWN
|
||||
}
|
||||
|
||||
type InfoApps implements Node {
|
||||
id: PrefixedID!
|
||||
|
||||
@@ -1106,60 +1351,6 @@ type Owner {
|
||||
avatar: String!
|
||||
}
|
||||
|
||||
type KeyFile {
|
||||
location: String
|
||||
contents: String
|
||||
}
|
||||
|
||||
type Registration implements Node {
|
||||
id: PrefixedID!
|
||||
type: registrationType
|
||||
keyFile: KeyFile
|
||||
state: RegistrationState
|
||||
expiration: String
|
||||
updateExpiration: String
|
||||
}
|
||||
|
||||
enum registrationType {
|
||||
BASIC
|
||||
PLUS
|
||||
PRO
|
||||
STARTER
|
||||
UNLEASHED
|
||||
LIFETIME
|
||||
INVALID
|
||||
TRIAL
|
||||
}
|
||||
|
||||
enum RegistrationState {
|
||||
TRIAL
|
||||
BASIC
|
||||
PLUS
|
||||
PRO
|
||||
STARTER
|
||||
UNLEASHED
|
||||
LIFETIME
|
||||
EEXPIRED
|
||||
EGUID
|
||||
EGUID1
|
||||
ETRIAL
|
||||
ENOKEYFILE
|
||||
ENOKEYFILE1
|
||||
ENOKEYFILE2
|
||||
ENOFLASH
|
||||
ENOFLASH1
|
||||
ENOFLASH2
|
||||
ENOFLASH3
|
||||
ENOFLASH4
|
||||
ENOFLASH5
|
||||
ENOFLASH6
|
||||
ENOFLASH7
|
||||
EBLACKLISTED
|
||||
EBLACKLISTED1
|
||||
EBLACKLISTED2
|
||||
ENOCONN
|
||||
}
|
||||
|
||||
type ProfileModel implements Node {
|
||||
id: PrefixedID!
|
||||
username: String!
|
||||
@@ -1225,197 +1416,6 @@ type Settings implements Node {
|
||||
api: ApiConfig!
|
||||
}
|
||||
|
||||
type Vars implements Node {
|
||||
id: PrefixedID!
|
||||
|
||||
"""Unraid version"""
|
||||
version: String
|
||||
maxArraysz: Int
|
||||
maxCachesz: Int
|
||||
|
||||
"""Machine hostname"""
|
||||
name: String
|
||||
timeZone: String
|
||||
comment: String
|
||||
security: String
|
||||
workgroup: String
|
||||
domain: String
|
||||
domainShort: String
|
||||
hideDotFiles: Boolean
|
||||
localMaster: Boolean
|
||||
enableFruit: String
|
||||
|
||||
"""Should a NTP server be used for time sync?"""
|
||||
useNtp: Boolean
|
||||
|
||||
"""NTP Server 1"""
|
||||
ntpServer1: String
|
||||
|
||||
"""NTP Server 2"""
|
||||
ntpServer2: String
|
||||
|
||||
"""NTP Server 3"""
|
||||
ntpServer3: String
|
||||
|
||||
"""NTP Server 4"""
|
||||
ntpServer4: String
|
||||
domainLogin: String
|
||||
sysModel: String
|
||||
sysArraySlots: Int
|
||||
sysCacheSlots: Int
|
||||
sysFlashSlots: Int
|
||||
useSsl: Boolean
|
||||
|
||||
"""Port for the webui via HTTP"""
|
||||
port: Int
|
||||
|
||||
"""Port for the webui via HTTPS"""
|
||||
portssl: Int
|
||||
localTld: String
|
||||
bindMgt: Boolean
|
||||
|
||||
"""Should telnet be enabled?"""
|
||||
useTelnet: Boolean
|
||||
porttelnet: Int
|
||||
useSsh: Boolean
|
||||
portssh: Int
|
||||
startPage: String
|
||||
startArray: Boolean
|
||||
spindownDelay: String
|
||||
queueDepth: String
|
||||
spinupGroups: Boolean
|
||||
defaultFormat: String
|
||||
defaultFsType: String
|
||||
shutdownTimeout: Int
|
||||
luksKeyfile: String
|
||||
pollAttributes: String
|
||||
pollAttributesDefault: String
|
||||
pollAttributesStatus: String
|
||||
nrRequests: Int
|
||||
nrRequestsDefault: Int
|
||||
nrRequestsStatus: String
|
||||
mdNumStripes: Int
|
||||
mdNumStripesDefault: Int
|
||||
mdNumStripesStatus: String
|
||||
mdSyncWindow: Int
|
||||
mdSyncWindowDefault: Int
|
||||
mdSyncWindowStatus: String
|
||||
mdSyncThresh: Int
|
||||
mdSyncThreshDefault: Int
|
||||
mdSyncThreshStatus: String
|
||||
mdWriteMethod: Int
|
||||
mdWriteMethodDefault: String
|
||||
mdWriteMethodStatus: String
|
||||
shareDisk: String
|
||||
shareUser: String
|
||||
shareUserInclude: String
|
||||
shareUserExclude: String
|
||||
shareSmbEnabled: Boolean
|
||||
shareNfsEnabled: Boolean
|
||||
shareAfpEnabled: Boolean
|
||||
shareInitialOwner: String
|
||||
shareInitialGroup: String
|
||||
shareCacheEnabled: Boolean
|
||||
shareCacheFloor: String
|
||||
shareMoverSchedule: String
|
||||
shareMoverLogging: Boolean
|
||||
fuseRemember: String
|
||||
fuseRememberDefault: String
|
||||
fuseRememberStatus: String
|
||||
fuseDirectio: String
|
||||
fuseDirectioDefault: String
|
||||
fuseDirectioStatus: String
|
||||
shareAvahiEnabled: Boolean
|
||||
shareAvahiSmbName: String
|
||||
shareAvahiSmbModel: String
|
||||
shareAvahiAfpName: String
|
||||
shareAvahiAfpModel: String
|
||||
safeMode: Boolean
|
||||
startMode: String
|
||||
configValid: Boolean
|
||||
configError: ConfigErrorState
|
||||
joinStatus: String
|
||||
deviceCount: Int
|
||||
flashGuid: String
|
||||
flashProduct: String
|
||||
flashVendor: String
|
||||
regCheck: String
|
||||
regFile: String
|
||||
regGuid: String
|
||||
regTy: registrationType
|
||||
regState: RegistrationState
|
||||
|
||||
"""Registration owner"""
|
||||
regTo: String
|
||||
regTm: String
|
||||
regTm2: String
|
||||
regGen: String
|
||||
sbName: String
|
||||
sbVersion: String
|
||||
sbUpdated: String
|
||||
sbEvents: Int
|
||||
sbState: String
|
||||
sbClean: Boolean
|
||||
sbSynced: Int
|
||||
sbSyncErrs: Int
|
||||
sbSynced2: Int
|
||||
sbSyncExit: String
|
||||
sbNumDisks: Int
|
||||
mdColor: String
|
||||
mdNumDisks: Int
|
||||
mdNumDisabled: Int
|
||||
mdNumInvalid: Int
|
||||
mdNumMissing: Int
|
||||
mdNumNew: Int
|
||||
mdNumErased: Int
|
||||
mdResync: Int
|
||||
mdResyncCorr: String
|
||||
mdResyncPos: String
|
||||
mdResyncDb: String
|
||||
mdResyncDt: String
|
||||
mdResyncAction: String
|
||||
mdResyncSize: Int
|
||||
mdState: String
|
||||
mdVersion: String
|
||||
cacheNumDevices: Int
|
||||
cacheSbNumDisks: Int
|
||||
fsState: String
|
||||
|
||||
"""Human friendly string of array events happening"""
|
||||
fsProgress: String
|
||||
|
||||
"""
|
||||
Percentage from 0 - 100 while upgrading a disk or swapping parity drives
|
||||
"""
|
||||
fsCopyPrcnt: Int
|
||||
fsNumMounted: Int
|
||||
fsNumUnmountable: Int
|
||||
fsUnmountableMask: String
|
||||
|
||||
"""Total amount of user shares"""
|
||||
shareCount: Int
|
||||
|
||||
"""Total amount shares with SMB enabled"""
|
||||
shareSmbCount: Int
|
||||
|
||||
"""Total amount shares with NFS enabled"""
|
||||
shareNfsCount: Int
|
||||
|
||||
"""Total amount shares with AFP enabled"""
|
||||
shareAfpCount: Int
|
||||
shareMoverActive: Boolean
|
||||
csrfToken: String
|
||||
}
|
||||
|
||||
"""Possible error states for configuration"""
|
||||
enum ConfigErrorState {
|
||||
UNKNOWN_ERROR
|
||||
INELIGIBLE
|
||||
INVALID
|
||||
NO_KEY_SERVER
|
||||
WITHDRAWN
|
||||
}
|
||||
|
||||
type VmDomain implements Node {
|
||||
"""The unique identifier for the vm (uuid)"""
|
||||
id: PrefixedID!
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
"@nestjs/core",
|
||||
"@parcel/watcher",
|
||||
"@swc/core",
|
||||
"@tailwindcss/oxide",
|
||||
"@unraid/libvirt",
|
||||
"core-js",
|
||||
"cpu-features",
|
||||
@@ -33,10 +34,12 @@
|
||||
"esbuild",
|
||||
"nestjs-pino",
|
||||
"protobufjs",
|
||||
"sharp",
|
||||
"simple-git-hooks",
|
||||
"ssh2",
|
||||
"unrs-resolver",
|
||||
"vue-demi"
|
||||
"vue-demi",
|
||||
"workerd"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
1285
pnpm-lock.yaml
generated
1285
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -4,5 +4,5 @@
|
||||
"tabWidth": 2,
|
||||
"printWidth": 105,
|
||||
"singleQuote": true,
|
||||
"plugins": ["prettier-plugin-tailwindcss", "@ianvs/prettier-plugin-sort-imports"]
|
||||
"plugins": ["@ianvs/prettier-plugin-sort-imports"]
|
||||
}
|
||||
|
||||
@@ -19,29 +19,21 @@ const config: StorybookConfig = {
|
||||
staticDirs: ['./static'],
|
||||
|
||||
async viteFinal(config) {
|
||||
const storybookDir = dirname(new URL(import.meta.url).pathname);
|
||||
|
||||
return {
|
||||
...config,
|
||||
root: dirname(require.resolve('@storybook/builder-vite')),
|
||||
plugins: [...(config.plugins ?? [])],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': join(dirname(new URL(import.meta.url).pathname), '../src'),
|
||||
'@/components': join(dirname(new URL(import.meta.url).pathname), '../src/components'),
|
||||
'@/lib': join(dirname(new URL(import.meta.url).pathname), '../src/lib'),
|
||||
'@': join(storybookDir, '../src'),
|
||||
'@/components': join(storybookDir, '../src/components'),
|
||||
'@/lib': join(storybookDir, '../src/lib'),
|
||||
},
|
||||
},
|
||||
optimizeDeps: {
|
||||
include: [...(config.optimizeDeps?.include ?? []), '@unraid/tailwind-rem-to-rem'],
|
||||
},
|
||||
css: {
|
||||
postcss: {
|
||||
plugins: [
|
||||
(await import('tailwindcss')).default({
|
||||
config: './tailwind.config.ts',
|
||||
}),
|
||||
(await import('autoprefixer')).default,
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Preview } from '@storybook/vue3-vite';
|
||||
import { registerAllComponents } from '../src/register';
|
||||
import '@/styles/index.css';
|
||||
import '@/../.storybook/static/index.css';
|
||||
|
||||
registerAllComponents({
|
||||
pathToSharedCss: '/index.css',
|
||||
|
||||
@@ -27,36 +27,6 @@ Import the component library styles in your main entry file:
|
||||
import '@unraid/ui/style.css';
|
||||
```
|
||||
|
||||
### 2. Configure TailwindCSS
|
||||
|
||||
Create a `tailwind.config.ts` file with the following configuration:
|
||||
|
||||
```typescript
|
||||
import tailwindConfig from '@unraid/ui/tailwind.config.ts';
|
||||
import type { Config } from 'tailwindcss';
|
||||
|
||||
export default {
|
||||
presets: [tailwindConfig],
|
||||
content: [
|
||||
// ... your content paths
|
||||
'./components/**/*.{js,vue,ts}',
|
||||
'./layouts/**/*.vue',
|
||||
'./pages/**/*.vue',
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
// your theme extensions
|
||||
},
|
||||
},
|
||||
} satisfies Partial<Config>;
|
||||
```
|
||||
|
||||
This configuration:
|
||||
|
||||
- Uses the Unraid UI library's Tailwind config as a preset
|
||||
- Properly types your configuration with TypeScript
|
||||
- Allows you to extend the base theme while maintaining all Unraid UI defaults
|
||||
|
||||
## Usage
|
||||
|
||||
```vue
|
||||
@@ -249,7 +219,7 @@ const meta = {
|
||||
argTypes: {
|
||||
variant: {
|
||||
control: 'select',
|
||||
options: ['primary', 'secondary', 'outline'],
|
||||
options: ['primary', 'secondary', 'outline-solid'],
|
||||
},
|
||||
size: {
|
||||
control: 'select',
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
"style": "default",
|
||||
"typescript": true,
|
||||
"tailwind": {
|
||||
"config": "tailwind.config.ts",
|
||||
"css": "src/styles/globals.css",
|
||||
"baseColor": "neutral",
|
||||
"cssVariables": true,
|
||||
|
||||
@@ -9,11 +9,9 @@
|
||||
"types": "./dist/index.d.ts",
|
||||
"sideEffects": false,
|
||||
"files": [
|
||||
"dist",
|
||||
"tailwind.config.ts"
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"prepare": "pnpm build",
|
||||
"// Development": "",
|
||||
"dev": "vite",
|
||||
"preview": "vite preview",
|
||||
@@ -34,11 +32,11 @@
|
||||
"preunraid:deploy": "pnpm build:wc",
|
||||
"unraid:deploy": "just deploy",
|
||||
"// Storybook": "",
|
||||
"prestorybook": "pnpm storybook:css",
|
||||
"storybook": "storybook dev -p 6006",
|
||||
"storybook:css": "node scripts/build-style.mjs",
|
||||
"prebuild-storybook": "pnpm storybook:css",
|
||||
"build-storybook": "storybook build",
|
||||
"storybook": "pnpm tailwind:watch & pnpm storybook:dev",
|
||||
"storybook:dev": "storybook dev -p 6006",
|
||||
"build-storybook": "pnpm tailwind:build && storybook build",
|
||||
"tailwind:build": "tailwindcss -i ./src/styles/globals.css -o ./.storybook/static/index.css",
|
||||
"tailwind:watch": "pnpm tailwind:build --watch",
|
||||
"// Cloudflare Workers Deployment": "",
|
||||
"deploy:storybook": "pnpm build-storybook && wrangler deploy",
|
||||
"deploy:storybook:staging": "pnpm build-storybook && wrangler deploy --env staging"
|
||||
@@ -54,6 +52,7 @@
|
||||
"@jsonforms/core": "3.6.0",
|
||||
"@jsonforms/vue": "3.6.0",
|
||||
"@jsonforms/vue-vanilla": "3.6.0",
|
||||
"@tailwindcss/cli": "^4.1.11",
|
||||
"@vueuse/core": "13.5.0",
|
||||
"class-variance-authority": "0.7.1",
|
||||
"clsx": "2.1.1",
|
||||
@@ -64,6 +63,7 @@
|
||||
"reka-ui": "2.3.2",
|
||||
"shadcn-vue": "2.2.0",
|
||||
"tailwind-merge": "2.6.0",
|
||||
"tw-animate-css": "^1.3.5",
|
||||
"vue-sonner": "1.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -72,19 +72,18 @@
|
||||
"@storybook/addon-links": "9.0.17",
|
||||
"@storybook/builder-vite": "9.0.17",
|
||||
"@storybook/vue3-vite": "9.0.17",
|
||||
"@tailwindcss/typography": "0.5.16",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"@testing-library/vue": "8.1.0",
|
||||
"@types/jsdom": "21.1.7",
|
||||
"@types/node": "22.16.4",
|
||||
"@types/testing-library__vue": "5.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "8.37.0",
|
||||
"@unraid/tailwind-rem-to-rem": "1.1.0",
|
||||
"@unraid/tailwind-rem-to-rem": "2.0.0",
|
||||
"@vitejs/plugin-vue": "6.0.0",
|
||||
"@vitest/coverage-v8": "3.2.4",
|
||||
"@vitest/ui": "3.2.4",
|
||||
"@vue/test-utils": "2.4.6",
|
||||
"@vue/tsconfig": "0.7.0",
|
||||
"autoprefixer": "10.4.21",
|
||||
"concurrently": "9.2.0",
|
||||
"eslint": "9.31.0",
|
||||
"eslint-config-prettier": "10.1.5",
|
||||
@@ -96,14 +95,10 @@
|
||||
"happy-dom": "18.0.1",
|
||||
"jiti": "2.4.2",
|
||||
"postcss": "8.5.6",
|
||||
"postcss-import": "16.1.1",
|
||||
"prettier": "3.6.2",
|
||||
"prettier-plugin-tailwindcss": "0.6.14",
|
||||
"rimraf": "6.0.1",
|
||||
"storybook": "9.0.17",
|
||||
"tailwind-rem-to-rem": "github:unraid/tailwind-rem-to-rem",
|
||||
"tailwindcss": "3.4.17",
|
||||
"tailwindcss-animate": "1.0.7",
|
||||
"tailwindcss": "4.1.11",
|
||||
"typescript": "5.8.3",
|
||||
"typescript-eslint": "8.37.0",
|
||||
"vite": "7.0.4",
|
||||
@@ -124,20 +119,7 @@
|
||||
"require": "./dist/index.cjs"
|
||||
},
|
||||
"./styles": "./dist/style.css",
|
||||
"./styles/*": "./src/styles/*",
|
||||
"./tailwind.config": {
|
||||
"types": "./dist/tailwind.config.d.ts",
|
||||
"import": "./dist/tailwind.config.js",
|
||||
"default": "./dist/tailwind.config.js"
|
||||
},
|
||||
"./tailwind.config.ts": {
|
||||
"import": "./tailwind.config.ts",
|
||||
"default": "./tailwind.config.ts"
|
||||
},
|
||||
"./theme/preset": {
|
||||
"types": "./dist/theme/preset.d.ts",
|
||||
"import": "./dist/theme/preset.js"
|
||||
}
|
||||
"./styles/*": "./src/styles/*"
|
||||
},
|
||||
"packageManager": "pnpm@10.13.1"
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
@@ -1,30 +0,0 @@
|
||||
import fs from 'fs/promises';
|
||||
import autoprefixer from 'autoprefixer';
|
||||
import postcss from 'postcss';
|
||||
import postcssImport from 'postcss-import';
|
||||
import tailwindcss from 'tailwindcss';
|
||||
|
||||
/**
|
||||
* Helper script for storybook to build the CSS file for the components. This is used to ensure that modals render using the shadow styles.
|
||||
*/
|
||||
|
||||
process.env.VITE_TAILWIND_BASE_FONT_SIZE = 16;
|
||||
|
||||
const inputPath = './src/styles/index.css';
|
||||
const outputPath = './.storybook/static/index.css'; // served from root: /index.css
|
||||
|
||||
const css = await fs.readFile(inputPath, 'utf8');
|
||||
|
||||
const result = await postcss([
|
||||
postcssImport(),
|
||||
tailwindcss({ config: './tailwind.config.ts' }),
|
||||
autoprefixer(),
|
||||
]).process(css, {
|
||||
from: inputPath,
|
||||
to: outputPath,
|
||||
});
|
||||
|
||||
await fs.mkdir('./.storybook/static', { recursive: true });
|
||||
await fs.writeFile(outputPath, result.css);
|
||||
|
||||
console.log('✅ CSS built for Storybook:', outputPath);
|
||||
@@ -42,18 +42,17 @@ const props = withDefaults(defineProps<BrandButtonProps>(), {
|
||||
defineEmits(['click']);
|
||||
|
||||
const classes = computed(() => {
|
||||
const iconSize = `w-${props.size}`;
|
||||
|
||||
return {
|
||||
button: cn(
|
||||
brandButtonVariants({ variant: props.variant, size: props.size, padding: props.padding }),
|
||||
props.class
|
||||
),
|
||||
icon: `${iconSize} fill-current flex-shrink-0`,
|
||||
icon: 'w-[var(--icon-size)] fill-current shrink-0',
|
||||
iconSize: props.size ?? '16px',
|
||||
};
|
||||
});
|
||||
const needsBrandGradientBackground = computed(() => {
|
||||
return ['outline', 'outline-primary'].includes(props.variant ?? '');
|
||||
return ['outline-solid', 'outline-primary'].includes(props.variant ?? '');
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -71,15 +70,20 @@ const needsBrandGradientBackground = computed(() => {
|
||||
>
|
||||
<div
|
||||
v-if="variant === 'fill'"
|
||||
class="absolute -top-[2px] -right-[2px] -bottom-[2px] -left-[2px] -z-10 bg-gradient-to-r from-unraid-red to-orange opacity-100 transition-all rounded-md group-hover:opacity-60 group-focus:opacity-60"
|
||||
class="absolute -top-[2px] -right-[2px] -bottom-[2px] -left-[2px] -z-10 bg-linear-to-r from-unraid-red to-orange opacity-100 transition-all rounded-md group-hover:opacity-60 group-focus:opacity-60"
|
||||
/>
|
||||
<!-- gives outline buttons the brand gradient background -->
|
||||
<div
|
||||
v-if="needsBrandGradientBackground"
|
||||
class="absolute -top-[2px] -right-[2px] -bottom-[2px] -left-[2px] -z-10 bg-gradient-to-r from-unraid-red to-orange opacity-0 transition-all rounded-md group-hover:opacity-100 group-focus:opacity-100"
|
||||
class="absolute -top-[2px] -right-[2px] -bottom-[2px] -left-[2px] -z-10 bg-linear-to-r from-unraid-red to-orange opacity-0 transition-all rounded-md group-hover:opacity-100 group-focus:opacity-100"
|
||||
/>
|
||||
|
||||
<component :is="icon" v-if="icon" :class="classes.icon" />
|
||||
<component
|
||||
:is="icon"
|
||||
v-if="icon"
|
||||
:class="classes.icon"
|
||||
:style="{ '--icon-size': classes.iconSize }"
|
||||
/>
|
||||
|
||||
{{ text }}
|
||||
<slot />
|
||||
@@ -92,6 +96,7 @@ const needsBrandGradientBackground = computed(() => {
|
||||
iconRightHoverDisplay &&
|
||||
'opacity-0 group-hover:opacity-100 group-focus:opacity-100 transition-all',
|
||||
]"
|
||||
:style="{ '--icon-size': classes.iconSize }"
|
||||
/>
|
||||
</component>
|
||||
</template>
|
||||
|
||||
@@ -21,15 +21,15 @@ export const brandButtonVariants = cva(
|
||||
'underline-hover-red':
|
||||
'opacity-75 underline border-transparent transition hover:text-white hover:bg-unraid-red hover:border-unraid-red focus:text-white focus:bg-unraid-red focus:border-unraid-red hover:opacity-100 focus:opacity-100',
|
||||
white: 'text-black bg-white transition hover:bg-grey focus:bg-grey',
|
||||
none: '',
|
||||
none: 'border-transparent hover:shadow-none focus:shadow-none',
|
||||
},
|
||||
size: {
|
||||
'12px': 'text-12px gap-4px',
|
||||
'14px': 'text-14px gap-8px',
|
||||
'16px': 'text-16px gap-8px',
|
||||
'18px': 'text-18px gap-8px',
|
||||
'20px': 'text-20px gap-8px',
|
||||
'24px': 'text-24px gap-8px',
|
||||
'12px': 'text-xs gap-1',
|
||||
'14px': 'text-sm gap-2',
|
||||
'16px': 'text-base gap-2',
|
||||
'18px': 'text-lg gap-2',
|
||||
'20px': 'text-xl gap-2',
|
||||
'24px': 'text-2xl gap-2',
|
||||
},
|
||||
padding: {
|
||||
default: '',
|
||||
@@ -41,32 +41,32 @@ export const brandButtonVariants = cva(
|
||||
{
|
||||
size: '12px',
|
||||
padding: 'default',
|
||||
class: 'p-8px',
|
||||
class: 'p-2',
|
||||
},
|
||||
{
|
||||
size: '14px',
|
||||
padding: 'default',
|
||||
class: 'p-8px',
|
||||
class: 'p-2',
|
||||
},
|
||||
{
|
||||
size: '16px',
|
||||
padding: 'default',
|
||||
class: 'p-12px',
|
||||
class: 'p-3',
|
||||
},
|
||||
{
|
||||
size: '18px',
|
||||
padding: 'default',
|
||||
class: 'p-12px',
|
||||
class: 'p-3',
|
||||
},
|
||||
{
|
||||
size: '20px',
|
||||
padding: 'default',
|
||||
class: 'p-16px',
|
||||
class: 'p-4',
|
||||
},
|
||||
{
|
||||
size: '24px',
|
||||
padding: 'default',
|
||||
class: 'p-16px',
|
||||
class: 'p-4',
|
||||
},
|
||||
],
|
||||
defaultVariants: {
|
||||
|
||||
@@ -23,12 +23,12 @@ const props = withDefaults(defineProps<BadgeProps>(), {
|
||||
|
||||
const badgeClasses = computed(() => {
|
||||
const iconSizes = {
|
||||
xs: 'w-12px',
|
||||
sm: 'w-14px',
|
||||
md: 'w-16px',
|
||||
lg: 'w-18px',
|
||||
xl: 'w-20px',
|
||||
'2xl': 'w-24px',
|
||||
xs: 'w-3',
|
||||
sm: 'w-3.5',
|
||||
md: 'w-4',
|
||||
lg: 'w-4.5',
|
||||
xl: 'w-5',
|
||||
'2xl': 'w-6',
|
||||
} as const;
|
||||
|
||||
return {
|
||||
@@ -40,8 +40,8 @@ const badgeClasses = computed(() => {
|
||||
|
||||
<template>
|
||||
<span :class="[badgeClasses.badge, props.class]">
|
||||
<component :is="icon" v-if="icon" class="flex-shrink-0" :class="badgeClasses.icon" />
|
||||
<component :is="icon" v-if="icon" class="shrink-0" :class="badgeClasses.icon" />
|
||||
<slot />
|
||||
<component :is="iconRight" v-if="iconRight" class="flex-shrink-0" :class="badgeClasses.icon" />
|
||||
<component :is="iconRight" v-if="iconRight" class="shrink-0" :class="badgeClasses.icon" />
|
||||
</span>
|
||||
</template>
|
||||
|
||||
@@ -22,12 +22,12 @@ export const badgeVariants = cva(
|
||||
custom: '',
|
||||
},
|
||||
size: {
|
||||
xs: 'text-12px px-8px py-4px gap-4px',
|
||||
sm: 'text-14px px-8px py-4px gap-8px',
|
||||
md: 'text-16px px-12px py-8px gap-8px',
|
||||
lg: 'text-18px px-12px py-8px gap-8px',
|
||||
xl: 'text-20px px-16px py-12px gap-8px',
|
||||
'2xl': 'text-24px px-16px py-12px gap-8px',
|
||||
xs: 'text-xs px-2 py-1 gap-1',
|
||||
sm: 'text-sm px-2 py-1 gap-2',
|
||||
md: 'text-base px-3 py-2 gap-2',
|
||||
lg: 'text-lg px-3 py-2 gap-2',
|
||||
xl: 'text-xl px-4 py-3 gap-2',
|
||||
'2xl': 'text-2xl px-4 py-3 gap-2',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
|
||||
@@ -20,7 +20,7 @@ describe('Button', () => {
|
||||
});
|
||||
|
||||
rerender({
|
||||
props: { variant: 'outline' },
|
||||
props: { variant: 'outline-solid' },
|
||||
slots: { default: 'Delete' },
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { VariantProps } from 'class-variance-authority';
|
||||
import { cva } from 'class-variance-authority';
|
||||
|
||||
export const buttonVariants = cva(
|
||||
'inline-flex items-center justify-center rounded-md text-base font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
|
||||
'inline-flex items-center justify-center rounded-md text-base font-medium ring-offset-background transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
|
||||
@@ -35,7 +35,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
||||
v-bind="{ ...forwarded, ...$attrs }"
|
||||
:class="
|
||||
cn(
|
||||
'z-50 w-72 rounded-md bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||
'z-50 w-72 rounded-md bg-popover p-4 text-popover-foreground shadow-md outline-hidden data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
|
||||
@@ -46,12 +46,12 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
||||
<template>
|
||||
<DialogPortal :disabled="disabled" :force-mount="forceMount" :to="teleportTarget">
|
||||
<DialogOverlay
|
||||
class="fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
|
||||
class="fixed inset-0 z-50 bg-black/60 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
|
||||
/>
|
||||
<DialogContent :class="sheetClass" v-bind="forwarded">
|
||||
<slot />
|
||||
<DialogClose
|
||||
class="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary"
|
||||
class="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary"
|
||||
>
|
||||
<X class="w-4 h-4 text-muted-foreground" />
|
||||
</DialogClose>
|
||||
|
||||
@@ -22,7 +22,7 @@ const forwarded = useForwardProps(delegatedProps);
|
||||
cn(
|
||||
'inline-flex items-center justify-center rounded-full text-muted-foreground/50 w-10 h-10',
|
||||
// Disabled
|
||||
'group-data-[disabled]:text-muted-foreground group-data-[disabled]:opacity-50',
|
||||
'group-data-disabled:text-muted-foreground group-data-disabled:opacity-50',
|
||||
// Active
|
||||
'group-data-[state=active]:bg-primary group-data-[state=active]:text-primary-foreground',
|
||||
// Completed
|
||||
|
||||
@@ -24,7 +24,7 @@ const forwarded = useForwardProps(delegatedProps);
|
||||
'flex flex-col items-start gap-1',
|
||||
'md:flex-row md:items-center md:gap-2',
|
||||
'group transition-all duration-200',
|
||||
'data-[disabled]:pointer-events-none data-[disabled]:opacity-80',
|
||||
'data-disabled:pointer-events-none data-disabled:opacity-80',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
|
||||
@@ -22,7 +22,7 @@ const forwarded = useForwardProps(delegatedProps);
|
||||
cn(
|
||||
'hidden md:block bg-muted md:w-24 md:h-px md:my-0',
|
||||
// Disabled
|
||||
'group-data-[disabled]:bg-muted group-data-[disabled]:opacity-75',
|
||||
'group-data-disabled:bg-muted group-data-disabled:opacity-75',
|
||||
// Completed
|
||||
'group-data-[state=completed]:bg-accent-foreground',
|
||||
props.class
|
||||
|
||||
@@ -16,7 +16,7 @@ const delegatedProps = computed(() => {
|
||||
v-bind="delegatedProps"
|
||||
:class="
|
||||
cn(
|
||||
'flex mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
||||
'flex mt-2 ring-offset-background focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
|
||||
@@ -19,7 +19,7 @@ const forwardedProps = useForwardProps(delegatedProps);
|
||||
v-bind="forwardedProps"
|
||||
:class="
|
||||
cn(
|
||||
'inline-flex items-center justify-center whitespace-nowrap rounded px-4.5 py-2.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm',
|
||||
'inline-flex items-center justify-center whitespace-nowrap rounded px-4.5 py-2.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-xs',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
|
||||
@@ -30,7 +30,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
||||
v-bind="forwarded"
|
||||
:class="
|
||||
cn(
|
||||
'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
|
||||
'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
|
||||
@@ -21,7 +21,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
||||
v-bind="forwarded"
|
||||
:class="
|
||||
cn(
|
||||
'relative flex cursor-default gap-2 select-none justify-between items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0',
|
||||
'relative flex cursor-default gap-2 select-none justify-between items-center rounded-sm px-2 py-1.5 text-sm outline-hidden data-highlighted:bg-accent data-highlighted:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
|
||||
@@ -33,7 +33,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
||||
v-bind="{ ...forwarded, ...$attrs }"
|
||||
:class="
|
||||
cn(
|
||||
'z-50 w-[200px] rounded-md border bg-popover text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||
'z-50 w-[200px] rounded-md border bg-popover text-popover-foreground shadow-md outline-hidden data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||
position === 'popper' &&
|
||||
'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
|
||||
props.class
|
||||
@@ -45,7 +45,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
||||
cn(
|
||||
'p-1',
|
||||
position === 'popper' &&
|
||||
'h-[--reka-combobox-trigger-height] w-full min-w-[--reka-combobox-trigger-width]'
|
||||
'h-(--reka-combobox-trigger-height) w-full min-w-(--reka-combobox-trigger-width)'
|
||||
)
|
||||
"
|
||||
>
|
||||
|
||||
@@ -24,7 +24,7 @@ const modelValue = useVModel(props, 'modelValue', emits, {
|
||||
v-model="modelValue"
|
||||
:class="
|
||||
cn(
|
||||
'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
|
||||
'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
|
||||
@@ -13,18 +13,18 @@ const checked = ref(false);
|
||||
</script>
|
||||
<template>
|
||||
<SwitchGroup as="div">
|
||||
<div class="flex flex-shrink-0 items-center gap-16px">
|
||||
<div class="flex shrink-0 items-center gap-4">
|
||||
<Switch
|
||||
v-model="checked"
|
||||
:class="[
|
||||
checked ? 'bg-green-500' : 'bg-gray-200',
|
||||
'relative inline-flex h-24px w-[44px] flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2',
|
||||
'relative inline-flex h-6 w-[44px] shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-hidden focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2',
|
||||
]"
|
||||
>
|
||||
<span
|
||||
:class="[
|
||||
checked ? 'translate-x-20px' : 'translate-x-0',
|
||||
'pointer-events-none relative inline-block h-20px w-20px transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out',
|
||||
checked ? 'translate-x-5' : 'translate-x-0',
|
||||
'pointer-events-none relative inline-block h-5 w-5 transform rounded-full bg-white shadow-sm ring-0 transition duration-200 ease-in-out',
|
||||
]"
|
||||
>
|
||||
<span
|
||||
@@ -34,7 +34,7 @@ const checked = ref(false);
|
||||
]"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<svg class="h-12px w-12px text-gray-400" fill="none" viewBox="0 0 12 12">
|
||||
<svg class="h-3 w-3 text-gray-400" fill="none" viewBox="0 0 12 12">
|
||||
<path
|
||||
d="M4 8l2-2m0 0l2-2M6 6L4 4m2 2l2 2"
|
||||
stroke="currentColor"
|
||||
@@ -51,7 +51,7 @@ const checked = ref(false);
|
||||
]"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<svg class="h-12px w-12px text-green-500" fill="currentColor" viewBox="0 0 12 12">
|
||||
<svg class="h-3 w-3 text-green-500" fill="currentColor" viewBox="0 0 12 12">
|
||||
<path
|
||||
d="M3.707 5.293a1 1 0 00-1.414 1.414l1.414-1.414zM5 8l-.707.707a1 1 0 001.414 0L5 8zm4.707-3.293a1 1 0 00-1.414-1.414l1.414 1.414zm-7.414 2l2 2 1.414-1.414-2-2-1.414 1.414zm3.414 2l4-4-1.414-1.414-4 4 1.414 1.414z"
|
||||
/>
|
||||
@@ -59,7 +59,7 @@ const checked = ref(false);
|
||||
</span>
|
||||
</span>
|
||||
</Switch>
|
||||
<SwitchLabel class="text-14px">
|
||||
<SwitchLabel class="text-sm">
|
||||
{{ label }}
|
||||
</SwitchLabel>
|
||||
</div>
|
||||
|
||||
@@ -12,7 +12,7 @@ const props = defineProps<{
|
||||
<div
|
||||
:class="
|
||||
cn(
|
||||
'relative [&>[data-slot=input]]:has-[[data-slot=increment]]:pr-5 [&>[data-slot=input]]:has-[[data-slot=decrement]]:pl-5',
|
||||
'relative has-data-[slot=increment]:*:data-[slot=input]:pr-5 has-data-[slot=decrement]:*:data-[slot=input]:pl-5',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
|
||||
@@ -13,7 +13,7 @@ const props = defineProps<{
|
||||
data-slot="input"
|
||||
:class="
|
||||
cn(
|
||||
'flex h-10 w-full rounded-md border border-input bg-background py-2 text-sm text-center ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
|
||||
'flex h-10 w-full rounded-md border border-input bg-background py-2 text-sm text-center ring-offset-background placeholder:text-muted-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
|
||||
@@ -27,7 +27,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
||||
v-bind="forwarded"
|
||||
:class="
|
||||
cn(
|
||||
'peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input',
|
||||
'peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
|
||||
@@ -4,13 +4,13 @@ withDefaults(
|
||||
maxWidth?: string;
|
||||
}>(),
|
||||
{
|
||||
maxWidth: 'max-w-1024px',
|
||||
maxWidth: 'max-w-[1024px]',
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="grid gap-y-24px w-full mx-auto px-16px" :class="maxWidth">
|
||||
<div class="grid gap-y-6 w-full mx-auto px-4" :class="maxWidth">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -44,7 +44,7 @@ const { teleportTarget } = useTeleport();
|
||||
|
||||
<DialogClose
|
||||
v-if="showCloseButton !== false"
|
||||
class="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground"
|
||||
class="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground"
|
||||
>
|
||||
<X class="w-4 h-4" />
|
||||
<span class="sr-only">Close</span>
|
||||
|
||||
@@ -24,7 +24,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
||||
v-bind="forwarded"
|
||||
:class="
|
||||
cn(
|
||||
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden transition-colors focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
|
||||
@@ -18,7 +18,7 @@ const forwardedProps = useForwardProps(delegatedProps);
|
||||
v-bind="forwardedProps"
|
||||
:class="
|
||||
cn(
|
||||
'relative flex cursor-default select-none items-center rounded-sm gap-2 px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0',
|
||||
'relative flex cursor-default select-none items-center rounded-sm gap-2 px-2 py-1.5 text-sm outline-hidden transition-colors focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0',
|
||||
inset && 'pl-8',
|
||||
props.class
|
||||
)
|
||||
|
||||
@@ -25,7 +25,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
||||
v-bind="forwarded"
|
||||
:class="
|
||||
cn(
|
||||
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden transition-colors focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
|
||||
@@ -17,7 +17,7 @@ const forwardedProps = useForwardProps(delegatedProps);
|
||||
v-bind="forwardedProps"
|
||||
:class="
|
||||
cn(
|
||||
'flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent',
|
||||
'flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-hidden focus:bg-accent data-[state=open]:bg-accent',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
|
||||
@@ -8,7 +8,7 @@ const forwardedProps = useForwardProps(props);
|
||||
|
||||
<template>
|
||||
<DropdownMenuTrigger
|
||||
class="outline-none cursor-pointer [&[data-state=open]]:cursor-pointer"
|
||||
class="outline-hidden cursor-pointer data-[state=open]:cursor-pointer"
|
||||
v-bind="forwardedProps"
|
||||
>
|
||||
<slot />
|
||||
|
||||
@@ -48,7 +48,7 @@ const { teleportTarget } = useTeleport();
|
||||
cn(
|
||||
'p-1',
|
||||
position === 'popper' &&
|
||||
'h-[--reka-select-trigger-height] w-full min-w-[--reka-select-trigger-width]'
|
||||
'h-(--reka-select-trigger-height) w-full min-w-(--reka-select-trigger-width)'
|
||||
)
|
||||
"
|
||||
>
|
||||
|
||||
@@ -23,7 +23,7 @@ const forwardedProps = useForwardProps(delegatedProps);
|
||||
v-bind="forwardedProps"
|
||||
:class="
|
||||
cn(
|
||||
'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
|
||||
@@ -17,7 +17,7 @@ const forwardedProps = useForwardProps(delegatedProps);
|
||||
v-bind="forwardedProps"
|
||||
:class="
|
||||
cn(
|
||||
'flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background data-[placeholder]:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:truncate text-start',
|
||||
'flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background data-placeholder:text-muted-foreground focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:truncate text-start',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
|
||||
@@ -99,7 +99,7 @@ if (control.value.data !== undefined && control.value.data !== null) {
|
||||
<ComboboxItem
|
||||
:value="suggestion.value"
|
||||
@select="handleSelect"
|
||||
class="relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent hover:text-accent-foreground data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground"
|
||||
class="relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-hidden hover:bg-accent hover:text-accent-foreground data-highlighted:bg-accent data-highlighted:text-accent-foreground"
|
||||
>
|
||||
<span>{{ suggestion.label || suggestion.value }}</span>
|
||||
</ComboboxItem>
|
||||
@@ -113,7 +113,7 @@ if (control.value.data !== undefined && control.value.data !== null) {
|
||||
v-else
|
||||
:value="suggestion.value"
|
||||
@select="handleSelect"
|
||||
class="relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent hover:text-accent-foreground data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground"
|
||||
class="relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-hidden hover:bg-accent hover:text-accent-foreground data-highlighted:bg-accent data-highlighted:text-accent-foreground"
|
||||
>
|
||||
<span>{{ suggestion.label || suggestion.value }}</span>
|
||||
</ComboboxItem>
|
||||
|
||||
@@ -13,7 +13,7 @@ const { control } = useJsonFormsControl(props);
|
||||
|
||||
<template>
|
||||
<!-- Only render the wrapper if the control is visible -->
|
||||
<div v-if="control.visible" class="flex-grow">
|
||||
<div v-if="control.visible" class="grow">
|
||||
<!-- Render the actual control passed via the default slot -->
|
||||
<slot />
|
||||
<!-- Automatically display errors below the control -->
|
||||
|
||||
@@ -46,7 +46,7 @@ const togglePasswordVisibility = () => {
|
||||
<Input
|
||||
v-model="value"
|
||||
:type="inputType"
|
||||
:class="cn('flex-grow', classOverride, { 'pr-10': isPassword })"
|
||||
:class="cn('grow', classOverride, { 'pr-10': isPassword })"
|
||||
:disabled="!control.enabled"
|
||||
:required="control.required"
|
||||
:placeholder="control.schema.description"
|
||||
|
||||
@@ -70,7 +70,7 @@ const descriptionClass = computed(() => {
|
||||
|
||||
<template>
|
||||
<!-- Use the computed isVisible based on renderer.value.visible -->
|
||||
<div class="flex flex-col gap-2 flex-shrink-0">
|
||||
<div class="flex flex-col gap-2 shrink-0">
|
||||
<!-- Replace native label with the Label component -->
|
||||
<Label v-if="labelText" :class="labelClass">{{ labelText }}</Label>
|
||||
<!-- Use v-html with the parsedDescription ref -->
|
||||
|
||||
@@ -145,7 +145,10 @@ const getStepState = (stepIndex: number): StepState => {
|
||||
|
||||
<!-- Render elements for the current step -->
|
||||
<!-- Added key to force re-render on step change, ensuring correct elements display -->
|
||||
<div class="current-step-content rounded-md border p-4 shadow" :key="`step-content-${currentStep}`">
|
||||
<div
|
||||
class="current-step-content rounded-md border p-4 shadow-sm"
|
||||
:key="`step-content-${currentStep}`"
|
||||
>
|
||||
<DispatchRenderer
|
||||
v-for="(element, index) in currentStepElements"
|
||||
:key="`${layout.path}-${index}-step-${currentStep}`"
|
||||
|
||||
@@ -13,8 +13,5 @@ export * from '@/forms/renderers';
|
||||
// Lib
|
||||
export * from '@/lib/utils';
|
||||
|
||||
// Config
|
||||
export { default as tailwindConfig } from '../tailwind.config';
|
||||
|
||||
// Composables
|
||||
export { default as useTeleport } from '@/composables/useTeleport';
|
||||
|
||||
@@ -1,98 +1,336 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@import 'tailwindcss';
|
||||
@import 'tw-animate-css';
|
||||
|
||||
/* Force generation of all color utilities for custom colors across packages */
|
||||
@source inline("{bg,text,border,ring,fill,stroke}-{unraid-green,unraid-red}{,-50,-100,-200,-300,-400,-500,-600,-700,-800,-900,-950}");
|
||||
@source inline("{bg,text,border,ring,fill,stroke}-{yellow-accent,orange-dark,orange}");
|
||||
@source inline("{from,via,to}-{unraid-red,orange}");
|
||||
@source inline("bg-linear-to-{r,l,t,b,tr,tl,br,bl}");
|
||||
@source "../**/*.vue";
|
||||
@source "../stories/**/*.ts";
|
||||
|
||||
@custom-variant dark (&:where(.dark, .dark *));
|
||||
|
||||
/*
|
||||
The default border color has changed to `currentcolor` in Tailwind CSS v4,
|
||||
so we've added these compatibility styles to make sure everything still
|
||||
looks the same as it did with Tailwind CSS v3.
|
||||
|
||||
If we ever want to remove these styles, we need to add an explicit border
|
||||
color utility to any element that depends on these defaults.
|
||||
*/
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 0 0% 3.9%;
|
||||
|
||||
--muted: 0 0% 96.1%;
|
||||
--muted-foreground: 0 0% 45.1%;
|
||||
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 0 0% 3.9%;
|
||||
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 0 0% 3.9%;
|
||||
|
||||
--border: 0 0% 89.8%;
|
||||
--input: 0 0% 89.8%;
|
||||
|
||||
--primary: 0 0% 9%;
|
||||
--primary-foreground: 0 0% 98%;
|
||||
|
||||
--secondary: 0 0% 96.1%;
|
||||
--secondary-foreground: 0 0% 9%;
|
||||
|
||||
--accent: 0 0% 96.1%;
|
||||
--accent-foreground: 0 0% 9%;
|
||||
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
|
||||
--ring: 0 0% 3.9%;
|
||||
|
||||
--radius: 0.5rem;
|
||||
|
||||
--chart-1: 12 76% 61%;
|
||||
|
||||
--chart-2: 173 58% 39%;
|
||||
|
||||
--chart-3: 197 37% 24%;
|
||||
|
||||
--chart-4: 43 74% 66%;
|
||||
|
||||
--chart-5: 27 87% 67%;
|
||||
*,
|
||||
::after,
|
||||
::before,
|
||||
::backdrop,
|
||||
::file-selector-button {
|
||||
border-color: var(--color-border);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 0 0% 3.9%;
|
||||
--foreground: 0 0% 98%;
|
||||
|
||||
--muted: 0 0% 14.9%;
|
||||
--muted-foreground: 0 0% 63.9%;
|
||||
|
||||
--popover: 0 0% 3.9%;
|
||||
--popover-foreground: 0 0% 98%;
|
||||
|
||||
--card: 0 0% 3.9%;
|
||||
--card-foreground: 0 0% 98%;
|
||||
|
||||
--border: 0 0% 14.9%;
|
||||
--input: 0 0% 14.9%;
|
||||
|
||||
--primary: 0 0% 98%;
|
||||
--primary-foreground: 0 0% 9%;
|
||||
|
||||
--secondary: 0 0% 14.9%;
|
||||
--secondary-foreground: 0 0% 98%;
|
||||
|
||||
--accent: 0 0% 14.9%;
|
||||
--accent-foreground: 0 0% 98%;
|
||||
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
|
||||
--ring: 0 0% 83.1%;
|
||||
|
||||
--chart-1: 220 70% 50%;
|
||||
|
||||
--chart-2: 160 60% 45%;
|
||||
|
||||
--chart-3: 30 80% 55%;
|
||||
|
||||
--chart-4: 280 65% 60%;
|
||||
|
||||
--chart-5: 340 75% 55%;
|
||||
/* Fix for Tailwind v4 border styles not working */
|
||||
* {
|
||||
--tw-border-style: solid;
|
||||
}
|
||||
}
|
||||
|
||||
@theme static {
|
||||
/* Container settings */
|
||||
--container-center: true;
|
||||
--container-padding: 2rem;
|
||||
--container-screen-2xl: 1400px;
|
||||
|
||||
/* Font families */
|
||||
--font-sans:
|
||||
clear-sans, ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
|
||||
'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
|
||||
/* Grid template columns */
|
||||
--grid-template-columns-settings: 35% 1fr;
|
||||
|
||||
/* Border color default */
|
||||
--default-border-color: var(--color-border);
|
||||
|
||||
/* Color palette */
|
||||
--color-inherit: inherit;
|
||||
--color-transparent: transparent;
|
||||
--color-black: #1c1b1b;
|
||||
--color-grey-darkest: #222;
|
||||
--color-grey-darker: #606f7b;
|
||||
--color-grey-dark: #383735;
|
||||
--color-grey-mid: #999999;
|
||||
--color-grey: #e0e0e0;
|
||||
--color-grey-light: #dae1e7;
|
||||
--color-grey-lighter: #f1f5f8;
|
||||
--color-grey-lightest: #f2f2f2;
|
||||
--color-white: #ffffff;
|
||||
|
||||
/* Unraid colors */
|
||||
--color-yellow-accent: #e9bf41;
|
||||
--color-orange-dark: #f15a2c;
|
||||
--color-orange: #ff8c2f;
|
||||
|
||||
/* Unraid red palette */
|
||||
--color-unraid-red: #e22828;
|
||||
--color-unraid-red-50: #fef2f2;
|
||||
--color-unraid-red-100: #ffe1e1;
|
||||
--color-unraid-red-200: #ffc9c9;
|
||||
--color-unraid-red-300: #fea3a3;
|
||||
--color-unraid-red-400: #fc6d6d;
|
||||
--color-unraid-red-500: #f43f3f;
|
||||
--color-unraid-red-600: #e22828;
|
||||
--color-unraid-red-700: #bd1818;
|
||||
--color-unraid-red-800: #9c1818;
|
||||
--color-unraid-red-900: #821a1a;
|
||||
--color-unraid-red-950: #470808;
|
||||
|
||||
/* Unraid green palette */
|
||||
--color-unraid-green: #63a659;
|
||||
--color-unraid-green-50: #f5f9f4;
|
||||
--color-unraid-green-100: #e7f3e5;
|
||||
--color-unraid-green-200: #d0e6cc;
|
||||
--color-unraid-green-300: #aad1a4;
|
||||
--color-unraid-green-400: #7db474;
|
||||
--color-unraid-green-500: #63a659;
|
||||
--color-unraid-green-600: #457b3e;
|
||||
--color-unraid-green-700: #396134;
|
||||
--color-unraid-green-800: #314e2d;
|
||||
--color-unraid-green-900: #284126;
|
||||
--color-unraid-green-950: #122211;
|
||||
|
||||
|
||||
/* Header colors */
|
||||
--color-header-text-primary: var(--header-text-primary);
|
||||
--color-header-text-secondary: var(--header-text-secondary);
|
||||
--color-header-background-color: var(--header-background-color);
|
||||
|
||||
/* Font sizes */
|
||||
--font-10px: 10px;
|
||||
--font-12px: 12px;
|
||||
--font-14px: 14px;
|
||||
--font-16px: 16px;
|
||||
--font-18px: 18px;
|
||||
--font-20px: 20px;
|
||||
--font-24px: 24px;
|
||||
--font-30px: 30px;
|
||||
|
||||
/* Spacing */
|
||||
--spacing-4_5: 1.125rem;
|
||||
--spacing--8px: -8px;
|
||||
--spacing-2px: 2px;
|
||||
--spacing-4px: 4px;
|
||||
--spacing-6px: 6px;
|
||||
--spacing-8px: 8px;
|
||||
--spacing-10px: 10px;
|
||||
--spacing-12px: 12px;
|
||||
--spacing-14px: 14px;
|
||||
--spacing-16px: 16px;
|
||||
--spacing-20px: 20px;
|
||||
--spacing-24px: 24px;
|
||||
--spacing-28px: 28px;
|
||||
--spacing-32px: 32px;
|
||||
--spacing-36px: 36px;
|
||||
--spacing-40px: 40px;
|
||||
--spacing-64px: 64px;
|
||||
--spacing-80px: 80px;
|
||||
--spacing-90px: 90px;
|
||||
--spacing-150px: 150px;
|
||||
--spacing-160px: 160px;
|
||||
--spacing-200px: 200px;
|
||||
--spacing-260px: 260px;
|
||||
--spacing-300px: 300px;
|
||||
--spacing-310px: 310px;
|
||||
--spacing-350px: 350px;
|
||||
--spacing-448px: 448px;
|
||||
--spacing-512px: 512px;
|
||||
--spacing-640px: 640px;
|
||||
--spacing-800px: 800px;
|
||||
|
||||
/* Width and Height values */
|
||||
--width-36px: 36px;
|
||||
--height-36px: 36px;
|
||||
|
||||
/* Min/Max widths */
|
||||
--min-width-86px: 86px;
|
||||
--min-width-160px: 160px;
|
||||
--min-width-260px: 260px;
|
||||
--min-width-300px: 300px;
|
||||
--min-width-310px: 310px;
|
||||
--min-width-350px: 350px;
|
||||
--min-width-800px: 800px;
|
||||
|
||||
--max-width-86px: 86px;
|
||||
--max-width-160px: 160px;
|
||||
--max-width-260px: 260px;
|
||||
--max-width-300px: 300px;
|
||||
--max-width-310px: 310px;
|
||||
--max-width-350px: 350px;
|
||||
--max-width-640px: 640px;
|
||||
--max-width-800px: 800px;
|
||||
--max-width-1024px: 1024px;
|
||||
|
||||
/* Breakpoints */
|
||||
--breakpoint-*: initial;
|
||||
|
||||
/* Animations */
|
||||
--animate-mark-2: mark-2 1.5s ease infinite;
|
||||
--animate-mark-3: mark-3 1.5s ease infinite;
|
||||
--animate-mark-6: mark-6 1.5s ease infinite;
|
||||
--animate-mark-7: mark-7 1.5s ease infinite;
|
||||
|
||||
/* Keyframes */
|
||||
--keyframes-mark-2: 50% { transform: translateY(-40px) } to { transform: translateY(0) };
|
||||
--keyframes-mark-3: 50% { transform: translateY(-62px) } to { transform: translateY(0) };
|
||||
--keyframes-mark-6: 50% { transform: translateY(40px) } to { transform: translateY(0) };
|
||||
--keyframes-mark-7: 50% { transform: translateY(62px) } to { transform: translateY(0) };
|
||||
|
||||
/* Radius */
|
||||
--radius: 0.5rem;
|
||||
|
||||
/* Text Resizing */
|
||||
--text-xs: 1.2rem; /* 12px at 10px base */
|
||||
--text-sm: 1.4rem; /* 14px at 10px base */
|
||||
--text-base: 1.6rem; /* 16px at 10px base */
|
||||
--text-lg: 1.8rem; /* 18px at 10px base */
|
||||
--text-xl: 2rem; /* 20px at 10px base */
|
||||
--text-2xl: 2.4rem; /* 24px at 10px base */
|
||||
--text-3xl: 3rem; /* 30px at 10px base */
|
||||
--text-4xl: 3.6rem; /* 36px at 10px base */
|
||||
--text-5xl: 4.8rem; /* 48px at 10px base */
|
||||
--text-6xl: 6rem; /* 60px at 10px base */
|
||||
--text-7xl: 7.2rem; /* 72px at 10px base */
|
||||
--text-8xl: 9.6rem; /* 96px at 10px base */
|
||||
--text-9xl: 12.8rem; /* 128px at 10px base */
|
||||
--spacing: 0.4rem; /* 4px at 10px base */
|
||||
}
|
||||
|
||||
/* Hybrid theme system: Native CSS + Theme Store fallback */
|
||||
@layer base {
|
||||
/* Light mode defaults */
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 0 0% 3.9%;
|
||||
--muted: 0 0% 96.1%;
|
||||
--muted-foreground: 0 0% 45.1%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 0 0% 3.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 0 0% 3.9%;
|
||||
--border: 0 0% 89.8%;
|
||||
--input: 0 0% 89.8%;
|
||||
--primary: 0 0% 9%;
|
||||
--primary-foreground: 0 0% 98%;
|
||||
--secondary: 0 0% 96.1%;
|
||||
--secondary-foreground: 0 0% 9%;
|
||||
--accent: 0 0% 96.1%;
|
||||
--accent-foreground: 0 0% 9%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
--ring: 0 0% 3.9%;
|
||||
--chart-1: 12 76% 61%;
|
||||
--chart-2: 173 58% 39%;
|
||||
--chart-3: 197 37% 24%;
|
||||
--chart-4: 43 74% 66%;
|
||||
--chart-5: 27 87% 67%;
|
||||
}
|
||||
|
||||
/* For web components: inherit CSS variables from the host */
|
||||
:host {
|
||||
--background: inherit;
|
||||
--foreground: inherit;
|
||||
--muted: inherit;
|
||||
--muted-foreground: inherit;
|
||||
--popover: inherit;
|
||||
--popover-foreground: inherit;
|
||||
--card: inherit;
|
||||
--card-foreground: inherit;
|
||||
--border: inherit;
|
||||
--input: inherit;
|
||||
--primary: inherit;
|
||||
--primary-foreground: inherit;
|
||||
--secondary: inherit;
|
||||
--secondary-foreground: inherit;
|
||||
--accent: inherit;
|
||||
--accent-foreground: inherit;
|
||||
--destructive: inherit;
|
||||
--destructive-foreground: inherit;
|
||||
--ring: inherit;
|
||||
--chart-1: inherit;
|
||||
--chart-2: inherit;
|
||||
--chart-3: inherit;
|
||||
--chart-4: inherit;
|
||||
--chart-5: inherit;
|
||||
}
|
||||
|
||||
/* Class-based dark mode support for web components using :host-context */
|
||||
:host-context(.dark) {
|
||||
--background: 0 0% 3.9%;
|
||||
--foreground: 0 0% 98%;
|
||||
--muted: 0 0% 14.9%;
|
||||
--muted-foreground: 0 0% 63.9%;
|
||||
--popover: 0 0% 3.9%;
|
||||
--popover-foreground: 0 0% 98%;
|
||||
--card: 0 0% 3.9%;
|
||||
--card-foreground: 0 0% 98%;
|
||||
--border: 0 0% 14.9%;
|
||||
--input: 0 0% 14.9%;
|
||||
--primary: 0 0% 98%;
|
||||
--primary-foreground: 0 0% 9%;
|
||||
--secondary: 0 0% 14.9%;
|
||||
--secondary-foreground: 0 0% 98%;
|
||||
--accent: 0 0% 14.9%;
|
||||
--accent-foreground: 0 0% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
--ring: 0 0% 83.1%;
|
||||
--chart-1: 220 70% 50%;
|
||||
--chart-2: 160 60% 45%;
|
||||
--chart-3: 30 80% 55%;
|
||||
--chart-4: 280 65% 60%;
|
||||
--chart-5: 340 75% 55%;
|
||||
}
|
||||
|
||||
/* Alternative class-based dark mode support for specific Unraid themes */
|
||||
:host-context(.dark[data-theme='black']),
|
||||
:host-context(.dark[data-theme='gray']) {
|
||||
--background: 0 0% 3.9%;
|
||||
--foreground: 0 0% 98%;
|
||||
--border: 0 0% 14.9%;
|
||||
}
|
||||
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Define the theme colors that reference the existing CSS variables from web/themes */
|
||||
@theme inline {
|
||||
--color-background: hsl(var(--background));
|
||||
--color-foreground: hsl(var(--foreground));
|
||||
--color-muted: hsl(var(--muted));
|
||||
--color-muted-foreground: hsl(var(--muted-foreground));
|
||||
--color-popover: hsl(var(--popover));
|
||||
--color-popover-foreground: hsl(var(--popover-foreground));
|
||||
--color-card: hsl(var(--card));
|
||||
--color-card-foreground: hsl(var(--card-foreground));
|
||||
--color-border: hsl(var(--border));
|
||||
--color-input: hsl(var(--input));
|
||||
--color-primary: hsl(var(--primary));
|
||||
--color-primary-foreground: hsl(var(--primary-foreground));
|
||||
--color-secondary: hsl(var(--secondary));
|
||||
--color-secondary-foreground: hsl(var(--secondary-foreground));
|
||||
--color-accent: hsl(var(--accent));
|
||||
--color-accent-foreground: hsl(var(--accent-foreground));
|
||||
--color-destructive: hsl(var(--destructive));
|
||||
--color-destructive-foreground: hsl(var(--destructive-foreground));
|
||||
--color-ring: hsl(var(--ring));
|
||||
--color-chart-1: hsl(var(--chart-1, 12 76% 61%));
|
||||
--color-chart-2: hsl(var(--chart-2, 173 58% 39%));
|
||||
--color-chart-3: hsl(var(--chart-3, 197 37% 24%));
|
||||
--color-chart-4: hsl(var(--chart-4, 43 74% 66%));
|
||||
--color-chart-5: hsl(var(--chart-5, 27 87% 67%));
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
/* global styles for unraid-ui */
|
||||
@import 'globals.css';
|
||||
@import 'sonner.css';
|
||||
@import './globals.css';
|
||||
@import './sonner.css';
|
||||
|
||||
0
unraid-ui/src/styles/style.css
Normal file
0
unraid-ui/src/styles/style.css
Normal file
@@ -1,271 +0,0 @@
|
||||
import typography from '@tailwindcss/typography';
|
||||
import type { Config } from 'tailwindcss';
|
||||
import animate from 'tailwindcss-animate';
|
||||
import type { PluginAPI } from 'tailwindcss/types/config';
|
||||
|
||||
export const unraidPreset = {
|
||||
darkMode: ['selector', '[data-mode="dark"]'],
|
||||
theme: {
|
||||
container: {
|
||||
center: true,
|
||||
padding: '2rem',
|
||||
screens: {
|
||||
'2xl': '1400px',
|
||||
},
|
||||
},
|
||||
extend: {
|
||||
borderColor: {
|
||||
DEFAULT: 'hsl(var(--border))',
|
||||
},
|
||||
fontFamily: {
|
||||
sans: 'clear-sans,ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji',
|
||||
},
|
||||
gridTemplateColumns: {
|
||||
settings: '35% 1fr',
|
||||
},
|
||||
colors: {
|
||||
inherit: 'inherit',
|
||||
transparent: 'transparent',
|
||||
|
||||
black: '#1c1b1b',
|
||||
'grey-darkest': '#222',
|
||||
'grey-darker': '#606f7b',
|
||||
'grey-dark': '#383735',
|
||||
'grey-mid': '#999999',
|
||||
grey: '#e0e0e0',
|
||||
'grey-light': '#dae1e7',
|
||||
'grey-lighter': '#f1f5f8',
|
||||
'grey-lightest': '#f2f2f2',
|
||||
white: '#ffffff',
|
||||
|
||||
// unraid colors
|
||||
'yellow-accent': '#E9BF41',
|
||||
'orange-dark': '#f15a2c',
|
||||
orange: '#ff8c2f',
|
||||
'unraid-red': {
|
||||
DEFAULT: '#E22828',
|
||||
'50': '#fef2f2',
|
||||
'100': '#ffe1e1',
|
||||
'200': '#ffc9c9',
|
||||
'300': '#fea3a3',
|
||||
'400': '#fc6d6d',
|
||||
'500': '#f43f3f',
|
||||
'600': '#e22828',
|
||||
'700': '#bd1818',
|
||||
'800': '#9c1818',
|
||||
'900': '#821a1a',
|
||||
'950': '#470808',
|
||||
},
|
||||
'unraid-green': {
|
||||
DEFAULT: '#63A659',
|
||||
'50': '#f5f9f4',
|
||||
'100': '#e7f3e5',
|
||||
'200': '#d0e6cc',
|
||||
'300': '#aad1a4',
|
||||
'400': '#7db474',
|
||||
'500': '#63a659',
|
||||
'600': '#457b3e',
|
||||
'700': '#396134',
|
||||
'800': '#314e2d',
|
||||
'900': '#284126',
|
||||
'950': '#122211',
|
||||
},
|
||||
'header-text-primary': 'var(--header-text-primary)',
|
||||
'header-text-secondary': 'var(--header-text-secondary)',
|
||||
'header-background-color': 'var(--header-background-color)',
|
||||
// ShadCN
|
||||
border: 'hsl(var(--border))',
|
||||
input: 'hsl(var(--input))',
|
||||
ring: 'hsl(var(--ring))',
|
||||
background: 'hsl(var(--background))',
|
||||
foreground: 'hsl(var(--foreground))',
|
||||
primary: {
|
||||
DEFAULT: 'hsl(var(--primary))',
|
||||
foreground: 'hsl(var(--primary-foreground))',
|
||||
},
|
||||
secondary: {
|
||||
DEFAULT: 'hsl(var(--secondary))',
|
||||
foreground: 'hsl(var(--secondary-foreground))',
|
||||
},
|
||||
destructive: {
|
||||
DEFAULT: 'hsl(var(--destructive))',
|
||||
foreground: 'hsl(var(--destructive-foreground))',
|
||||
},
|
||||
muted: {
|
||||
DEFAULT: 'hsl(var(--muted))',
|
||||
foreground: 'hsl(var(--muted-foreground))',
|
||||
},
|
||||
accent: {
|
||||
DEFAULT: 'hsl(var(--accent))',
|
||||
foreground: 'hsl(var(--accent-foreground))',
|
||||
},
|
||||
popover: {
|
||||
DEFAULT: 'hsl(var(--popover))',
|
||||
foreground: 'hsl(var(--popover-foreground))',
|
||||
},
|
||||
card: {
|
||||
DEFAULT: 'hsl(var(--card))',
|
||||
foreground: 'hsl(var(--card-foreground))',
|
||||
},
|
||||
},
|
||||
fontSize: {
|
||||
'10px': '10px',
|
||||
'12px': '12px',
|
||||
'14px': '14px',
|
||||
'16px': '16px',
|
||||
'18px': '18px',
|
||||
'20px': '20px',
|
||||
'24px': '24px',
|
||||
'30px': '30px',
|
||||
},
|
||||
spacing: {
|
||||
'4.5': '1.125rem',
|
||||
'-8px': '-8px',
|
||||
'2px': '2px',
|
||||
'4px': '4px',
|
||||
'6px': '6px',
|
||||
'8px': '8px',
|
||||
'10px': '10px',
|
||||
'12px': '12px',
|
||||
'14px': '14px',
|
||||
'16px': '16px',
|
||||
'20px': '20px',
|
||||
'24px': '24px',
|
||||
'28px': '28px',
|
||||
'32px': '32px',
|
||||
'36px': '36px',
|
||||
'40px': '40px',
|
||||
'64px': '64px',
|
||||
'80px': '80px',
|
||||
'90px': '90px',
|
||||
'150px': '150px',
|
||||
'160px': '160px',
|
||||
'200px': '200px',
|
||||
'260px': '260px',
|
||||
'300px': '300px',
|
||||
'310px': '310px',
|
||||
'350px': '350px',
|
||||
'448px': '448px',
|
||||
'512px': '512px',
|
||||
'640px': '640px',
|
||||
'800px': '800px',
|
||||
},
|
||||
minWidth: {
|
||||
'86px': '86px',
|
||||
'160px': '160px',
|
||||
'260px': '260px',
|
||||
'300px': '300px',
|
||||
'310px': '310px',
|
||||
'350px': '350px',
|
||||
'800px': '800px',
|
||||
},
|
||||
maxWidth: {
|
||||
'86px': '86px',
|
||||
'160px': '160px',
|
||||
'260px': '260px',
|
||||
'300px': '300px',
|
||||
'310px': '310px',
|
||||
'350px': '350px',
|
||||
'640px': '640px',
|
||||
'800px': '800px',
|
||||
'1024px': '1024px',
|
||||
},
|
||||
screens: {
|
||||
'2xs': '470px',
|
||||
xs: '530px',
|
||||
tall: { raw: '(min-height: 700px)' },
|
||||
},
|
||||
keyframes: {
|
||||
'accordion-down': {
|
||||
from: { height: '0' },
|
||||
to: { height: 'var(--radix-accordion-content-height)' },
|
||||
},
|
||||
'accordion-up': {
|
||||
from: { height: 'var(--radix-accordion-content-height)' },
|
||||
to: { height: '0' },
|
||||
},
|
||||
'collapsible-down': {
|
||||
from: { height: '0' },
|
||||
to: { height: 'var(--radix-collapsible-content-height)' },
|
||||
},
|
||||
'collapsible-up': {
|
||||
from: { height: 'var(--radix-collapsible-content-height)' },
|
||||
to: { height: '0' },
|
||||
},
|
||||
'mark-2': {
|
||||
'50%': { transform: 'translateY(-40px)' },
|
||||
'100%': { transform: 'translateY(0)' },
|
||||
},
|
||||
'mark-3': {
|
||||
'50%': { transform: 'translateY(-62px)' },
|
||||
'100%': { transform: 'translateY(0)' },
|
||||
},
|
||||
'mark-6': {
|
||||
'50%': { transform: 'translateY(40px)' },
|
||||
'100%': { transform: 'translateY(0)' },
|
||||
},
|
||||
'mark-7': {
|
||||
'50%': { transform: 'translateY(62px)' },
|
||||
'100%': { transform: 'translateY(0)' },
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
'accordion-down': 'accordion-down 0.2s ease-out',
|
||||
'accordion-up': 'accordion-up 0.2s ease-out',
|
||||
'collapsible-down': 'collapsible-down 0.2s ease-in-out',
|
||||
'collapsible-up': 'collapsible-up 0.2s ease-in-out',
|
||||
'mark-2': 'mark-2 1.5s ease infinite',
|
||||
'mark-3': 'mark-3 1.5s ease infinite',
|
||||
'mark-6': 'mark-6 1.5s ease infinite',
|
||||
'mark-7': 'mark-7 1.5s ease infinite',
|
||||
},
|
||||
typography: (theme: PluginAPI['theme']) => ({
|
||||
DEFAULT: {
|
||||
css: {
|
||||
color: theme('colors.foreground'),
|
||||
a: {
|
||||
color: theme('colors.primary'),
|
||||
textDecoration: 'underline',
|
||||
'&:hover': {
|
||||
color: theme('colors.primary-foreground'),
|
||||
},
|
||||
},
|
||||
'--tw-prose-body': theme('colors.foreground'),
|
||||
'--tw-prose-headings': theme('colors.foreground'),
|
||||
'--tw-prose-lead': theme('colors.foreground'),
|
||||
'--tw-prose-links': theme('colors.primary'),
|
||||
'--tw-prose-bold': theme('colors.foreground'),
|
||||
'--tw-prose-counters': theme('colors.foreground'),
|
||||
'--tw-prose-bullets': theme('colors.foreground'),
|
||||
'--tw-prose-hr': theme('colors.foreground'),
|
||||
'--tw-prose-quotes': theme('colors.foreground'),
|
||||
'--tw-prose-quote-borders': theme('colors.foreground'),
|
||||
'--tw-prose-captions': theme('colors.foreground'),
|
||||
'--tw-prose-code': theme('colors.foreground'),
|
||||
'--tw-prose-pre-code': theme('colors.foreground'),
|
||||
'--tw-prose-pre-bg': theme('colors.background'),
|
||||
'--tw-prose-th-borders': theme('colors.foreground'),
|
||||
'--tw-prose-td-borders': theme('colors.foreground'),
|
||||
'--tw-prose-invert-body': theme('colors.background'),
|
||||
'--tw-prose-invert-headings': theme('colors.background'),
|
||||
'--tw-prose-invert-lead': theme('colors.background'),
|
||||
'--tw-prose-invert-links': theme('colors.primary'),
|
||||
'--tw-prose-invert-bold': theme('colors.background'),
|
||||
'--tw-prose-invert-counters': theme('colors.background'),
|
||||
'--tw-prose-invert-bullets': theme('colors.background'),
|
||||
'--tw-prose-invert-hr': theme('colors.background'),
|
||||
'--tw-prose-invert-quotes': theme('colors.background'),
|
||||
'--tw-prose-invert-quote-borders': theme('colors.background'),
|
||||
'--tw-prose-invert-captions': theme('colors.background'),
|
||||
'--tw-prose-invert-code': theme('colors.background'),
|
||||
'--tw-prose-invert-pre-code': theme('colors.background'),
|
||||
'--tw-prose-invert-pre-bg': theme('colors.foreground'),
|
||||
'--tw-prose-invert-th-borders': theme('colors.background'),
|
||||
'--tw-prose-invert-td-borders': theme('colors.background'),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
plugins: [typography, animate],
|
||||
} satisfies Partial<Config>;
|
||||
@@ -4,32 +4,113 @@ import { BrandButton } from '../../../src/components/brand/index.js';
|
||||
const meta = {
|
||||
title: 'Components/Brand',
|
||||
component: BrandButton,
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
},
|
||||
argTypes: {
|
||||
variant: {
|
||||
control: { type: 'select' },
|
||||
options: [
|
||||
'fill',
|
||||
'black',
|
||||
'gray',
|
||||
'outline',
|
||||
'outline-primary',
|
||||
'outline-black',
|
||||
'outline-white',
|
||||
'underline',
|
||||
'underline-hover-red',
|
||||
'white',
|
||||
'none',
|
||||
],
|
||||
},
|
||||
size: {
|
||||
control: { type: 'select' },
|
||||
options: ['12px', '14px', '16px', '18px', '20px', '24px'],
|
||||
},
|
||||
padding: {
|
||||
control: { type: 'select' },
|
||||
options: ['default', 'none', 'lean'],
|
||||
},
|
||||
disabled: {
|
||||
control: { type: 'boolean' },
|
||||
},
|
||||
text: {
|
||||
control: { type: 'text' },
|
||||
},
|
||||
},
|
||||
args: {
|
||||
variant: 'fill',
|
||||
size: '16px',
|
||||
padding: 'default',
|
||||
text: 'Click me',
|
||||
disabled: false,
|
||||
},
|
||||
} satisfies Meta<typeof BrandButton>;
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Button: Story = {
|
||||
args: {
|
||||
variant: 'fill',
|
||||
size: '14px',
|
||||
padding: 'default',
|
||||
text: 'Click me',
|
||||
},
|
||||
render: (args) => ({
|
||||
export const Default: Story = {};
|
||||
|
||||
export const AllVariants: Story = {
|
||||
render: () => ({
|
||||
components: { BrandButton },
|
||||
setup() {
|
||||
return { args };
|
||||
},
|
||||
template: `
|
||||
<BrandButton
|
||||
:variant="args.variant"
|
||||
:size="args.size"
|
||||
:padding="args.padding"
|
||||
:text="args.text"
|
||||
:class="args.class"
|
||||
/>
|
||||
<div class="grid grid-cols-3 gap-4 p-4">
|
||||
<div class="bg-gray-900 p-2 rounded">
|
||||
<BrandButton variant="fill" text="Fill" />
|
||||
</div>
|
||||
<BrandButton variant="black" text="Black" />
|
||||
<BrandButton variant="gray" text="Gray" />
|
||||
<BrandButton variant="outline" text="Outline" />
|
||||
<BrandButton variant="outline-primary" text="Outline Primary" />
|
||||
<BrandButton variant="outline-black" text="Outline Black" />
|
||||
<div class="bg-gray-900 p-2 rounded">
|
||||
<BrandButton variant="outline-white" text="Outline White" />
|
||||
</div>
|
||||
<BrandButton variant="underline" text="Underline" />
|
||||
<BrandButton variant="underline-hover-red" text="Underline Hover Red" />
|
||||
<BrandButton variant="white" text="White" />
|
||||
<BrandButton variant="none" text="None" />
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
};
|
||||
|
||||
export const AllSizes: Story = {
|
||||
render: () => ({
|
||||
components: { BrandButton },
|
||||
template: `
|
||||
<div class="flex flex-col gap-4 p-4">
|
||||
<BrandButton size="12px" text="12px" />
|
||||
<BrandButton size="14px" text="14px" />
|
||||
<BrandButton size="16px" text="16px" />
|
||||
<BrandButton size="18px" text="18px" />
|
||||
<BrandButton size="20px" text="20px" />
|
||||
<BrandButton size="24px" text="24px" />
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
};
|
||||
|
||||
export const AllPadding: Story = {
|
||||
render: () => ({
|
||||
components: { BrandButton },
|
||||
template: `
|
||||
<div class="flex flex-col gap-4 p-4">
|
||||
<BrandButton padding="default" text="Default Padding" />
|
||||
<BrandButton padding="none" text="No Padding" />
|
||||
<BrandButton padding="lean" text="Lean Padding" />
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
};
|
||||
|
||||
export const Disabled: Story = {
|
||||
args: {
|
||||
disabled: true,
|
||||
text: 'Disabled Button',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -60,7 +60,7 @@ export const Horizontal: Story = {
|
||||
<div class="flex p-4">
|
||||
${Array(50)
|
||||
.fill(0)
|
||||
.map((_, i) => `<div class="flex-shrink-0 mr-2">Content ${i + 1}</div>`)
|
||||
.map((_, i) => `<div class="shrink-0 mr-2">Content ${i + 1}</div>`)
|
||||
.join('')}
|
||||
</div>
|
||||
<ScrollBar orientation="horizontal" />
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
import tailwindRemToRem from '@unraid/tailwind-rem-to-rem';
|
||||
import type { Config } from 'tailwindcss';
|
||||
import tailwindcssAnimate from 'tailwindcss-animate';
|
||||
/* eslint-disable no-relative-import-paths/no-relative-import-paths */
|
||||
import { unraidPreset } from './src/theme/preset';
|
||||
|
||||
export default {
|
||||
darkMode: ['class'],
|
||||
presets: [unraidPreset],
|
||||
content: [
|
||||
'./src/components/**/*.{js,vue,ts}',
|
||||
'./src/components/**/*.ce.{js,vue,ts}',
|
||||
'./src/composables/**/*.{js,vue,ts}',
|
||||
'./stories/**/*.stories.{js,ts,jsx,mdx}',
|
||||
'./index.html',
|
||||
],
|
||||
safelist: [
|
||||
'dark',
|
||||
'unraid_mark_1',
|
||||
'unraid_mark_2',
|
||||
'unraid_mark_3',
|
||||
'unraid_mark_4',
|
||||
'unraid_mark_6',
|
||||
'unraid_mark_7',
|
||||
'unraid_mark_8',
|
||||
'unraid_mark_9',
|
||||
{
|
||||
pattern: /^text-(header-text-secondary|orange-dark)$/,
|
||||
variants: ['group-hover', 'group-focus'],
|
||||
},
|
||||
{
|
||||
pattern: /^(underline|no-underline)$/,
|
||||
variants: ['group-hover', 'group-focus'],
|
||||
},
|
||||
],
|
||||
plugins: [
|
||||
tailwindRemToRem({
|
||||
baseFontSize: 16,
|
||||
newFontSize: Number(process.env.VITE_TAILWIND_BASE_FONT_SIZE ?? 10),
|
||||
}),
|
||||
tailwindcssAnimate,
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
borderRadius: {
|
||||
lg: 'var(--radius)',
|
||||
md: 'calc(var(--radius) - 2px)',
|
||||
sm: 'calc(var(--radius) - 4px)',
|
||||
},
|
||||
colors: {
|
||||
background: 'hsl(var(--background))',
|
||||
foreground: 'hsl(var(--foreground))',
|
||||
card: {
|
||||
DEFAULT: 'hsl(var(--card))',
|
||||
foreground: 'hsl(var(--card-foreground))',
|
||||
},
|
||||
popover: {
|
||||
DEFAULT: 'hsl(var(--popover))',
|
||||
foreground: 'hsl(var(--popover-foreground))',
|
||||
},
|
||||
primary: {
|
||||
DEFAULT: 'hsl(var(--primary))',
|
||||
foreground: 'hsl(var(--primary-foreground))',
|
||||
},
|
||||
secondary: {
|
||||
DEFAULT: 'hsl(var(--secondary))',
|
||||
foreground: 'hsl(var(--secondary-foreground))',
|
||||
},
|
||||
muted: {
|
||||
DEFAULT: 'hsl(var(--muted))',
|
||||
foreground: 'hsl(var(--muted-foreground))',
|
||||
},
|
||||
accent: {
|
||||
DEFAULT: 'hsl(var(--accent))',
|
||||
foreground: 'hsl(var(--accent-foreground))',
|
||||
},
|
||||
destructive: {
|
||||
DEFAULT: 'hsl(var(--destructive))',
|
||||
foreground: 'hsl(var(--destructive-foreground))',
|
||||
},
|
||||
border: 'hsl(var(--border))',
|
||||
input: 'hsl(var(--input))',
|
||||
ring: 'hsl(var(--ring))',
|
||||
chart: {
|
||||
'1': 'hsl(var(--chart-1))',
|
||||
'2': 'hsl(var(--chart-2))',
|
||||
'3': 'hsl(var(--chart-3))',
|
||||
'4': 'hsl(var(--chart-4))',
|
||||
'5': 'hsl(var(--chart-5))',
|
||||
},
|
||||
},
|
||||
keyframes: {
|
||||
'accordion-down': {
|
||||
from: {
|
||||
height: '0',
|
||||
},
|
||||
to: {
|
||||
height: 'var(--reka-accordion-content-height)',
|
||||
},
|
||||
},
|
||||
'accordion-up': {
|
||||
from: {
|
||||
height: 'var(--reka-accordion-content-height)',
|
||||
},
|
||||
to: {
|
||||
height: '0',
|
||||
},
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
'accordion-down': 'accordion-down 0.2s ease-out',
|
||||
'accordion-up': 'accordion-up 0.2s ease-out',
|
||||
},
|
||||
},
|
||||
},
|
||||
} satisfies Partial<Config>;
|
||||
@@ -41,7 +41,6 @@
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.vue",
|
||||
"src/**/*.ce.vue",
|
||||
"tailwind.config.ts",
|
||||
"src/theme/**/*.ts",
|
||||
"**/*.config.ts",
|
||||
"eslint.config.ts",
|
||||
|
||||
@@ -1,31 +1,30 @@
|
||||
/// <reference types="vitest" />
|
||||
import { resolve } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import tailwindcss from 'tailwindcss';
|
||||
import { defineConfig } from 'vite';
|
||||
import dts from 'vite-plugin-dts';
|
||||
|
||||
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
||||
|
||||
export default function createConfig() {
|
||||
return defineConfig({
|
||||
plugins: [
|
||||
tailwindcss(),
|
||||
vue(),
|
||||
...(process.env.npm_lifecycle_script?.includes('storybook')
|
||||
? []
|
||||
: [
|
||||
dts({
|
||||
insertTypesEntry: true,
|
||||
include: ['src/**/*.ts', 'src/**/*.vue', 'tailwind.config.ts'],
|
||||
include: ['src/**/*.ts', 'src/**/*.vue'],
|
||||
outDir: 'dist',
|
||||
rollupTypes: true,
|
||||
copyDtsFiles: true,
|
||||
}),
|
||||
]),
|
||||
],
|
||||
css: {
|
||||
postcss: {
|
||||
plugins: [tailwindcss()],
|
||||
},
|
||||
},
|
||||
build: {
|
||||
cssCodeSplit: false,
|
||||
rollupOptions: {
|
||||
@@ -36,8 +35,6 @@ export default function createConfig() {
|
||||
],
|
||||
input: {
|
||||
index: resolve(__dirname, 'src/index.ts'),
|
||||
tailwind: resolve(__dirname, 'tailwind.config.ts'),
|
||||
preset: resolve(__dirname, 'src/theme/preset.ts'),
|
||||
},
|
||||
preserveEntrySignatures: 'allow-extension',
|
||||
output: {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { fileURLToPath, URL } from 'node:url';
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import tailwindcss from 'tailwindcss';
|
||||
import { defineConfig } from 'vite';
|
||||
import vueDevTools from 'vite-plugin-vue-devtools';
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [vue(), vueDevTools()],
|
||||
plugins: [tailwindcss(), vue(), vueDevTools()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
||||
@@ -15,11 +15,6 @@ export default defineConfig({
|
||||
define: {
|
||||
'process.env.NODE_ENV': JSON.stringify('production'),
|
||||
},
|
||||
css: {
|
||||
postcss: {
|
||||
plugins: [tailwindcss()],
|
||||
},
|
||||
},
|
||||
build: {
|
||||
outDir: 'dist-wc',
|
||||
manifest: 'ui.manifest.json',
|
||||
|
||||
@@ -7,7 +7,7 @@ const config = {
|
||||
tabWidth: 2,
|
||||
printWidth: 105,
|
||||
singleQuote: true,
|
||||
plugins: ['prettier-plugin-tailwindcss', '@ianvs/prettier-plugin-sort-imports'],
|
||||
plugins: ['@ianvs/prettier-plugin-sort-imports'],
|
||||
// decorators-legacy lets the import sorter transform files with decorators
|
||||
importOrderParserPlugins: ['typescript', 'decorators-legacy'],
|
||||
importOrder: [
|
||||
|
||||
@@ -164,7 +164,7 @@ describe('KeyActions', () => {
|
||||
|
||||
const button = wrapper.findComponent(BrandButton);
|
||||
|
||||
expect(button.props('class')).toContain('sm:max-w-300px');
|
||||
expect(button.props('class')).toContain('sm:max-w-[300px]');
|
||||
});
|
||||
|
||||
it('passes all required props to BrandButton component', () => {
|
||||
|
||||
@@ -167,7 +167,7 @@ describe('Modal', () => {
|
||||
expect(modalDiv.classes()).toContain('border-green-600/10');
|
||||
});
|
||||
|
||||
it('disables shadow when disableShadow is true', async () => {
|
||||
it('disables shadow-sm when disableShadow is true', async () => {
|
||||
wrapper = mount(Modal, {
|
||||
props: {
|
||||
t,
|
||||
@@ -208,7 +208,7 @@ describe('Modal', () => {
|
||||
|
||||
it('applies overlay color and opacity classes', async () => {
|
||||
const overlayColor = 'bg-blue-500';
|
||||
const overlayOpacity = 'bg-opacity-50';
|
||||
const overlayOpacity = 'bg-blue-500/50';
|
||||
|
||||
wrapper = mount(Modal, {
|
||||
props: {
|
||||
|
||||
@@ -245,7 +245,7 @@ describe('UserProfile.ce.vue', () => {
|
||||
expect(mockCopy).toHaveBeenCalledTimes(1);
|
||||
expect(mockCopy).toHaveBeenCalledWith(initialServerData.lanIp);
|
||||
|
||||
const copiedMessage = wrapper.find('.text-white.text-12px');
|
||||
const copiedMessage = wrapper.find('.text-white.text-xs');
|
||||
|
||||
expect(copiedMessage.exists()).toBe(true);
|
||||
expect(copiedMessage.text()).toContain(t('LAN IP Copied'));
|
||||
@@ -265,7 +265,7 @@ describe('UserProfile.ce.vue', () => {
|
||||
expect(copyLanIpSpy).toHaveBeenCalledTimes(1);
|
||||
expect(mockCopy).not.toHaveBeenCalled();
|
||||
|
||||
const notSupportedMessage = wrapper.find('.text-white.text-12px');
|
||||
const notSupportedMessage = wrapper.find('.text-white.text-xs');
|
||||
|
||||
expect(notSupportedMessage.exists()).toBe(true);
|
||||
expect(notSupportedMessage.text()).toContain(t('LAN IP {0}', [initialServerData.lanIp]));
|
||||
|
||||
23
web/app.vue
23
web/app.vue
@@ -1,7 +1,30 @@
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from 'vue';
|
||||
import { NuxtLayout, NuxtPage } from '#components';
|
||||
import { devConfig } from '~/helpers/env';
|
||||
|
||||
onMounted(() => {
|
||||
document.documentElement.setAttribute('data-env', devConfig.NODE_ENV || 'production');
|
||||
|
||||
|
||||
// Override text sizes back to 16px base in dev mode (from 10px base in globals.css)
|
||||
if (devConfig.NODE_ENV === 'development') {
|
||||
document.documentElement.style.setProperty('--text-xs', '0.75rem'); /* 12px */
|
||||
document.documentElement.style.setProperty('--text-sm', '0.875rem'); /* 14px */
|
||||
document.documentElement.style.setProperty('--text-base', '1rem'); /* 16px */
|
||||
document.documentElement.style.setProperty('--text-lg', '1.125rem'); /* 18px */
|
||||
document.documentElement.style.setProperty('--text-xl', '1.25rem'); /* 20px */
|
||||
document.documentElement.style.setProperty('--text-2xl', '1.5rem'); /* 24px */
|
||||
document.documentElement.style.setProperty('--text-3xl', '1.875rem'); /* 30px */
|
||||
document.documentElement.style.setProperty('--text-4xl', '2.25rem'); /* 36px */
|
||||
document.documentElement.style.setProperty('--text-5xl', '3rem'); /* 48px */
|
||||
document.documentElement.style.setProperty('--text-6xl', '3.75rem'); /* 60px */
|
||||
document.documentElement.style.setProperty('--text-7xl', '4.5rem'); /* 72px */
|
||||
document.documentElement.style.setProperty('--text-8xl', '6rem'); /* 96px */
|
||||
document.documentElement.style.setProperty('--text-9xl', '8rem'); /* 128px */
|
||||
document.documentElement.style.setProperty('--spacing', '0.25rem'); /* 4px */
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,45 +1,109 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@import 'tailwindcss';
|
||||
@import '@unraid/ui/styles';
|
||||
|
||||
/*
|
||||
darkTheme
|
||||
alpha: '#1c1b1b',
|
||||
beta: '#f2f2f2',
|
||||
gamma: '#999999',
|
||||
/* Force generation of all color utilities for custom colors across packages */
|
||||
@source inline("{bg,text,border,ring,fill,stroke}-{unraid-green,unraid-red}{,-50,-100,-200,-300,-400,-500,-600,-700,-800,-900,-950}");
|
||||
@source inline("{bg,text,border,ring,fill,stroke}-{yellow-accent,orange-dark,orange}");
|
||||
|
||||
lightTheme
|
||||
alpha: '#f2f2f2',
|
||||
beta: '#1c1b1b',
|
||||
gamma: '#999999',
|
||||
*/
|
||||
/* Scan unraid-ui package for class usage */
|
||||
@source "../unraid-ui/src/**/*.{vue,ts,js}";
|
||||
|
||||
body {
|
||||
--color-alpha: #1c1b1b;
|
||||
--color-beta: #f2f2f2;
|
||||
--color-gamma: #999999;
|
||||
--color-gamma-opaque: rgba(153, 153, 153, .5);
|
||||
--color-customgradient-start: rgba(242, 242, 242, .0);
|
||||
--color-customgradient-end: rgba(242, 242, 242, .85);
|
||||
--shadow-beta: 0 25px 50px -12px rgba(242, 242, 242, .15);
|
||||
--ring-offset-shadow: 0 0 --var(--color-beta);
|
||||
--ring-shadow: 0 0 --var(--color-beta);
|
||||
@custom-variant dark (&:where(.dark, .dark *));
|
||||
|
||||
@theme {
|
||||
/* Header colors */
|
||||
--color-header-text-primary: var(--header-text-primary);
|
||||
--color-header-text-secondary: var(--header-text-secondary);
|
||||
--color-header-background-color: var(--header-background-color);
|
||||
|
||||
/* Legacy colors */
|
||||
--color-alpha: var(--color-alpha);
|
||||
--color-beta: var(--color-beta);
|
||||
--color-gamma: var(--color-gamma);
|
||||
--color-gamma-opaque: var(--color-gamma-opaque);
|
||||
--color-customgradient-start: var(--color-customgradient-start);
|
||||
--color-customgradient-end: var(--color-customgradient-end);
|
||||
|
||||
/* Gradients */
|
||||
--color-header-gradient-start: var(--header-gradient-start);
|
||||
--color-header-gradient-end: var(--header-gradient-end);
|
||||
--color-banner-gradient: var(--banner-gradient);
|
||||
}
|
||||
|
||||
/* .button-gradient-border-to-bg {
|
||||
background: linear-gradient(to right,#e03237 0%,#fd8c3c 100%) left top no-repeat,linear-gradient(to right,#e03237 0%,#fd8c3c 100%) left bottom no-repeat,linear-gradient(to top,#e03237 0%,#e03237 100%) left bottom no-repeat,linear-gradient(to top,#fd8c3c 0%,#fd8c3c 100%) right bottom no-repeat;
|
||||
background-size: 100% 2px,100% 2px,2px 100%,2px 100%;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background: linear-gradient(to right,#E22828 0%,#FF8C2F 100%);
|
||||
@layer utilities {
|
||||
:host {
|
||||
--tw-divide-y-reverse: 0;
|
||||
--tw-border-style: solid;
|
||||
--tw-font-weight: initial;
|
||||
--tw-tracking: initial;
|
||||
--tw-translate-x: 0;
|
||||
--tw-translate-y: 0;
|
||||
--tw-translate-z: 0;
|
||||
--tw-rotate-x: rotateX(0);
|
||||
--tw-rotate-y: rotateY(0);
|
||||
--tw-rotate-z: rotateZ(0);
|
||||
--tw-skew-x: skewX(0);
|
||||
--tw-skew-y: skewY(0);
|
||||
--tw-space-x-reverse: 0;
|
||||
--tw-gradient-position: initial;
|
||||
--tw-gradient-from: #0000;
|
||||
--tw-gradient-via: #0000;
|
||||
--tw-gradient-to: #0000;
|
||||
--tw-gradient-stops: initial;
|
||||
--tw-gradient-via-stops: initial;
|
||||
--tw-gradient-from-position: 0%;
|
||||
--tw-gradient-via-position: 50%;
|
||||
--tw-gradient-to-position: 100%;
|
||||
--tw-shadow: 0 0 #0000;
|
||||
--tw-shadow-color: initial;
|
||||
--tw-inset-shadow: 0 0 #0000;
|
||||
--tw-inset-shadow-color: initial;
|
||||
--tw-ring-color: initial;
|
||||
--tw-ring-shadow: 0 0 #0000;
|
||||
--tw-inset-ring-color: initial;
|
||||
--tw-inset-ring-shadow: 0 0 #0000;
|
||||
--tw-ring-inset: initial;
|
||||
--tw-ring-offset-width: 0px;
|
||||
--tw-ring-offset-color: #fff;
|
||||
--tw-ring-offset-shadow: 0 0 #0000;
|
||||
--tw-blur: initial;
|
||||
--tw-brightness: initial;
|
||||
--tw-contrast: initial;
|
||||
--tw-grayscale: initial;
|
||||
--tw-hue-rotate: initial;
|
||||
--tw-invert: initial;
|
||||
--tw-opacity: initial;
|
||||
--tw-saturate: initial;
|
||||
--tw-sepia: initial;
|
||||
--tw-drop-shadow: initial;
|
||||
--tw-duration: initial;
|
||||
--tw-ease: initial;
|
||||
}
|
||||
} */
|
||||
|
||||
/* Ensure this is always at the bottom – @see https://tailwindcss.com/docs/content-configuration#working-with-third-party-libraries */
|
||||
@tailwind utilities;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
*,
|
||||
::after,
|
||||
::before,
|
||||
::backdrop,
|
||||
::file-selector-button {
|
||||
border-color: hsl(var(--border));
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
--color-alpha: #1c1b1b;
|
||||
--color-beta: #f2f2f2;
|
||||
--color-gamma: #999999;
|
||||
--color-gamma-opaque: rgba(153, 153, 153, 0.5);
|
||||
--color-customgradient-start: rgba(242, 242, 242, 0);
|
||||
--color-customgradient-end: rgba(242, 242, 242, 0.85);
|
||||
--shadow-beta: 0 25px 50px -12px rgba(242, 242, 242, 0.15);
|
||||
--ring-offset-shadow: 0 0 --var(--color-beta);
|
||||
--ring-shadow: 0 0 --var(--color-beta);
|
||||
}
|
||||
|
||||
button:not(:disabled),
|
||||
[role='button']:not(:disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,4 +41,4 @@ const config: CodegenConfig = {
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
export default config;
|
||||
|
||||
@@ -67,8 +67,8 @@ const docsButtons = computed<BrandButtonProps[]>(() => {
|
||||
:title-in-main="partnerInfo?.hasPartnerLogo"
|
||||
:description="description"
|
||||
overlay-color="bg-background"
|
||||
overlay-opacity="bg-opacity-100"
|
||||
max-width="max-w-800px"
|
||||
overlay-opacity="bg-background/100"
|
||||
max-width="max-w-[800px]"
|
||||
:modal-vertical-center="false"
|
||||
:disable-shadow="true"
|
||||
>
|
||||
@@ -77,7 +77,7 @@ const docsButtons = computed<BrandButtonProps[]>(() => {
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<div class="w-full flex gap-8px justify-center mx-auto">
|
||||
<div class="w-full flex gap-2 justify-center mx-auto">
|
||||
<BrandButton
|
||||
:text="t('Activate Now')"
|
||||
:icon-right="ArrowTopRightOnSquareIcon"
|
||||
|
||||
@@ -18,5 +18,5 @@ const { darkMode } = storeToRefs(useThemeStore());
|
||||
:src="partnerInfo?.partnerLogoUrl"
|
||||
class="w-72"
|
||||
:class="{ invert: darkMode && partnerInfo.hasPartnerLogo }"
|
||||
/>
|
||||
>
|
||||
</template>
|
||||
|
||||
@@ -72,12 +72,12 @@ const steps: readonly Step[] = [
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Stepper :default-value="activeStep" class="text-foreground flex w-full items-start gap-2 text-16px">
|
||||
<Stepper :default-value="activeStep" class="text-foreground flex w-full items-start gap-2 text-base">
|
||||
<StepperItem
|
||||
v-for="step in steps"
|
||||
:key="step.step"
|
||||
v-slot="{ state }: { state: StepState }"
|
||||
class="relative flex w-full flex-col items-center justify-center data-[disabled]:opacity-100"
|
||||
class="relative flex w-full flex-col items-center justify-center data-disabled:opacity-100"
|
||||
:step="step.step"
|
||||
:disabled="true"
|
||||
>
|
||||
|
||||
@@ -57,7 +57,7 @@ watchEffect(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="modals" ref="modals" class="relative z-[99999]">
|
||||
<div id="modals" ref="modals" class="relative z-99999">
|
||||
<Dialog
|
||||
v-model="showModal"
|
||||
:show-footer="false"
|
||||
@@ -70,9 +70,9 @@ watchEffect(() => {
|
||||
<ActivationPartnerLogo />
|
||||
</div>
|
||||
|
||||
<h1 class="text-center text-20px sm:text-24px font-semibold mt-4">{{ title }}</h1>
|
||||
<h1 class="text-center text-xl sm:text-2xl font-semibold mt-4">{{ title }}</h1>
|
||||
<div class="sm:max-w-lg mx-auto mt-2 text-center">
|
||||
<p class="text-18px sm:text-20px opacity-75">
|
||||
<p class="text-lg sm:text-xl opacity-75">
|
||||
{{ t(`First, you'll create your device's login credentials, then you'll activate your Unraid license—your device's operating system (OS).`) }}
|
||||
</p>
|
||||
</div>
|
||||
@@ -89,7 +89,7 @@ watchEffect(() => {
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="postcss">
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
|
||||
@@ -38,4 +38,4 @@ export const ACTIVATION_CODE_QUERY = graphql(/* GraphQL */ `
|
||||
}
|
||||
}
|
||||
}
|
||||
`);
|
||||
`);
|
||||
|
||||
@@ -207,7 +207,7 @@ async function upsertKey() {
|
||||
"
|
||||
@primary-click="upsertKey"
|
||||
>
|
||||
<div class="max-w-800px">
|
||||
<div class="max-w-[800px]">
|
||||
<form @submit.prevent="upsertKey">
|
||||
<div class="mb-2">
|
||||
<Label for="api-key-name">Name</Label>
|
||||
@@ -290,7 +290,7 @@ async function upsertKey() {
|
||||
(e.target as HTMLInputElement)?.checked
|
||||
)
|
||||
"
|
||||
/>
|
||||
>
|
||||
<span class="text-sm">{{ action }}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@@ -127,7 +127,7 @@ async function copyKeyValue(keyValue: string) {
|
||||
}}</Badge>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-2 flex-shrink-0">
|
||||
<div class="flex gap-2 shrink-0">
|
||||
<Button variant="secondary" size="sm" @click="openCreateModal(key)">Edit</Button>
|
||||
<Button variant="destructive" size="sm" @click="_deleteKey(key.id)">Delete</Button>
|
||||
</div>
|
||||
|
||||
@@ -27,4 +27,4 @@ export function actionVariant(action: string):
|
||||
default:
|
||||
return 'gray';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import ApiKeyManager from '~/components/ApiKey/ApiKeyManager.vue';
|
||||
<ApiKeyManager />
|
||||
</div>
|
||||
</template>
|
||||
<style lang="postcss">
|
||||
<style>
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
|
||||
@@ -13,10 +13,10 @@ const { authAction, stateData } = storeToRefs(serverStore);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="whitespace-normal flex flex-col gap-y-16px max-w-3xl">
|
||||
<div class="whitespace-normal flex flex-col gap-y-4 max-w-3xl">
|
||||
<span v-if="stateData.error" class="text-unraid-red font-semibold">
|
||||
<h3 class="text-16px mb-8px">{{ t(stateData.heading) }}</h3>
|
||||
<span class="text-14px" v-html="t(stateData.message)" />
|
||||
<h3 class="text-base mb-2">{{ t(stateData.heading) }}</h3>
|
||||
<span class="text-sm" v-html="t(stateData.message)" />
|
||||
</span>
|
||||
<span v-if="authAction">
|
||||
<BrandButton
|
||||
@@ -31,7 +31,7 @@ const { authAction, stateData } = storeToRefs(serverStore);
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="postcss">
|
||||
<style>
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
|
||||
@@ -19,15 +19,15 @@ const { avatar, connectPluginInstalled, registered, username } = storeToRefs(ser
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<figure class="group relative z-0 flex items-center justify-center w-36px h-36px rounded-full bg-gradient-to-r from-unraid-red to-orange">
|
||||
<figure class="group relative z-0 flex items-center justify-center w-9 h-9 rounded-full bg-linear-to-r from-unraid-red to-orange">
|
||||
<img
|
||||
v-if="avatar && connectPluginInstalled && registered"
|
||||
:src="avatar"
|
||||
:alt="username"
|
||||
class="absolute z-10 inset-0 w-36px h-36px rounded-full overflow-hidden"
|
||||
class="absolute z-10 inset-0 w-9 h-9 rounded-full overflow-hidden"
|
||||
>
|
||||
<template v-else>
|
||||
<BrandMark gradient-start="#fff" gradient-stop="#fff" class="opacity-100 absolute z-10 w-36px px-4px" />
|
||||
<BrandMark gradient-start="#fff" gradient-stop="#fff" class="opacity-100 absolute z-10 w-9 px-[4px]" />
|
||||
</template>
|
||||
</figure>
|
||||
</template>
|
||||
|
||||
@@ -15,7 +15,7 @@ onBeforeMount(() => {
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="postcss">
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
|
||||
@@ -87,7 +87,7 @@ watch([form], () => {
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="postcss">
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
|
||||
@@ -107,14 +107,14 @@ const onChange = ({ data }: { data: Record<string, unknown> }) => {
|
||||
>
|
||||
<template v-if="connectPluginInstalled">
|
||||
<Label>Account Status:</Label>
|
||||
<div v-html="'<unraid-auth></unraid-auth>'"></div>
|
||||
<div v-html="'<unraid-auth></unraid-auth>'"/>
|
||||
</template>
|
||||
<Label>Download Unraid API Logs:</Label>
|
||||
<div
|
||||
v-html="
|
||||
'<unraid-download-api-logs></unraid-download-api-logs>'
|
||||
"
|
||||
></div>
|
||||
/>
|
||||
</div>
|
||||
<!-- auto-generated settings form -->
|
||||
<div class="mt-6 pl-3 [&_.vertical-layout]:space-y-6">
|
||||
@@ -136,7 +136,6 @@ const onChange = ({ data }: { data: Record<string, unknown> }) => {
|
||||
</div>
|
||||
<div class="col-start-2 ml-10 space-y-4">
|
||||
<BrandButton
|
||||
variant="outline-primary"
|
||||
padding="lean"
|
||||
size="12px"
|
||||
class="leading-normal"
|
||||
@@ -151,7 +150,7 @@ const onChange = ({ data }: { data: Record<string, unknown> }) => {
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="postcss">
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '../../assets/main.css';
|
||||
|
||||
@@ -24,7 +24,7 @@ import { CogIcon } from '@heroicons/vue/24/solid';
|
||||
|
||||
</template>
|
||||
|
||||
<style lang="postcss">
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
|
||||
@@ -78,7 +78,7 @@ onBeforeMount(() => {
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="postcss">
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
|
||||
@@ -16,7 +16,7 @@ const downloadUrl = computed(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="whitespace-normal flex flex-col gap-y-16px max-w-3xl">
|
||||
<div class="whitespace-normal flex flex-col gap-y-4 max-w-3xl">
|
||||
<span>
|
||||
{{ t('The primary method of support for Unraid Connect is through our forums and Discord.') }}
|
||||
{{
|
||||
@@ -26,7 +26,7 @@ const downloadUrl = computed(() => {
|
||||
}}
|
||||
{{ t('The logs may contain sensitive information so do not post them publicly.') }}
|
||||
</span>
|
||||
<span class="flex flex-col gap-y-16px">
|
||||
<span class="flex flex-col gap-y-4">
|
||||
<div class="flex">
|
||||
<BrandButton
|
||||
class="grow-0 shrink-0"
|
||||
@@ -39,40 +39,40 @@ const downloadUrl = computed(() => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row items-baseline gap-8px">
|
||||
<div class="flex flex-row items-baseline gap-2">
|
||||
<a
|
||||
:href="CONNECT_FORUMS.toString()"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-[#486dba] hover:text-[#3b5ea9] focus:text-[#3b5ea9] hover:underline focus:underline inline-flex flex-row items-center justify-start gap-8px"
|
||||
class="text-[#486dba] hover:text-[#3b5ea9] focus:text-[#3b5ea9] hover:underline focus:underline inline-flex flex-row items-center justify-start gap-2"
|
||||
>
|
||||
{{ t('Unraid Connect Forums') }}
|
||||
<ArrowTopRightOnSquareIcon class="w-16px" />
|
||||
<ArrowTopRightOnSquareIcon class="w-4" />
|
||||
</a>
|
||||
<a
|
||||
:href="DISCORD.toString()"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-[#486dba] hover:text-[#3b5ea9] focus:text-[#3b5ea9] hover:underline focus:underline inline-flex flex-row items-center justify-start gap-8px"
|
||||
class="text-[#486dba] hover:text-[#3b5ea9] focus:text-[#3b5ea9] hover:underline focus:underline inline-flex flex-row items-center justify-start gap-2"
|
||||
>
|
||||
{{ t('Unraid Discord') }}
|
||||
<ArrowTopRightOnSquareIcon class="w-16px" />
|
||||
<ArrowTopRightOnSquareIcon class="w-4" />
|
||||
</a>
|
||||
<a
|
||||
:href="CONTACT.toString()"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-[#486dba] hover:text-[#3b5ea9] focus:text-[#3b5ea9] hover:underline focus:underline inline-flex flex-row items-center justify-start gap-8px"
|
||||
class="text-[#486dba] hover:text-[#3b5ea9] focus:text-[#3b5ea9] hover:underline focus:underline inline-flex flex-row items-center justify-start gap-2"
|
||||
>
|
||||
{{ t('Unraid Contact Page') }}
|
||||
<ArrowTopRightOnSquareIcon class="w-16px" />
|
||||
<ArrowTopRightOnSquareIcon class="w-4" />
|
||||
</a>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="postcss">
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
|
||||
@@ -27,7 +27,7 @@ const { selector, serverState } = storeToRefs(store);
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="postcss">
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
|
||||
@@ -90,7 +90,7 @@ const updateOsStatus = computed(() => {
|
||||
:src="'/webGui/images/UN-logotype-gradient.svg'"
|
||||
class="w-[160px] h-auto max-h-[30px] object-contain"
|
||||
alt="Unraid Logo"
|
||||
/>
|
||||
>
|
||||
</a>
|
||||
|
||||
<div class="flex flex-wrap justify-start gap-2">
|
||||
@@ -101,7 +101,7 @@ const updateOsStatus = computed(() => {
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<InformationCircleIcon class="fill-current w-3 h-3 xs:w-4 xs:h-4 flex-shrink-0" />
|
||||
<InformationCircleIcon class="fill-current w-3 h-3 xs:w-4 xs:h-4 shrink-0" />
|
||||
{{ osVersion }}
|
||||
</a>
|
||||
<component
|
||||
@@ -128,7 +128,7 @@ const updateOsStatus = computed(() => {
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="postcss">
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
|
||||
|
||||
@@ -46,10 +46,10 @@ const filteredKeyActions = computed((): ServerStateDataAction[] | undefined => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ul v-if="filteredKeyActions" class="flex flex-col gap-y-8px">
|
||||
<ul v-if="filteredKeyActions" class="flex flex-col gap-y-2">
|
||||
<li v-for="action in filteredKeyActions" :key="action.name">
|
||||
<BrandButton
|
||||
:class="cn('w-full', props.maxWidth ? 'sm:max-w-300px' : '')"
|
||||
:class="cn('w-full', props.maxWidth ? 'sm:max-w-[300px]' : '')"
|
||||
:disabled="action?.disabled"
|
||||
:external="action?.external"
|
||||
:href="action?.href"
|
||||
|
||||
@@ -192,7 +192,7 @@ watch(selectedLogFile, (newValue) => {
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="postcss">
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
|
||||
@@ -371,7 +371,7 @@ defineExpose({ refreshLogContent });
|
||||
<div
|
||||
class="w-2 h-2 rounded-full bg-green-500 animate-pulse cursor-help"
|
||||
aria-hidden="true"
|
||||
></div>
|
||||
/>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Watching log file</p>
|
||||
@@ -427,7 +427,7 @@ defineExpose({ refreshLogContent });
|
||||
<!-- Loading indicator for loading more content -->
|
||||
<div
|
||||
v-if="state.isLoadingMore"
|
||||
class="sticky top-0 z-10 bg-muted/80 backdrop-blur-sm border-b border-border rounded-md mx-2 mt-2"
|
||||
class="sticky top-0 z-10 bg-muted/80 backdrop-blur-xs border-b border-border rounded-md mx-2 mt-2"
|
||||
>
|
||||
<div class="flex items-center justify-center p-2 text-xs text-primary-foreground">
|
||||
<ArrowPathIcon class="h-3 w-3 mr-2 animate-spin" aria-hidden="true" />
|
||||
@@ -439,12 +439,12 @@ defineExpose({ refreshLogContent });
|
||||
class="font-mono whitespace-pre-wrap p-4 m-0 text-xs leading-6 hljs"
|
||||
:class="{ 'theme-dark': isDarkMode, 'theme-light': !isDarkMode }"
|
||||
v-html="logContent"
|
||||
></pre>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
<style scoped>
|
||||
/* Define CSS variables for both light and dark themes */
|
||||
:root {
|
||||
/* Light theme colors (default) - adjusted for better readability */
|
||||
|
||||
@@ -8,4 +8,4 @@ export const LOG_FILE_SUBSCRIPTION = graphql(/* GraphQL */ `
|
||||
totalLines
|
||||
}
|
||||
}
|
||||
`);
|
||||
`);
|
||||
|
||||
@@ -39,7 +39,7 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
titleInMain: false,
|
||||
headerJustifyCenter: true,
|
||||
overlayColor: 'bg-black',
|
||||
overlayOpacity: 'bg-opacity-80',
|
||||
overlayOpacity: 'bg-black/80',
|
||||
modalVerticalCenter: true,
|
||||
disableShadow: false,
|
||||
disableOverlayClose: false,
|
||||
@@ -83,7 +83,7 @@ const computedVerticalCenter = computed<string>(() => {
|
||||
@keyup.esc="closeModal"
|
||||
>
|
||||
<div
|
||||
class="fixed inset-0 flex flex-col min-h-screen w-screen items-center p-8px sm:p-16px overflow-y-auto"
|
||||
class="fixed inset-0 flex flex-col min-h-screen w-screen items-center p-2 sm:p-4 overflow-y-auto"
|
||||
:class="computedVerticalCenter"
|
||||
>
|
||||
<TransitionChild
|
||||
@@ -119,11 +119,11 @@ const computedVerticalCenter = computed<string>(() => {
|
||||
success ? 'shadow-green-600/30 border-green-600/10' : '',
|
||||
!error && !success && !disableShadow ? 'shadow-orange/10 border-white/10' : '',
|
||||
]"
|
||||
class="text-16px text-foreground bg-background text-left relative z-10 mx-auto flex flex-col justify-around border-2 border-solid transform overflow-hidden rounded-lg transition-all"
|
||||
class="text-base text-foreground bg-background text-left relative z-10 mx-auto flex flex-col justify-around border-2 border-solid transform overflow-hidden rounded-lg transition-all"
|
||||
>
|
||||
<div v-if="showCloseX" class="absolute z-20 right-0 top-0 pt-4px pr-4px sm:block">
|
||||
<div v-if="showCloseX" class="absolute z-20 right-0 top-0 pt-1 pr-1 sm:block">
|
||||
<button
|
||||
class="rounded-md text-foreground bg-transparent p-2 hover:text-white focus:text-white hover:bg-unraid-red focus:bg-unraid-red focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
|
||||
class="rounded-md text-foreground bg-transparent p-2 hover:text-white focus:text-white hover:bg-unraid-red focus:bg-unraid-red focus:outline-hidden focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
|
||||
type="button"
|
||||
@click="closeModal"
|
||||
>
|
||||
@@ -133,9 +133,9 @@ const computedVerticalCenter = computed<string>(() => {
|
||||
</div>
|
||||
|
||||
<header
|
||||
class="relative z-0 grid items-start gap-2 p-16px md:p-24px rounded-t"
|
||||
class="relative z-0 grid items-start gap-2 p-4 md:p-6 rounded-t"
|
||||
:class="{
|
||||
'sm:pr-40px': showCloseX,
|
||||
'sm:pr-10': showCloseX,
|
||||
'justify-between': !headerJustifyCenter,
|
||||
'justify-center': headerJustifyCenter,
|
||||
}"
|
||||
@@ -145,7 +145,7 @@ const computedVerticalCenter = computed<string>(() => {
|
||||
<h1
|
||||
v-if="title && !titleInMain"
|
||||
:id="ariaLablledById"
|
||||
class="text-center text-20px sm:text-24px font-semibold flex flex-wrap justify-center gap-x-4px"
|
||||
class="text-center text-xl sm:text-2xl font-semibold flex flex-wrap justify-center gap-x-1"
|
||||
>
|
||||
{{ title }}
|
||||
<slot name="headerTitle" />
|
||||
@@ -156,26 +156,26 @@ const computedVerticalCenter = computed<string>(() => {
|
||||
|
||||
<div
|
||||
v-if="$slots['main'] || description"
|
||||
class="relative max-h-[65vh] tall:max-h-[75vh] flex flex-col gap-y-16px sm:gap-y-24px p-16px md:p-24px overflow-y-auto"
|
||||
class="relative max-h-[65vh] tall:max-h-[75vh] flex flex-col gap-y-4 sm:gap-y-6 p-4 md:p-6 overflow-y-auto"
|
||||
:class="[centerContent && 'text-center', !disableShadow && 'shadow-inner']"
|
||||
>
|
||||
<div class="flex flex-col gap-y-12px">
|
||||
<div class="flex flex-col gap-y-3">
|
||||
<h1
|
||||
v-if="title && titleInMain"
|
||||
:id="ariaLablledById"
|
||||
class="text-center text-20px sm:text-24px font-semibold flex flex-wrap justify-center gap-x-4px"
|
||||
class="text-center text-xl sm:text-2xl font-semibold flex flex-wrap justify-center gap-x-1"
|
||||
>
|
||||
{{ title }}
|
||||
<slot name="headerTitle" />
|
||||
</h1>
|
||||
<h2 v-if="description" class="text-18px sm:text-20px opacity-75" v-html="description" />
|
||||
<h2 v-if="description" class="text-lg sm:text-xl opacity-75" v-html="description" />
|
||||
</div>
|
||||
<div v-if="$slots['main']">
|
||||
<slot name="main" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer v-if="$slots['footer']" class="text-14px relative p-16px md:p-24px">
|
||||
<footer v-if="$slots['footer']" class="text-sm relative p-4 md:p-6">
|
||||
<div class="absolute z-0 inset-0 opacity-10 bg-popover" />
|
||||
<div class="relative z-10">
|
||||
<slot name="footer" />
|
||||
|
||||
@@ -22,7 +22,7 @@ const { modalVisible: apiKeyModalVisible } = storeToRefs(useApiKeyStore());
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="modals" ref="modals" class="relative z-[99999]">
|
||||
<div id="modals" ref="modals" class="relative z-99999">
|
||||
<UpcCallbackFeedback :t="t" :open="callbackStatus !== 'ready'" />
|
||||
<UpcTrial :t="t" :open="trialModalVisible" />
|
||||
<UpdateOsCheckUpdateResponseModal :t="t" :open="updateOsModalVisible" />
|
||||
@@ -32,7 +32,7 @@ const { modalVisible: apiKeyModalVisible } = storeToRefs(useApiKeyStore());
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="postcss">
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
|
||||
@@ -131,7 +131,7 @@ const prepareToViewNotifications = () => {
|
||||
</SheetTrigger>
|
||||
<SheetContent
|
||||
side="right"
|
||||
class="w-full max-w-[100vw] sm:max-w-[540px] max-h-screen h-screen min-h-screen px-0 flex flex-col gap-5 pb-0"
|
||||
class="w-full max-w-screen sm:max-w-[540px] max-h-screen h-screen min-h-screen px-0 flex flex-col gap-5 pb-0"
|
||||
>
|
||||
<div class="relative flex flex-col h-full w-full">
|
||||
<SheetHeader class="ml-1 px-6 items-baseline gap-1 pb-2">
|
||||
@@ -188,7 +188,7 @@ const prepareToViewNotifications = () => {
|
||||
importance = strVal === 'all' || !strVal ? undefined : (strVal as Importance);
|
||||
}
|
||||
"
|
||||
></Select>
|
||||
/>
|
||||
</div>
|
||||
|
||||
<TabsContent value="unread" class="flex-col flex-1 min-h-0">
|
||||
|
||||
@@ -204,7 +204,7 @@ provide('isSubmitting', isCreating);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-white rounded-lg border border-gray-200 shadow-sm">
|
||||
<div class="bg-white rounded-lg border border-gray-200 shadow-xs">
|
||||
<div class="p-6">
|
||||
<h2 class="text-xl font-medium mb-4">Configure RClone Remote</h2>
|
||||
|
||||
@@ -239,7 +239,7 @@ provide('isSubmitting', isCreating);
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="postcss">
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
</style>
|
||||
|
||||
@@ -126,7 +126,7 @@ declare global {
|
||||
|
||||
<div
|
||||
v-if="showConfigModal"
|
||||
class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50"
|
||||
class="fixed inset-0 bg-black/50 flex items-center justify-center z-50"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="modal-title"
|
||||
|
||||
@@ -33,7 +33,7 @@ const getLinkedRemote = (remote: string | unknown): string => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-white rounded-lg border border-gray-200 shadow-sm p-4">
|
||||
<div class="bg-white rounded-lg border border-gray-200 shadow-xs p-4">
|
||||
<div class="flex justify-between items-start">
|
||||
<div class="space-y-1">
|
||||
<h3 class="text-lg font-medium">{{ remote.name }}</h3>
|
||||
@@ -74,4 +74,4 @@ const getLinkedRemote = (remote: string | unknown): string => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -271,22 +271,22 @@ const items = computed((): RegistrationItemProps[] => {
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<PageContainer class="max-w-800px">
|
||||
<PageContainer class="max-w-[800px]">
|
||||
<CardWrapper :increased-padding="true">
|
||||
<div class="flex flex-col gap-20px sm:gap-24px">
|
||||
<header class="flex flex-col gap-y-16px">
|
||||
<div class="flex flex-col gap-5 sm:gap-6">
|
||||
<header class="flex flex-col gap-y-4">
|
||||
<h3
|
||||
class="text-20px md:text-24px font-semibold leading-normal flex flex-row items-center gap-8px"
|
||||
class="text-xl md:text-2xl font-semibold leading-normal flex flex-row items-center gap-2"
|
||||
:class="serverErrors.length ? 'text-unraid-red' : 'text-green-500'"
|
||||
>
|
||||
<component :is="headingIcon" class="w-24px h-24px" />
|
||||
<component :is="headingIcon" class="w-6 h-6" />
|
||||
<span>
|
||||
{{ heading }}
|
||||
</span>
|
||||
</h3>
|
||||
<div
|
||||
v-if="subheading"
|
||||
class="prose text-16px leading-relaxed whitespace-normal opacity-75"
|
||||
class="prose text-base leading-relaxed whitespace-normal opacity-75"
|
||||
v-html="subheading"
|
||||
/>
|
||||
<span v-if="authAction" class="grow-0">
|
||||
@@ -325,7 +325,7 @@ const items = computed((): RegistrationItemProps[] => {
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="postcss">
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
|
||||
@@ -26,10 +26,10 @@ const evenBgColor = computed(() => {
|
||||
error && 'text-white bg-unraid-red',
|
||||
warning && 'text-black bg-yellow-100',
|
||||
]"
|
||||
class="text-16px p-12px grid grid-cols-1 gap-4px sm:px-20px sm:grid-cols-5 sm:gap-16px items-baseline rounded"
|
||||
class="text-base p-3 grid grid-cols-1 gap-1 sm:px-5 sm:grid-cols-5 sm:gap-4 items-baseline rounded"
|
||||
>
|
||||
<dt v-if="label" class="font-semibold leading-normal sm:col-span-2 flex flex-row sm:justify-end sm:text-right items-center gap-x-8px">
|
||||
<ShieldExclamationIcon v-if="error" class="w-16px h-16px fill-current" />
|
||||
<dt v-if="label" class="font-semibold leading-normal sm:col-span-2 flex flex-row sm:justify-end sm:text-right items-center gap-x-2">
|
||||
<ShieldExclamationIcon v-if="error" class="w-4 h-4 fill-current" />
|
||||
<span v-html="label" />
|
||||
</dt>
|
||||
<dd
|
||||
|
||||
@@ -19,7 +19,7 @@ defineProps<{
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-wrap items-center justify-between gap-8px">
|
||||
<div class="flex flex-wrap items-center justify-between gap-2">
|
||||
<BrandButton
|
||||
v-if="keyLinkedStatus !== 'linked' && keyLinkedStatus !== 'checking'"
|
||||
variant="none"
|
||||
@@ -41,7 +41,7 @@ defineProps<{
|
||||
{{ t(keyLinkedOutput.text ?? 'Unknown') }}
|
||||
</Badge>
|
||||
|
||||
<span class="inline-flex flex-wrap-items-start gap-8px">
|
||||
<span class="inline-flex flex-wrap-items-start gap-2">
|
||||
<BrandButton
|
||||
v-if="keyLinkedStatus === 'notLinked'"
|
||||
variant="underline"
|
||||
@@ -50,7 +50,7 @@ defineProps<{
|
||||
:icon-right="ArrowTopRightOnSquareIcon"
|
||||
:text="t('Link Key')"
|
||||
:title="t('Learn more and link your key to your account')"
|
||||
class="text-14px"
|
||||
class="text-sm"
|
||||
@click="accountStore.linkKey"
|
||||
/>
|
||||
<BrandButton
|
||||
@@ -59,7 +59,7 @@ defineProps<{
|
||||
:external="true"
|
||||
:icon-right="ArrowTopRightOnSquareIcon"
|
||||
:text="t('Learn More')"
|
||||
class="text-14px"
|
||||
class="text-sm"
|
||||
@click="accountStore.myKeys"
|
||||
/>
|
||||
</span>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user