diff --git a/Sources/App/Extensions/ViewController+respond.swift b/Sources/App/Extensions/ViewController+respond.swift index 6f0fa18..2787a84 100644 --- a/Sources/App/Extensions/ViewController+respond.swift +++ b/Sources/App/Extensions/ViewController+respond.swift @@ -13,10 +13,10 @@ extension ViewController { route: route, isHtmxRequest: request.isHtmxRequest, logger: request.logger, - // authenticate: { request.session.authenticate($0) }, - // currentUser: { - // try request.auth.require(User.self) - // } + authenticateUser: { request.session.authenticate($0) }, + currentUser: { + try request.auth.require(User.self) + } ) ) return AnyHTMLResponse(value: html) diff --git a/Sources/App/Middleware/ViewRoute+middleware.swift b/Sources/App/Middleware/ViewRoute+middleware.swift new file mode 100644 index 0000000..44ae75c --- /dev/null +++ b/Sources/App/Middleware/ViewRoute+middleware.swift @@ -0,0 +1,24 @@ +import DatabaseClient +import Fluent +import ManualDCore +import Vapor + +private let viewRouteMiddleware: [any Middleware] = [ + UserPasswordAuthenticator(), + UserSessionAuthenticator(), + User.redirectMiddleware(path: "/login"), +] + +extension SiteRoute.View { + var middleware: [any Middleware]? { + switch self { + case .project, + .frictionRate, + .effectiveLength, + .room: + return viewRouteMiddleware + case .login, .signup: + return nil + } + } +} diff --git a/Sources/App/configure.swift b/Sources/App/configure.swift index 71fc6ac..ebb6a8c 100644 --- a/Sources/App/configure.swift +++ b/Sources/App/configure.swift @@ -100,15 +100,14 @@ private func addCommands(to app: Application) { extension SiteRoute { fileprivate func middleware() -> [any Middleware]? { - return nil - // switch self { - // case .api(let route): - // return route.middleware - // // case .health: - // // return nil - // case .view(let route): - // return route.middleware - // } + switch self { + case .api: + return nil + case .health: + return nil + case .view(let route): + return route.middleware + } } } diff --git a/Sources/DatabaseClient/Interface.swift b/Sources/DatabaseClient/Interface.swift index 4c409d7..8ea8a43 100644 --- a/Sources/DatabaseClient/Interface.swift +++ b/Sources/DatabaseClient/Interface.swift @@ -18,6 +18,7 @@ public struct DatabaseClient: Sendable { public var equipment: Equipment public var componentLoss: ComponentLoss public var effectiveLength: EffectiveLengthClient + public var users: Users } extension DatabaseClient: TestDependencyKey { @@ -27,7 +28,8 @@ extension DatabaseClient: TestDependencyKey { rooms: .testValue, equipment: .testValue, componentLoss: .testValue, - effectiveLength: .testValue + effectiveLength: .testValue, + users: .testValue ) public static func live(database: any Database) -> Self { @@ -37,7 +39,8 @@ extension DatabaseClient: TestDependencyKey { rooms: .live(database: database), equipment: .live(database: database), componentLoss: .live(database: database), - effectiveLength: .live(database: database) + effectiveLength: .live(database: database), + users: .live(database: database) ) } } diff --git a/Sources/DatabaseClient/Projects.swift b/Sources/DatabaseClient/Projects.swift index a1b4c07..b2ee243 100644 --- a/Sources/DatabaseClient/Projects.swift +++ b/Sources/DatabaseClient/Projects.swift @@ -10,7 +10,7 @@ extension DatabaseClient { public var create: @Sendable (User.ID, Project.Create) async throws -> Project public var delete: @Sendable (Project.ID) async throws -> Void public var get: @Sendable (Project.ID) async throws -> Project? - public var fetch: @Sendable (User.ID) async throws -> [Project] + public var fetch: @Sendable (User.ID, PageRequest) async throws -> Page } } @@ -33,11 +33,12 @@ extension DatabaseClient.Projects: TestDependencyKey { get: { id in try await ProjectModel.find(id, on: database).map { try $0.toDTO() } }, - fetch: { userID in + fetch: { userID, request in try await ProjectModel.query(on: database) + .sort(\.$createdAt, .descending) .with(\.$user) .filter(\.$user.$id == userID) - .all() + .paginate(request) .map { try $0.toDTO() } } ) diff --git a/Sources/DatabaseClient/Users.swift b/Sources/DatabaseClient/Users.swift index e29c20d..4b95f4b 100644 --- a/Sources/DatabaseClient/Users.swift +++ b/Sources/DatabaseClient/Users.swift @@ -83,8 +83,8 @@ extension User { .field("username", .string, .required) .field("email", .string, .required) .field("password_hash", .string, .required) - .field("created_at", .datetime) - .field("updated_at", .datetime) + .field("createdAt", .datetime) + .field("updatedAt", .datetime) .unique(on: "email", "username") .create() } diff --git a/Sources/ManualDCore/Routes/ViewRoute.swift b/Sources/ManualDCore/Routes/ViewRoute.swift index b6e18e8..01c25ac 100644 --- a/Sources/ManualDCore/Routes/ViewRoute.swift +++ b/Sources/ManualDCore/Routes/ViewRoute.swift @@ -8,16 +8,20 @@ extension SiteRoute { /// The routes return html. public enum View: Equatable, Sendable { case login(LoginRoute) + case signup(SignupRoute) case project(ProjectRoute) case room(RoomRoute) case frictionRate(FrictionRateRoute) case effectiveLength(EffectiveLengthRoute) - case user(UserRoute) + // case user(UserRoute) public static let router = OneOf { Route(.case(Self.login)) { SiteRoute.View.LoginRoute.router } + Route(.case(Self.signup)) { + SiteRoute.View.SignupRoute.router + } Route(.case(Self.project)) { SiteRoute.View.ProjectRoute.router } @@ -30,9 +34,9 @@ extension SiteRoute { Route(.case(Self.effectiveLength)) { SiteRoute.View.EffectiveLengthRoute.router } - Route(.case(Self.user)) { - SiteRoute.View.UserRoute.router - } + // Route(.case(Self.user)) { + // SiteRoute.View.UserRoute.router + // } } } } @@ -42,6 +46,7 @@ extension SiteRoute.View { case create(Project.Create) case form(dismiss: Bool = false) case index + case page(page: Int = 1, limit: Int = 25) static let rootPath = "projects" @@ -74,6 +79,17 @@ extension SiteRoute.View { Path { rootPath } Method.get } + Route(.case(Self.page(page:limit:))) { + Path { + rootPath + "page" + } + Method.get + Query { + Field("page", default: 1) { Int.parser() } + Field("limit", default: 25) { Int.parser() } + } + } } } } @@ -182,17 +198,17 @@ extension SiteRoute.View.EffectiveLengthRoute { } } -extension SiteRoute.View { - public enum UserRoute: Equatable, Sendable { - case signup(Signup) - - public static let router = OneOf { - Route(.case(Self.signup)) { - SiteRoute.View.UserRoute.Signup.router - } - } - } -} +// extension SiteRoute.View { +// public enum UserRoute: Equatable, Sendable { +// case signup(Signup) +// +// public static let router = OneOf { +// Route(.case(Self.signup)) { +// SiteRoute.View.UserRoute.Signup.router +// } +// } +// } +// } extension SiteRoute.View { @@ -222,9 +238,9 @@ extension SiteRoute.View { } } -extension SiteRoute.View.UserRoute { +extension SiteRoute.View { - public enum Signup: Equatable, Sendable { + public enum SignupRoute: Equatable, Sendable { case index case submit(User.Create) diff --git a/Sources/Styleguide/Indicator.swift b/Sources/Styleguide/Indicator.swift new file mode 100644 index 0000000..ad2a3fb --- /dev/null +++ b/Sources/Styleguide/Indicator.swift @@ -0,0 +1,26 @@ +import Elementary + +public struct Indicator: HTML, Sendable { + + let size: Size + + public init(size: Size = .lg) { + self.size = size + } + + public var body: some HTML { + span(.class("loading loading-spinner \(size.class) htmx-indicator")) {} + } + + public enum Size: String, Equatable, Sendable { + case xs + case sm + case md + case lg + case xl + + var `class`: String { + "loading-\(rawValue)" + } + } +} diff --git a/Sources/ViewController/Interface.swift b/Sources/ViewController/Interface.swift index 7a48242..ae974d7 100644 --- a/Sources/ViewController/Interface.swift +++ b/Sources/ViewController/Interface.swift @@ -15,6 +15,10 @@ public typealias AnySendableHTML = (any HTML & Sendable) @DependencyClient public struct ViewController: Sendable { + + public typealias AuthenticateHandler = @Sendable (User) -> Void + public typealias CurrentUserHandler = @Sendable () throws -> User + public var view: @Sendable (Request) async throws -> AnySendableHTML } @@ -25,15 +29,25 @@ extension ViewController { public let route: SiteRoute.View public let isHtmxRequest: Bool public let logger: Logger + public let authenticateUser: AuthenticateHandler + public let currentUser: CurrentUserHandler public init( route: SiteRoute.View, isHtmxRequest: Bool, - logger: Logger + logger: Logger, + authenticateUser: @escaping AuthenticateHandler, + currentUser: @escaping CurrentUserHandler ) { self.route = route self.isHtmxRequest = isHtmxRequest self.logger = logger + self.authenticateUser = authenticateUser + self.currentUser = currentUser + } + + func authenticate(_ user: User) { + self.authenticateUser(user) } } } diff --git a/Sources/ViewController/Live.swift b/Sources/ViewController/Live.swift index c1cc1e4..2fb408b 100644 --- a/Sources/ViewController/Live.swift +++ b/Sources/ViewController/Live.swift @@ -1,11 +1,32 @@ +import DatabaseClient +import Dependencies import Elementary +import Foundation import ManualDCore extension ViewController.Request { func render() async throws -> AnySendableHTML { + + @Dependency(\.database) var database + switch route { case .login(let route): + switch route { + case .index: + return try await _render(isHtmxRequest: isHtmxRequest, showSidebar: false) { + LoginForm() + } + case .submit(let login): + let token = try await database.users.login(login) + let user = try await database.users.get(token.userID)! + authenticate(user) + let projects = try await database.projects.fetch(user.id, .init(page: 1, per: 25)) + return try await _render(isHtmxRequest: isHtmxRequest, showSidebar: false) { + ProjectsTable(userID: user.id, projects: projects) + } + } + case .signup(let route): return try await route.renderView(isHtmxRequest: isHtmxRequest) case .project(let route): return try await route.renderView(isHtmxRequest: isHtmxRequest) @@ -15,11 +36,11 @@ extension ViewController.Request { return try await route.renderView(isHtmxRequest: isHtmxRequest) case .effectiveLength(let route): return try await route.renderView(isHtmxRequest: isHtmxRequest) - case .user(let route): - return try await route.renderView(isHtmxRequest: isHtmxRequest) + // case .user(let route): + // return try await route.renderView(isHtmxRequest: isHtmxRequest) default: // FIX: FIX - return _render(isHtmxRequest: false) { + return try await _render(isHtmxRequest: false) { div { "Fix me!" } } } @@ -27,11 +48,29 @@ extension ViewController.Request { } extension SiteRoute.View.ProjectRoute { + + private var shouldShowSidebar: Bool { + switch self { + case .index, .page: return false + default: return true + } + } + func renderView(isHtmxRequest: Bool) async throws -> AnySendableHTML { - _render(isHtmxRequest: isHtmxRequest) { + @Dependency(\.database.projects) var projects + + return try await _render( + isHtmxRequest: isHtmxRequest, + showSidebar: shouldShowSidebar + ) { switch self { case .index: - ProjectView(project: .mock) + // ProjectView(project: .mock) + let page = try await projects.fetch(UUID(0), .init(page: 1, per: 25)) + ProjectsTable(userID: UUID(0), projects: page) + case .page(let page, let limit): + let page = try await projects.fetch(UUID(0), .init(page: page, per: limit)) + ProjectsTable.Rows(projects: page) case .form(let dismiss): ProjectForm(dismiss: dismiss) case .create: @@ -47,7 +86,7 @@ extension SiteRoute.View.RoomRoute { case .form(let dismiss): return RoomForm(dismiss: dismiss) case .index: - return _render(isHtmxRequest: isHtmxRequest, active: .rooms) { + return try await _render(isHtmxRequest: isHtmxRequest, active: .rooms) { RoomsView(rooms: Room.mocks) } } @@ -58,7 +97,7 @@ extension SiteRoute.View.FrictionRateRoute { func renderView(isHtmxRequest: Bool) async throws -> AnySendableHTML { switch self { case .index: - return _render(isHtmxRequest: isHtmxRequest, active: .frictionRate) { + return try await _render(isHtmxRequest: isHtmxRequest, active: .frictionRate) { FrictionRateView() } case .form(let type, let dismiss): @@ -89,7 +128,7 @@ extension SiteRoute.View.EffectiveLengthRoute { func renderView(isHtmxRequest: Bool) async throws -> AnySendableHTML { switch self { case .index: - return _render(isHtmxRequest: isHtmxRequest, active: .effectiveLength) { + return try await _render(isHtmxRequest: isHtmxRequest, active: .effectiveLength) { EffectiveLengthsView(effectiveLengths: EffectiveLength.mocks) } case .form(let dismiss): @@ -106,51 +145,66 @@ extension SiteRoute.View.EffectiveLengthRoute { } } -extension SiteRoute.View.UserRoute { +extension SiteRoute.View.SignupRoute { func renderView(isHtmxRequest: Bool) async throws -> AnySendableHTML { + @Dependency(\.database.users) var users + switch self { - // case .login(.index): - // return _render(isHtmxRequest: isHtmxRequest, showSidebar: false) { - // LoginForm() - // } - case .signup(.index): - return _render(isHtmxRequest: isHtmxRequest, showSidebar: false) { + case .index: + return try await _render(isHtmxRequest: isHtmxRequest, showSidebar: false) { LoginForm(style: .signup) } - default: - return div { "Fix Me!" } + case .submit(let request): + _ = try await users.create(request) + // FIX: We should just login the new user at this point. + return try await _render(isHtmxRequest: isHtmxRequest, showSidebar: false) { + LoginForm() + } + + // default: + // return div { "Fix Me!" } } } } -extension SiteRoute.View.LoginRoute { - func renderView(isHtmxRequest: Bool) async throws -> AnySendableHTML { - _render(isHtmxRequest: isHtmxRequest, showSidebar: false) { - switch self { - case .index: - LoginForm() - case .submit: - // FIX: - div { "Fix me!" } - } - } - } -} +// extension SiteRoute.View.LoginRoute { +// func renderView(on req: ViewController.Request) async throws -> AnySendableHTML { +// +// @Dependency(\.database) var database +// +// return try await _render(isHtmxRequest: req.isHtmxRequest, showSidebar: false) { +// switch self { +// case .index: +// LoginForm() +// case .submit(let login): +// // FIX: +// // div { "Logged in Success! Fix me!" } +// let token = try await database.users.login(login) +// let user = try await database.users.get(token.userID)! +// _ = req.authenticate(user) +// // req.authenticate(user) +// let page = try await database.projects.fetch(user.id, .init(page: 1, per: 25)) +// ProjectsTable(userID: user.id, projects: page) +// } +// } +// } +// } private func _render( isHtmxRequest: Bool, active activeTab: Sidebar.ActiveTab = .projects, showSidebar: Bool = true, - @HTMLBuilder inner: () -> C -) -> AnySendableHTML where C: Sendable { + @HTMLBuilder inner: () async throws -> C +) async throws -> AnySendableHTML where C: Sendable { + let inner = try await inner() if isHtmxRequest { - return inner() + return inner } return MainPage( active: activeTab, showSidebar: showSidebar ) { - inner() + inner } } diff --git a/Sources/ViewController/Views/Project/ProjectsTable.swift b/Sources/ViewController/Views/Project/ProjectsTable.swift new file mode 100644 index 0000000..2f7c7c5 --- /dev/null +++ b/Sources/ViewController/Views/Project/ProjectsTable.swift @@ -0,0 +1,80 @@ +import Elementary +import ElementaryHTMX +import Fluent +import ManualDCore +import Styleguide +import Vapor + +struct ProjectsTable: HTML, Sendable { + + let userID: User.ID + let projects: Page + + init(userID: User.ID, projects: Page) { + self.userID = userID + self.projects = projects + } + + var body: some HTML { + div { + Row { + h1(.class("text-2xl font-bold")) { "Projects" } + div( + .class("tooltip tooltip-left"), + .data("tip", value: "Add project") + ) { + button( + .class("btn btn-primary w-[40px] text-2xl") + ) { + "+" + } + } + } + .attributes(.class("pb-6")) + + div(.class("overflow-x-auto rounded-box border")) { + table(.class("table table-zebra")) { + thead { + tr { + th { Label("Date") } + th { Label("Name") } + th { Label("Address") } + } + } + tbody { + Rows(projects: projects) + } + } + } + } + } +} + +extension ProjectsTable { + struct Rows: HTML, Sendable { + let projects: Page + + var body: some HTML { + for project in projects.items { + tr(.id("\(project.id)")) { + td { "\(project.createdAt)" } + td { "\(project.name)" } + td { "\(project.streetAddress)" } + } + } + // Have a row that when revealed fetches the next page, + // if there are more pages left. + if projects.metadata.pageCount > projects.metadata.page { + tr( + .hx.get(route: .project(.page(page: projects.metadata.page + 1, limit: 25))), + .hx.trigger(.event(.revealed)), + .hx.swap(.outerHTML), + .hx.target("this"), + .hx.indicator("next .htmx-indicator") + ) { + Indicator(size: .lg) + } + } + } + } +} diff --git a/Sources/ViewController/Views/User/LoginForm.swift b/Sources/ViewController/Views/User/LoginForm.swift index 2fb6cbd..51054fb 100644 --- a/Sources/ViewController/Views/User/LoginForm.swift +++ b/Sources/ViewController/Views/User/LoginForm.swift @@ -15,7 +15,9 @@ struct LoginForm: HTML, Sendable { .id("loginForm"), .class("flex items-center justify-center") ) { - form { + form( + .method(.post) + ) { fieldset(.class("fieldset bg-base-200 border-base-300 rounded-box w-xl border p-4")) { legend(.class("fieldset-legend")) { style.title } @@ -24,6 +26,7 @@ struct LoginForm: HTML, Sendable { SVG(.user) input( .type(.text), .required, .placeholder("Username"), + .name("username"), .id("username"), .minlength("3"), .pattern(.username) ) } @@ -37,7 +40,8 @@ struct LoginForm: HTML, Sendable { label(.class("input validator w-full")) { SVG(.email) input( - .type(.email), .placeholder("Email"), .required + .type(.email), .placeholder("Email"), .required, + .name("email"), .id("email"), ) } div(.class("validator-hint hidden")) { "Enter valid email address." } @@ -46,7 +50,8 @@ struct LoginForm: HTML, Sendable { SVG(.key) input( .type(.password), .placeholder("Password"), .required, - .pattern(.password), .minlength("8") + .pattern(.password), .minlength("8"), + .name("password"), .id("password"), ) } @@ -55,7 +60,8 @@ struct LoginForm: HTML, Sendable { SVG(.key) input( .type(.password), .placeholder("Confirm Password"), .required, - .pattern(.password), .minlength("8") + .pattern(.password), .minlength("8"), + .name("confirmPassword"), .id("confirmPassword"), ) } } @@ -75,7 +81,7 @@ struct LoginForm: HTML, Sendable { button(.class("btn btn-secondary mt-4")) { style.title } a( .class("btn btn-link mt-4"), - .href(route: style == .signup ? .login(.index) : .user(.signup(.index))) + .href(route: style == .signup ? .login(.index) : .signup(.index)) ) { style == .login ? "Sign Up" : "Login" } diff --git a/justfile b/justfile index 222beeb..9aa3ba3 100644 --- a/justfile +++ b/justfile @@ -7,7 +7,7 @@ run-css: @./tailwindcss -i input.css -o output.css --watch run: - @swift run App + @SWIFT_BACTRACE=enable=no swift run App build-docker: @podman build -f docker/Dockerfile.dev -t {{docker_image}}:dev . diff --git a/output.css b/output.css index 9f8e300..ca7347c 100644 --- a/output.css +++ b/output.css @@ -278,272 +278,6 @@ } } } - .menu { - @layer daisyui.l1.l2.l3 { - display: flex; - width: fit-content; - flex-direction: column; - flex-wrap: wrap; - padding: calc(0.25rem * 2); - --menu-active-fg: var(--color-neutral-content); - --menu-active-bg: var(--color-neutral); - font-size: 0.875rem; - :where(li ul) { - position: relative; - margin-inline-start: calc(0.25rem * 4); - padding-inline-start: calc(0.25rem * 2); - white-space: nowrap; - &:before { - position: absolute; - inset-inline-start: calc(0.25rem * 0); - top: calc(0.25rem * 3); - bottom: calc(0.25rem * 3); - background-color: var(--color-base-content); - opacity: 10%; - width: var(--border); - content: ""; - } - } - :where(li > .menu-dropdown:not(.menu-dropdown-show)) { - display: none; - } - :where(li:not(.menu-title) > *:not(ul, details, .menu-title, .btn)), :where(li:not(.menu-title) > details > summary:not(.menu-title)) { - display: grid; - grid-auto-flow: column; - align-content: flex-start; - align-items: center; - gap: calc(0.25rem * 2); - border-radius: var(--radius-field); - padding-inline: calc(0.25rem * 3); - padding-block: calc(0.25rem * 1.5); - text-align: start; - transition-property: color, background-color, box-shadow; - transition-duration: 0.2s; - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); - grid-auto-columns: minmax(auto, max-content) auto max-content; - text-wrap: balance; - user-select: none; - } - :where(li > details > summary) { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - &::-webkit-details-marker { - display: none; - } - } - :where(li > details > summary), :where(li > .menu-dropdown-toggle) { - &:after { - justify-self: flex-end; - display: block; - height: 0.375rem; - width: 0.375rem; - rotate: -135deg; - translate: 0 -1px; - transition-property: rotate, translate; - transition-duration: 0.2s; - content: ""; - transform-origin: 50% 50%; - box-shadow: 2px 2px inset; - pointer-events: none; - } - } - details { - overflow: hidden; - interpolate-size: allow-keywords; - } - details::details-content { - block-size: 0; - @media (prefers-reduced-motion: no-preference) { - transition-behavior: allow-discrete; - transition-property: block-size, content-visibility; - transition-duration: 0.2s; - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); - } - } - details[open]::details-content { - block-size: auto; - } - :where(li > details[open] > summary):after, :where(li > .menu-dropdown-toggle.menu-dropdown-show):after { - rotate: 45deg; - translate: 0 1px; - } - :where( li:not(.menu-title, .disabled) > *:not(ul, details, .menu-title), li:not(.menu-title, .disabled) > details > summary:not(.menu-title) ):not(.menu-active, :active, .btn) { - &.menu-focus, &:focus-visible { - cursor: pointer; - background-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-base-content) 10%, transparent); - } - color: var(--color-base-content); - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - } - :where( li:not(.menu-title, .disabled) > *:not(ul, details, .menu-title):not(.menu-active, :active, .btn):hover, li:not(.menu-title, .disabled) > details > summary:not(.menu-title):not(.menu-active, :active, .btn):hover ) { - cursor: pointer; - background-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-base-content) 10%, transparent); - } - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - box-shadow: 0 1px oklch(0% 0 0 / 0.01) inset, 0 -1px oklch(100% 0 0 / 0.01) inset; - } - :where(li:empty) { - background-color: var(--color-base-content); - opacity: 10%; - margin: 0.5rem 1rem; - height: 1px; - } - :where(li) { - position: relative; - display: flex; - flex-shrink: 0; - flex-direction: column; - flex-wrap: wrap; - align-items: stretch; - .badge { - justify-self: flex-end; - } - & > *:not(ul, .menu-title, details, .btn):active, & > *:not(ul, .menu-title, details, .btn).menu-active, & > details > summary:active { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - color: var(--menu-active-fg); - background-color: var(--menu-active-bg); - background-size: auto, calc(var(--noise) * 100%); - background-image: none, var(--fx-noise); - &:not(&:active) { - box-shadow: 0 2px calc(var(--depth) * 3px) -2px var(--menu-active-bg); - } - } - &.menu-disabled { - pointer-events: none; - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 20%, transparent); - } - } - } - .dropdown:focus-within { - .menu-dropdown-toggle:after { - rotate: 45deg; - translate: 0 1px; - } - } - .dropdown-content { - margin-top: calc(0.25rem * 2); - padding: calc(0.25rem * 2); - &:before { - display: none; - } - } - } - } - .dropdown { - @layer daisyui.l1.l2.l3 { - position: relative; - display: inline-block; - position-area: var(--anchor-v, bottom) var(--anchor-h, span-right); - & > *:not(:has(~ [class*="dropdown-content"])):focus { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - .dropdown-content { - position: absolute; - } - &.dropdown-close .dropdown-content, &:not(details, .dropdown-open, .dropdown-hover:hover, :focus-within) .dropdown-content, &.dropdown-hover:not(:hover) [tabindex]:first-child:focus:not(:focus-visible) ~ .dropdown-content { - display: none; - transform-origin: top; - opacity: 0%; - scale: 95%; - } - &[popover], .dropdown-content { - z-index: 999; - @media (prefers-reduced-motion: no-preference) { - animation: dropdown 0.2s; - transition-property: opacity, scale, display; - transition-behavior: allow-discrete; - transition-duration: 0.2s; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - } - } - @starting-style { - &[popover], .dropdown-content { - scale: 95%; - opacity: 0; - } - } - &:not(.dropdown-close) { - &.dropdown-open, &:not(.dropdown-hover):focus, &:focus-within { - > [tabindex]:first-child { - pointer-events: none; - } - .dropdown-content { - opacity: 100%; - scale: 100%; - } - } - &.dropdown-hover:hover { - .dropdown-content { - opacity: 100%; - scale: 100%; - } - } - } - &:is(details) { - summary { - &::-webkit-details-marker { - display: none; - } - } - } - &:where([popover]) { - background: #0000; - } - &[popover] { - position: fixed; - color: inherit; - @supports not (position-area: bottom) { - margin: auto; - &.dropdown-close, &.dropdown-open:not(:popover-open) { - display: none; - transform-origin: top; - opacity: 0%; - scale: 95%; - } - &::backdrop { - background-color: color-mix(in oklab, #000 30%, #0000); - } - } - &.dropdown-close, &:not(.dropdown-open, :popover-open) { - display: none; - transform-origin: top; - opacity: 0%; - scale: 95%; - } - } - } - } .btn { :where(&) { @layer daisyui.l1.l2.l3 { @@ -676,22 +410,18 @@ } } } - .btn-disabled { - @layer daisyui.l1.l2 { - &:not(.btn-link, .btn-ghost) { - background-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-base-content) 10%, transparent); - } - box-shadow: none; - } + .loading { + @layer daisyui.l1.l2.l3 { pointer-events: none; - --btn-border: #0000; - --btn-noise: none; - --btn-fg: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --btn-fg: color-mix(in oklch, var(--color-base-content) 20%, #0000); - } + display: inline-block; + aspect-ratio: 1 / 1; + background-color: currentcolor; + vertical-align: middle; + width: calc(var(--size-selector, 0.25rem) * 6); + mask-size: 100%; + mask-repeat: no-repeat; + mask-position: center; + mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E"); } } .validator-hint { @@ -724,27 +454,6 @@ } } } - .toast { - @layer daisyui.l1.l2.l3 { - position: fixed; - inset-inline-start: auto; - inset-inline-end: calc(0.25rem * 4); - top: auto; - bottom: calc(0.25rem * 4); - display: flex; - flex-direction: column; - gap: calc(0.25rem * 2); - background-color: transparent; - translate: var(--toast-x, 0) var(--toast-y, 0); - width: max-content; - max-width: calc(100vw - 2rem); - & > * { - @media (prefers-reduced-motion: no-preference) { - animation: toast 0.25s ease-out; - } - } - } - } .toggle { @layer daisyui.l1.l2.l3 { border: var(--border) solid currentColor; @@ -1071,443 +780,6 @@ } } } - .select { - @layer daisyui.l1.l2.l3 { - border: var(--border) solid #0000; - position: relative; - display: inline-flex; - flex-shrink: 1; - appearance: none; - align-items: center; - gap: calc(0.25rem * 1.5); - background-color: var(--color-base-100); - padding-inline-start: calc(0.25rem * 3); - padding-inline-end: calc(0.25rem * 7); - vertical-align: middle; - width: clamp(3rem, 20rem, 100%); - height: var(--size); - font-size: 0.875rem; - touch-action: manipulation; - border-start-start-radius: var(--join-ss, var(--radius-field)); - border-start-end-radius: var(--join-se, var(--radius-field)); - border-end-start-radius: var(--join-es, var(--radius-field)); - border-end-end-radius: var(--join-ee, var(--radius-field)); - background-image: linear-gradient(45deg, #0000 50%, currentColor 50%), linear-gradient(135deg, currentColor 50%, #0000 50%); - background-position: calc(100% - 20px) calc(1px + 50%), calc(100% - 16.1px) calc(1px + 50%); - background-size: 4px 4px, 4px 4px; - background-repeat: no-repeat; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - box-shadow: 0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - } - border-color: var(--input-color); - --input-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --input-color: color-mix(in oklab, var(--color-base-content) 20%, #0000); - } - --size: calc(var(--size-field, 0.25rem) * 10); - [dir="rtl"] & { - background-position: calc(0% + 12px) calc(1px + 50%), calc(0% + 16px) calc(1px + 50%); - &::picker(select), select::picker(select) { - translate: 0.5rem 0; - } - } - &[multiple] { - height: auto; - overflow: auto; - padding-block: calc(0.25rem * 3); - padding-inline-end: calc(0.25rem * 3); - background-image: none; - } - select { - margin-inline-start: calc(0.25rem * -3); - margin-inline-end: calc(0.25rem * -7); - width: calc(100% + 2.75rem); - appearance: none; - padding-inline-start: calc(0.25rem * 3); - padding-inline-end: calc(0.25rem * 7); - height: calc(100% - calc(var(--border) * 2)); - align-items: center; - background: inherit; - border-radius: inherit; - border-style: none; - &:focus, &:focus-within { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - &:not(:last-child) { - margin-inline-end: calc(0.25rem * -5.5); - background-image: none; - } - } - &:focus, &:focus-within { - --input-color: var(--color-base-content); - box-shadow: 0 1px var(--input-color); - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000); - } - outline: 2px solid var(--input-color); - outline-offset: 2px; - isolation: isolate; - } - &:has(> select[disabled]), &:is(:disabled, [disabled]), fieldset:disabled & { - cursor: not-allowed; - border-color: var(--color-base-200); - background-color: var(--color-base-200); - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 40%, transparent); - } - &::placeholder { - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 20%, transparent); - } - } - } - &:has(> select[disabled]) > select[disabled] { - cursor: not-allowed; - } - &, & select { - @supports (appearance: base-select) { - appearance: base-select; - } - @supports (appearance: base-select) { - &::picker(select) { - appearance: base-select; - } - } - &::picker(select) { - color: inherit; - max-height: min(24rem, 70dvh); - margin-inline: 0.5rem; - translate: -0.5rem 0; - border: var(--border) solid var(--color-base-200); - margin-block: calc(0.25rem * 2); - border-radius: var(--radius-box); - padding: calc(0.25rem * 2); - background-color: inherit; - box-shadow: 0 2px calc(var(--depth) * 3px) -2px oklch(0% 0 0/0.2); - box-shadow: 0 20px 25px -5px rgb(0 0 0 / calc(var(--depth) * 0.1)), 0 8px 10px -6px rgb(0 0 0 / calc(var(--depth) * 0.1)); - } - &::picker-icon { - display: none; - } - optgroup { - padding-top: 0.5em; - option { - &:nth-child(1) { - margin-top: 0.5em; - } - } - } - option { - border-radius: var(--radius-field); - padding-inline: calc(0.25rem * 3); - padding-block: calc(0.25rem * 1.5); - transition-property: color, background-color; - transition-duration: 0.2s; - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); - white-space: normal; - &:not(:disabled) { - &:hover, &:focus-visible { - cursor: pointer; - background-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-base-content) 10%, transparent); - } - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - &:active { - background-color: var(--color-neutral); - color: var(--color-neutral-content); - box-shadow: 0 2px calc(var(--depth) * 3px) -2px var(--color-neutral); - } - } - } - } - } - } - .checkbox { - @layer daisyui.l1.l2.l3 { - border: var(--border) solid var(--input-color, var(--color-base-content)); - @supports (color: color-mix(in lab, red, red)) { - border: var(--border) solid var(--input-color, color-mix(in oklab, var(--color-base-content) 20%, #0000)); - } - position: relative; - display: inline-block; - flex-shrink: 0; - cursor: pointer; - appearance: none; - border-radius: var(--radius-selector); - padding: calc(0.25rem * 1); - vertical-align: middle; - color: var(--color-base-content); - box-shadow: 0 1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 0 #0000 inset, 0 0 #0000; - transition: background-color 0.2s, box-shadow 0.2s; - --size: calc(var(--size-selector, 0.25rem) * 6); - width: var(--size); - height: var(--size); - background-size: auto, calc(var(--noise) * 100%); - background-image: none, var(--fx-noise); - &:before { - --tw-content: ""; - content: var(--tw-content); - display: block; - width: 100%; - height: 100%; - rotate: 45deg; - background-color: currentcolor; - opacity: 0%; - transition: clip-path 0.3s, opacity 0.1s, rotate 0.3s, translate 0.3s; - transition-delay: 0.1s; - clip-path: polygon(20% 100%, 20% 80%, 50% 80%, 50% 80%, 70% 80%, 70% 100%); - box-shadow: 0px 3px 0 0px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - font-size: 1rem; - line-height: 0.75; - } - &:focus-visible { - outline: 2px solid var(--input-color, currentColor); - outline-offset: 2px; - } - &:checked, &[aria-checked="true"] { - background-color: var(--input-color, #0000); - box-shadow: 0 0 #0000 inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * 0.1)); - &:before { - clip-path: polygon(20% 100%, 20% 80%, 50% 80%, 50% 0%, 70% 0%, 70% 100%); - opacity: 100%; - } - @media (forced-colors: active) { - &:before { - rotate: 0deg; - background-color: transparent; - --tw-content: "✔︎"; - clip-path: none; - } - } - @media print { - &:before { - rotate: 0deg; - background-color: transparent; - --tw-content: "✔︎"; - clip-path: none; - } - } - } - &:indeterminate { - background-color: var( --input-color, var(--color-base-content) ); - @supports (color: color-mix(in lab, red, red)) { - background-color: var( --input-color, color-mix(in oklab, var(--color-base-content) 20%, #0000) ); - } - &:before { - rotate: 0deg; - opacity: 100%; - translate: 0 -35%; - clip-path: polygon(20% 100%, 20% 80%, 50% 80%, 50% 80%, 80% 80%, 80% 100%); - } - } - } - &:disabled { - @layer daisyui.l1.l2 { - cursor: not-allowed; - opacity: 20%; - } - } - } - .radio { - @layer daisyui.l1.l2.l3 { - position: relative; - display: inline-block; - flex-shrink: 0; - cursor: pointer; - appearance: none; - border-radius: calc(infinity * 1px); - padding: calc(0.25rem * 1); - vertical-align: middle; - border: var(--border) solid var(--input-color, currentColor); - @supports (color: color-mix(in lab, red, red)) { - border: var(--border) solid var(--input-color, color-mix(in srgb, currentColor 20%, #0000)); - } - box-shadow: 0 1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset; - --size: calc(var(--size-selector, 0.25rem) * 6); - width: var(--size); - height: var(--size); - color: var(--input-color, currentColor); - &:before { - display: block; - width: 100%; - height: 100%; - border-radius: calc(infinity * 1px); - --tw-content: ""; - content: var(--tw-content); - background-size: auto, calc(var(--noise) * 100%); - background-image: none, var(--fx-noise); - } - &:focus-visible { - outline: 2px solid currentColor; - } - &:checked, &[aria-checked="true"] { - border-color: currentcolor; - background-color: var(--color-base-100); - @media (prefers-reduced-motion: no-preference) { - animation: radio 0.2s ease-out; - } - &:before { - background-color: currentcolor; - box-shadow: 0 -1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * 0.1)); - } - @media (forced-colors: active) { - &:before { - outline-style: var(--tw-outline-style); - outline-width: 1px; - outline-offset: calc(1px * -1); - } - } - @media print { - &:before { - outline: 0.25rem solid; - outline-offset: -1rem; - } - } - } - } - &:disabled { - @layer daisyui.l1.l2 { - cursor: not-allowed; - opacity: 20%; - } - } - } - .rating { - @layer daisyui.l1.l2.l3 { - position: relative; - display: inline-flex; - vertical-align: middle; - & input { - border: none; - appearance: none; - } - :where(*) { - height: calc(0.25rem * 6); - width: calc(0.25rem * 6); - border-radius: 0; - background-color: var(--color-base-content); - opacity: 20%; - @media (prefers-reduced-motion: no-preference) { - animation: rating 0.25s ease-out; - } - &:is(input) { - cursor: pointer; - } - } - & .rating-hidden { - width: calc(0.25rem * 2); - background-color: transparent; - } - input[type="radio"]:checked { - background-image: none; - } - * { - &:checked, &[aria-checked="true"], &[aria-current="true"], &:has(~ *:checked, ~ *[aria-checked="true"], ~ *[aria-current="true"]) { - opacity: 100%; - } - &:focus-visible { - scale: 1.1; - @media (prefers-reduced-motion: no-preference) { - transition: scale 0.2s ease-out; - } - } - } - & *:active:focus { - animation: none; - scale: 1.1; - } - } - @layer daisyui.l1.l2 { - &.rating-xs :where(*:not(.rating-hidden)) { - width: calc(0.25rem * 4); - height: calc(0.25rem * 4); - } - &.rating-sm :where(*:not(.rating-hidden)) { - width: calc(0.25rem * 5); - height: calc(0.25rem * 5); - } - &.rating-md :where(*:not(.rating-hidden)) { - width: calc(0.25rem * 6); - height: calc(0.25rem * 6); - } - &.rating-lg :where(*:not(.rating-hidden)) { - width: calc(0.25rem * 7); - height: calc(0.25rem * 7); - } - &.rating-xl :where(*:not(.rating-hidden)) { - width: calc(0.25rem * 8); - height: calc(0.25rem * 8); - } - } - } - .progress { - @layer daisyui.l1.l2.l3 { - position: relative; - height: calc(0.25rem * 2); - width: 100%; - appearance: none; - overflow: hidden; - border-radius: var(--radius-box); - background-color: currentcolor; - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, currentcolor 20%, transparent); - } - color: var(--color-base-content); - &:indeterminate { - background-image: repeating-linear-gradient( 90deg, currentColor -1%, currentColor 10%, #0000 10%, #0000 90% ); - background-size: 200%; - background-position-x: 15%; - @media (prefers-reduced-motion: no-preference) { - animation: progress 5s ease-in-out infinite; - } - @supports (-moz-appearance: none) { - &::-moz-progress-bar { - background-color: transparent; - @media (prefers-reduced-motion: no-preference) { - animation: progress 5s ease-in-out infinite; - background-image: repeating-linear-gradient( 90deg, currentColor -1%, currentColor 10%, #0000 10%, #0000 90% ); - background-size: 200%; - background-position-x: 15%; - } - } - } - } - @supports (-moz-appearance: none) { - &::-moz-progress-bar { - border-radius: var(--radius-box); - background-color: currentcolor; - } - } - @supports (-webkit-appearance: none) { - &::-webkit-progress-bar { - border-radius: var(--radius-box); - background-color: transparent; - } - &::-webkit-progress-value { - border-radius: var(--radius-box); - background-color: currentColor; - } - } - } - } .fixed { position: fixed; } @@ -1538,81 +810,6 @@ .left-\[25vw\] { left: 25vw; } - .textarea { - @layer daisyui.l1.l2.l3 { - border: var(--border) solid #0000; - min-height: calc(0.25rem * 20); - flex-shrink: 1; - appearance: none; - border-radius: var(--radius-field); - background-color: var(--color-base-100); - padding-block: calc(0.25rem * 2); - vertical-align: middle; - width: clamp(3rem, 20rem, 100%); - padding-inline-start: 0.75rem; - padding-inline-end: 0.75rem; - font-size: max(var(--font-size, 0.875rem), 0.875rem); - touch-action: manipulation; - border-color: var(--input-color); - box-shadow: 0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - } - --input-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --input-color: color-mix(in oklab, var(--color-base-content) 20%, #0000); - } - textarea { - appearance: none; - background-color: transparent; - border: none; - &:focus, &:focus-within { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - } - &:focus, &:focus-within { - --input-color: var(--color-base-content); - box-shadow: 0 1px var(--input-color); - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000); - } - outline: 2px solid var(--input-color); - outline-offset: 2px; - isolation: isolate; - } - @media (pointer: coarse) { - @supports (-webkit-touch-callout: none) { - &:focus, &:focus-within { - --font-size: 1rem; - } - } - } - &:has(> textarea[disabled]), &:is(:disabled, [disabled]) { - cursor: not-allowed; - border-color: var(--color-base-200); - background-color: var(--color-base-200); - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 40%, transparent); - } - &::placeholder { - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 20%, transparent); - } - } - box-shadow: none; - } - &:has(> textarea[disabled]) > textarea[disabled] { - cursor: not-allowed; - } - } - } .z-50 { z-index: 50; } @@ -1796,71 +993,12 @@ grid-auto-rows: max-content; } } - .prose { - :root & { - --tw-prose-body: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --tw-prose-body: color-mix(in oklab, var(--color-base-content) 80%, #0000); - } - --tw-prose-headings: var(--color-base-content); - --tw-prose-lead: var(--color-base-content); - --tw-prose-links: var(--color-base-content); - --tw-prose-bold: var(--color-base-content); - --tw-prose-counters: var(--color-base-content); - --tw-prose-bullets: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --tw-prose-bullets: color-mix(in oklab, var(--color-base-content) 50%, #0000); - } - --tw-prose-hr: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --tw-prose-hr: color-mix(in oklab, var(--color-base-content) 20%, #0000); - } - --tw-prose-quotes: var(--color-base-content); - --tw-prose-quote-borders: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --tw-prose-quote-borders: color-mix(in oklab, var(--color-base-content) 20%, #0000); - } - --tw-prose-captions: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --tw-prose-captions: color-mix(in oklab, var(--color-base-content) 50%, #0000); - } - --tw-prose-code: var(--color-base-content); - --tw-prose-pre-code: var(--color-neutral-content); - --tw-prose-pre-bg: var(--color-neutral); - --tw-prose-th-borders: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --tw-prose-th-borders: color-mix(in oklab, var(--color-base-content) 50%, #0000); - } - --tw-prose-td-borders: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --tw-prose-td-borders: color-mix(in oklab, var(--color-base-content) 20%, #0000); - } - --tw-prose-kbd: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --tw-prose-kbd: color-mix(in oklab, var(--color-base-content) 80%, #0000); - } - :where(code):not(pre > code) { - background-color: var(--color-base-200); - border-radius: var(--radius-selector); - border: var(--border) solid var(--color-base-300); - padding-inline: 0.5em; - padding-block: 0.2em; - font-weight: inherit; - &:before, &:after { - display: none; - } - } - } - } .contents { display: contents; } .flex { display: flex; } - .grid { - display: grid; - } .hidden { display: none; } @@ -1873,9 +1011,6 @@ .h-screen { height: 100vh; } - .w-1 { - width: calc(var(--spacing) * 1); - } .w-1\/2 { width: calc(1/2 * 100%); } @@ -1894,64 +1029,12 @@ .flex-none { flex: none; } - .flex-shrink { - flex-shrink: 1; - } - .border-collapse { - border-collapse: collapse; - } - .transform { - transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,); - } - .skeleton { - @layer daisyui.l1.l2.l3 { - border-radius: var(--radius-box); - background-color: var(--color-base-300); - @media (prefers-reduced-motion: reduce) { - transition-duration: 15s; - } - will-change: background-position; - background-image: linear-gradient( 105deg, #0000 0% 40%, var(--color-base-100) 50%, #0000 60% 100% ); - background-size: 200% auto; - background-position-x: -50%; - @media (prefers-reduced-motion: no-preference) { - animation: skeleton 1.8s ease-in-out infinite; - } - } - } - .link { - @layer daisyui.l1.l2.l3 { - cursor: pointer; - text-decoration-line: underline; - &:focus { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - &:focus-visible { - outline: 2px solid currentColor; - outline-offset: 2px; - } - } - } - .resize { - resize: both; - } - .grid-cols-3 { - grid-template-columns: repeat(3, minmax(0, 1fr)); - } .flex-col { flex-direction: column; } .flex-row { flex-direction: row; } - .flex-wrap { - flex-wrap: wrap; - } .items-center { align-items: center; } @@ -2078,8 +1161,10 @@ .bg-white { background-color: var(--color-white); } - .mask-repeat { - mask-repeat: repeat; + .loading-spinner { + @layer daisyui.l1.l2 { + mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E"); + } } .p-4 { padding: calc(var(--spacing) * 4); @@ -2093,9 +1178,6 @@ .px-6 { padding-inline: calc(var(--spacing) * 6); } - .py-1 { - padding-block: calc(var(--spacing) * 1); - } .py-1\.5 { padding-block: calc(var(--spacing) * 1.5); } @@ -2146,22 +1228,6 @@ --tw-font-weight: var(--font-weight-bold); font-weight: var(--font-weight-bold); } - .text-wrap { - text-wrap: wrap; - } - .link-primary { - @layer daisyui.l1.l2 { - color: var(--color-primary); - @media (hover: hover) { - &:hover { - color: var(--color-primary); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-primary) 80%, #000); - } - } - } - } - } .text-error { color: var(--color-error); } @@ -2209,14 +1275,6 @@ } } } - .prose { - & :where(.btn-link):not(:where([class~="not-prose"], [class~="not-prose"] *)) { - text-decoration-line: none; - } - } - .underline { - text-decoration-line: underline; - } .opacity-50 { opacity: 50%; } @@ -2224,10 +1282,6 @@ --tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 4px 6px -4px var(--tw-shadow-color, rgb(0 0 0 / 0.1)); box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); } - .outline { - outline-style: var(--tw-outline-style); - outline-width: 1px; - } .outline-1 { outline-style: var(--tw-outline-style); outline-width: 1px; @@ -2241,83 +1295,6 @@ .filter { filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,); } - .transition { - transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, content-visibility, overlay, pointer-events; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - } - .ease-in-out { - --tw-ease: var(--ease-in-out); - transition-timing-function: var(--ease-in-out); - } - .btn-outline { - @layer daisyui.l1 { - &:not( .btn-active, :hover, :active:focus, :focus-visible, input:checked:not(.filter .btn), :disabled, [disabled], .btn-disabled ) { - --btn-shadow: ""; - --btn-bg: #0000; - --btn-fg: var(--btn-color); - --btn-border: var(--btn-color); - --btn-noise: none; - } - @media (hover: none) { - &:not(.btn-active, :active, :focus-visible, input:checked:not(.filter .btn)):hover { - --btn-shadow: ""; - --btn-bg: #0000; - --btn-fg: var(--btn-color); - --btn-border: var(--btn-color); - --btn-noise: none; - } - } - } - } - .btn-soft { - @layer daisyui.l1 { - &:not( .btn-active, :hover, :active:focus, :focus-visible, input:checked:not(.filter .btn), :disabled, [disabled], .btn-disabled ) { - --btn-shadow: ""; - --btn-fg: var(--btn-color, var(--color-base-content)); - --btn-bg: var(--btn-color, var(--color-base-content)); - @supports (color: color-mix(in lab, red, red)) { - --btn-bg: color-mix( - in oklab, - var(--btn-color, var(--color-base-content)) 8%, - var(--color-base-100) - ); - } - --btn-border: var(--btn-color, var(--color-base-content)); - @supports (color: color-mix(in lab, red, red)) { - --btn-border: color-mix( - in oklab, - var(--btn-color, var(--color-base-content)) 10%, - var(--color-base-100) - ); - } - --btn-noise: none; - } - @media (hover: none) { - &:not(.btn-active, :active, :focus-visible, input:checked:not(.filter .btn)):hover { - --btn-shadow: ""; - --btn-fg: var(--btn-color, var(--color-base-content)); - --btn-bg: var(--btn-color, var(--color-base-content)); - @supports (color: color-mix(in lab, red, red)) { - --btn-bg: color-mix( - in oklab, - var(--btn-color, var(--color-base-content)) 8%, - var(--color-base-100) - ); - } - --btn-border: var(--btn-color, var(--color-base-content)); - @supports (color: color-mix(in lab, red, red)) { - --btn-border: color-mix( - in oklab, - var(--btn-color, var(--color-base-content)) 10%, - var(--color-base-100) - ); - } - --btn-noise: none; - } - } - } - } .badge-error { @layer daisyui.l1.l2 { --badge-color: var(--color-error); @@ -2336,12 +1313,6 @@ --badge-fg: var(--color-success-content); } } - .btn-neutral { - @layer daisyui.l1.l2.l3 { - --btn-color: var(--color-neutral); - --btn-fg: var(--color-neutral-content); - } - } .btn-primary { @layer daisyui.l1.l2.l3 { --btn-color: var(--color-primary); @@ -2675,26 +1646,6 @@ background-position-x: -115%; } } -@property --tw-rotate-x { - syntax: "*"; - inherits: false; -} -@property --tw-rotate-y { - syntax: "*"; - inherits: false; -} -@property --tw-rotate-z { - syntax: "*"; - inherits: false; -} -@property --tw-skew-x { - syntax: "*"; - inherits: false; -} -@property --tw-skew-y { - syntax: "*"; - inherits: false; -} @property --tw-space-y-reverse { syntax: "*"; inherits: false; @@ -2837,18 +1788,9 @@ syntax: "*"; inherits: false; } -@property --tw-ease { - syntax: "*"; - inherits: false; -} @layer properties { @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) { *, ::before, ::after, ::backdrop { - --tw-rotate-x: initial; - --tw-rotate-y: initial; - --tw-rotate-z: initial; - --tw-skew-x: initial; - --tw-skew-y: initial; --tw-space-y-reverse: 0; --tw-space-x-reverse: 0; --tw-border-style: solid; @@ -2881,7 +1823,6 @@ --tw-drop-shadow-color: initial; --tw-drop-shadow-alpha: 100%; --tw-drop-shadow-size: initial; - --tw-ease: initial; } } }