Merge pull request #37 from lambdaclan/binding-reactor-demo

Add binding onSet demo
This commit is contained in:
david-swift 2024-07-04 21:46:17 +02:00 committed by GitHub
commit 9bcf802b54
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 206 additions and 0 deletions

View File

@ -5,3 +5,4 @@
- [Zev Eisenberg](https://github.com/ZevEisenberg)
- [Jay Wren](https://github.com/jrwren)
- [Amzd](https://github.com/amzd)
- [lambdaclan](https://github.com/lambdaclan)

View File

@ -55,6 +55,13 @@ struct Demo: App {
.closeShortcut()
.defaultSize(width: 400, height: 250)
.title("Form Demo")
Window(id: "password-checker-demo", open: 0) { _ in
PasswordCheckerDemo.WindowContent()
}
.closeShortcut()
.defaultSize(width: 400, height: 250)
.title("Password Checker Demo")
Window(id: "navigation", open: 0) { _ in
NavigationViewDemo.WindowContent()
}

View File

@ -25,6 +25,7 @@ enum Page: String, Identifiable, CaseIterable, Codable, CustomStringConvertible
case carousel
case viewSwitcher
case form
case passwordChecker
case popover
case flowBox
case navigationView
@ -45,6 +46,8 @@ enum Page: String, Identifiable, CaseIterable, Codable, CustomStringConvertible
return "Navigation View"
case .alertDialog:
return "Alert Dialog"
case .passwordChecker:
return "Password Checker"
default:
return rawValue.capitalized
}
@ -87,6 +90,8 @@ enum Page: String, Identifiable, CaseIterable, Codable, CustomStringConvertible
return "Switch the window's view"
case .form:
return "Group controls used for data entry"
case .passwordChecker:
return "Check the validity of a password"
case .popover:
return "Present content in a bubble-like context popup"
case .flowBox:
@ -130,6 +135,8 @@ enum Page: String, Identifiable, CaseIterable, Codable, CustomStringConvertible
ViewSwitcherDemo(app: app)
case .form:
FormDemo(app: app)
case .passwordChecker:
PasswordCheckerDemo(app: app)
case .popover:
PopoverDemo()
case .flowBox:

View File

@ -0,0 +1,191 @@
//
// PasswordCheckerDemo.swift
// Adwaita
//
// Created by lambdaclan on 13.06.24.
//
// Adjusted by david-swift on 18.06.24.
// swiftlint:disable missing_docs no_magic_numbers
import Adwaita
enum PasswordChecker: String, CaseIterable, CustomStringConvertible, Identifiable {
case length
case upper
case lower
case special
case numeric
var id: String {
self.rawValue
}
var label: String {
switch self {
case .length:
return "Password Length"
case .upper:
return "Password Uppercase Characters"
case .lower:
return "Password Lowercase Characters"
case .special:
return "Password Special Characters"
case .numeric:
return "Password Numeric Characters"
}
}
var description: String {
switch self {
case .length:
return "Password needs to be greater than 8 characters long"
case .upper:
return "Password needs to contain at least one uppercase character"
case .lower:
return "Password needs to contain at least one lowercase character"
case .special:
return "Password needs to contain at least one special character `!&^%$#@()/`"
case .numeric:
return "Password needs to contain at least one numeric character"
}
}
}
struct PasswordCheckerDemo: View {
var app: GTUIApp
var view: Body {
VStack {
Button("View Demo") {
app.showWindow("password-checker-demo")
}
.suggested()
.pill()
.frame(maxWidth: 100)
}
}
struct WindowContent: View {
@State private var password = ""
@State private var copied: Signal = .init()
var checkStatus: [String: Bool] {
PasswordChecker.allCases.enumerated().reduce([String: Bool]()) { dict, checker in
var dict = dict
dict[checker.element.rawValue] = check(password: password, checker: checker.element).1
return dict
}
}
var view: Body {
VStack {
FormSection("") {
Form {
EntryRow("Password", text: $password)
.suffix {
Button(icon: .default(icon: .editCopy)) {
State<Any>.copy(password)
copied.signal()
}
.flat()
.verticalCenter()
.tooltip("Copy")
Button(icon: .default(icon: .editClear)) {
password = ""
}
.flat()
.verticalCenter()
.tooltip("Clear")
}
}
}
.padding()
ForEach(PasswordChecker.allCases) { checker in
CheckerButton(
isValid: .constant(checkStatus[checker.rawValue] ?? false),
checkerName: checker.label,
checkerInfo: checker.description
)
.padding()
}
}
.padding()
.toast("Copied to clipboard", signal: copied)
.frame(minWidth: 340, minHeight: 400)
.topToolbar {
HeaderBar.empty()
}
}
private func check(password: String, checker: PasswordChecker) -> (String, Bool) {
switch checker {
case .length:
return (PasswordChecker.length.rawValue, password.count > 8)
case .upper:
let result = password.range(of: ".*[A-Z]+.*", options: .regularExpression)
return (PasswordChecker.upper.rawValue, result != nil ? true : false)
case .lower:
let result = password.range(of: ".*[a-z]+.*", options: .regularExpression)
return (PasswordChecker.lower.rawValue, result != nil ? true : false)
case .special:
let result = password.range(of: ".*[!&^%$#@()/]+.*", options: .regularExpression)
return (PasswordChecker.special.rawValue, result != nil ? true : false)
case .numeric:
let result = password.range(of: ".*[0-9]+.*", options: .regularExpression)
return (PasswordChecker.numeric.rawValue, result != nil ? true : false)
}
}
}
private struct CheckerButton: View {
@Binding var isValid: Bool
@State var isInfoVisible = false
var checkerName: String
var checkerInfo: String
var view: Body {
if isValid {
Button("") {
isInfoVisible = true
}
.child {
ButtonContent()
.iconName(Icon.DefaultIcon.emblemOk.string)
.label(checkerName)
.halign(.start)
}
.success()
.alertDialog(
visible: $isInfoVisible,
heading: checkerName,
body: checkerInfo
)
.response("OK", role: .close) {}
} else {
Button("") {
isInfoVisible = true
}
.child {
ButtonContent()
.iconName(Icon.DefaultIcon.faceAngry.string)
.label(checkerName)
.halign(.start)
}
.destructive()
.alertDialog(
visible: $isInfoVisible,
heading: checkerName,
body: checkerInfo
)
.response("OK", role: .close) {}
}
}
}
}
// swiftlint:enable missing_docs no_magic_numbers