Merge branch 'develop' into feature/entryFields
# Conflicts: # VDS.xcodeproj/project.pbxproj Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
commit
46eaac262c
90
.gitlab-ci.yml
Normal file
90
.gitlab-ci.yml
Normal file
@ -0,0 +1,90 @@
|
||||
stages:
|
||||
# - test
|
||||
- download
|
||||
- build
|
||||
- deploy
|
||||
|
||||
#test:
|
||||
# stage: test
|
||||
# script:
|
||||
# - echo "This job tests something"
|
||||
# tags:
|
||||
# - xcode_12_2
|
||||
|
||||
download_artifacts:
|
||||
stage: download
|
||||
script:
|
||||
- ./Scripts/download_dependencies.sh
|
||||
only:
|
||||
- branches
|
||||
- develop
|
||||
tags:
|
||||
- bash_shell
|
||||
environment:
|
||||
name: oneartifactory
|
||||
url: https://oneartifactoryprod.verizon.com/artifactory
|
||||
variables:
|
||||
ARTIFACTORY_URL: https://oneartifactoryprod.verizon.com/artifactory
|
||||
|
||||
build_project:
|
||||
stage: build
|
||||
script:
|
||||
- ./Scripts/build_aggregate.sh
|
||||
only:
|
||||
- branches
|
||||
- develop
|
||||
tags:
|
||||
- xcode_12_2
|
||||
|
||||
deploy_snapshot:
|
||||
stage: deploy
|
||||
script:
|
||||
- cd Scripts && ./upload_vds_frameworks.sh
|
||||
only:
|
||||
- branches
|
||||
- develop
|
||||
tags:
|
||||
- bash_shell
|
||||
environment:
|
||||
name: oneartifactory
|
||||
url: https://oneartifactoryprod.verizon.com/artifactory
|
||||
variables:
|
||||
ARTIFACTORY_URL: https://oneartifactoryprod.verizon.com/artifactory
|
||||
|
||||
#promote_snapshot:
|
||||
# stage: go live
|
||||
# # Prevent artifacts from needing to re-download. Everything we need is in Artifactory.
|
||||
# dependencies: []
|
||||
# script:
|
||||
# # Grab the framework version from the xcode project.
|
||||
# - framework_ver=$(cd RemoteView && agvtool vers -terse)
|
||||
# - cd Scripts && ./promote_remote_view_frameworks.sh $framework_ver
|
||||
# only:
|
||||
# - tags
|
||||
# tags:
|
||||
# - bash_shell
|
||||
# environment:
|
||||
# name: oneartifactory
|
||||
# url: https://oneartifactoryprod.verizon.com/artifactory
|
||||
# variables:
|
||||
# ARTIFACTORY_URL: https://oneartifactoryprod.verizon.com/artifactory
|
||||
#
|
||||
#create_version_tag:
|
||||
# stage: tag
|
||||
# when: manual
|
||||
# # Prevent artifacts from needing to re-download.
|
||||
# dependencies: []
|
||||
# script:
|
||||
# # Grab the framework version from the xcode project and create a tag of the version.
|
||||
# - framework_ver=$(cd RemoteView && agvtool vers -terse)
|
||||
# - git tag -a "v${framework_ver}" -m "Version ${framework_ver} created by gitlab-ci Build"
|
||||
# # Extract the git repo url to ssh version (git@gitlab.verizon.com)
|
||||
# - ci_push_repo="git@${CI_SERVER_HOST}:${CI_PROJECT_PATH}.git"
|
||||
# - echo $ci_push_repo
|
||||
# # Set the remote url for pushing assuming the gitlab runner has SSH access to the repo.
|
||||
# - git remote set-url --push origin $ci_push_repo
|
||||
# - git push origin "v${framework_ver}"
|
||||
# only:
|
||||
# - develop
|
||||
# tags:
|
||||
# - bash_shell
|
||||
55
Scripts/build_aggregate.sh
Executable file
55
Scripts/build_aggregate.sh
Executable file
@ -0,0 +1,55 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
unset TOOLCHAINS #Xcode 7.3 BUG FIX http://stackoverflow.com/questions/36184930/xcodebuild-7-3-cant-enable-bitcode
|
||||
|
||||
# define variables
|
||||
C_PROJECT_NAME="VDS"
|
||||
PHONE_CONFIGURATION="Release"
|
||||
BUILD_DIR=$(xcodebuild -showBuildSettings -project ./${C_PROJECT_NAME}.xcodeproj | grep -w -o 'BUILD_DIR = .*' | cut -d\ -f3-)
|
||||
PHONE_ARCHIVE_PATH="${BUILD_DIR}/${PHONE_CONFIGURATION}-iphoneos/${C_PROJECT_NAME}.xcarchive"
|
||||
SIMULATOR_CONFIGURATION="Debug"
|
||||
SIMULATOR_ARCHIVE_PATH="${BUILD_DIR}/${SIMULATOR_CONFIGURATION}-iphonesimulator/${C_PROJECT_NAME}.xcarchive"
|
||||
FRAMEWORKS_DIR=$BUILD_DIR/../../../Frameworks/VDS
|
||||
UNIVERSAL_OUTPUTFOLDER="${BUILD_DIR}/universal"
|
||||
|
||||
sed -i '' "s|path = \.\.\/SharedFrameworks|path = ${FRAMEWORKS_DIR}|g" ./VDS.xcodeproj/project.pbxproj
|
||||
|
||||
# Build device archive
|
||||
xcodebuild archive \
|
||||
-scheme "${C_PROJECT_NAME}" \
|
||||
-configuration ${PHONE_CONFIGURATION} \
|
||||
-archivePath "${PHONE_ARCHIVE_PATH}" \
|
||||
-sdk iphoneos \
|
||||
-destination 'generic/platform=iOS' \
|
||||
-quiet \
|
||||
SKIP_INSTALL=NO \
|
||||
ONLY_ACTIVE_ARCH=NO \
|
||||
FRAMEWORK_SEARCH_PATHS="$FRAMEWORKS_DIR" \
|
||||
ALWAYS_SEARCH_USER_PATHS=true
|
||||
|
||||
# Build simulator archive
|
||||
xcodebuild archive \
|
||||
-scheme "${C_PROJECT_NAME}" \
|
||||
-configuration ${SIMULATOR_CONFIGURATION} \
|
||||
-archivePath "${SIMULATOR_ARCHIVE_PATH}" \
|
||||
-sdk iphonesimulator \
|
||||
-destination 'generic/platform=iOS Simulator' \
|
||||
-quiet \
|
||||
SKIP_INSTALL=NO \
|
||||
ONLY_ACTIVE_ARCH=NO \
|
||||
FRAMEWORK_SEARCH_PATHS=$FRAMEWORKS_DIR \
|
||||
ALWAYS_SEARCH_USER_PATHS=true
|
||||
|
||||
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"
|
||||
|
||||
# Remove any existing xc framework.
|
||||
rm -rf ${UNIVERSAL_OUTPUTFOLDER}/${C_PROJECT_NAME}.xcframework
|
||||
|
||||
# Create the xcframework in the universal output folder.
|
||||
xcodebuild -create-xcframework \
|
||||
-framework "${PHONE_ARCHIVE_PATH}/Products/Library/Frameworks/${C_PROJECT_NAME}.framework" \
|
||||
-framework "${SIMULATOR_ARCHIVE_PATH}/Products/Library/Frameworks/${C_PROJECT_NAME}.framework" \
|
||||
-output ${UNIVERSAL_OUTPUTFOLDER}/${C_PROJECT_NAME}.xcframework
|
||||
|
||||
# Copy the dSYM file as well.
|
||||
ditto "${PHONE_ARCHIVE_PATH}/dSYMs/${C_PROJECT_NAME}.framework.dSYM" "${UNIVERSAL_OUTPUTFOLDER}/${C_PROJECT_NAME}.xcframework.dSYM"
|
||||
25
Scripts/download_dependencies.sh
Executable file
25
Scripts/download_dependencies.sh
Executable file
@ -0,0 +1,25 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
# download_dependencies.sh
|
||||
#
|
||||
# Downloads all compiled framework flavors in from Artifactory.
|
||||
#
|
||||
|
||||
# Create new aggregate builds
|
||||
|
||||
if [ -z $ARTIFACTORY_URL ]; then
|
||||
ARTIFACTORY_URL="https://oneartifactoryprod.verizon.com/artifactory"
|
||||
fi
|
||||
|
||||
BUILD_DIR=$(xcodebuild -showBuildSettings -project ./VDS.xcodeproj | grep -w -o 'BUILD_DIR = .*' | cut -d\ -f3-)
|
||||
FRAMEWORKS_DIR=$BUILD_DIR/../../../Frameworks/VDS
|
||||
|
||||
if [ ! -d $FRAMEWORKS_DIR ]; then
|
||||
mkdir -p $FRAMEWORKS_DIR
|
||||
fi
|
||||
|
||||
./Scripts/download_framework.sh $ARTIFACTORY_URL "$FRAMEWORKS_DIR/VDSColorTokens.xcframework" GVJV_VDS_Maven/@vds-tokens/ios/VDSColorTokens.1.0.6.xcframework.zip
|
||||
|
||||
./Scripts/download_framework.sh $ARTIFACTORY_URL "$FRAMEWORKS_DIR/VDSFormControlsTokens.xcframework" GVJV_VDS_Maven/@vds-tokens/ios/VDSFormControlsTokens.1.0.7.xcframework.zip
|
||||
|
||||
./Scripts/download_framework.sh $ARTIFACTORY_URL "$FRAMEWORKS_DIR/VDSTypographyTokens.xcframework" GVJV_VDS_Maven/@vds-tokens/ios/VDSTypographyTokens.2.0.0.xcframework.zip
|
||||
86
Scripts/download_framework.sh
Executable file
86
Scripts/download_framework.sh
Executable file
@ -0,0 +1,86 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
# upload_framework.sh
|
||||
#
|
||||
# Downloads an iOS framework from Artificatory.
|
||||
#
|
||||
# An API key from Artifcatory is required in the api_key.private file before uploading.
|
||||
#
|
||||
|
||||
URL=$1
|
||||
LOCALPATH="${2}"
|
||||
REMOTEPATH="${3}"
|
||||
LOGFILE=$3
|
||||
LOCALDIR=$(dirname "${LOCALPATH}")
|
||||
LOCALBASE=$(basename "${LOCALPATH}")
|
||||
NEWFILEPATH="${LOCALDIR}"/$(basename "${REMOTEPATH}")
|
||||
VERSIONFILE=$LOCALDIR/Checksums/"${LOCALBASE}".txt
|
||||
|
||||
if [ -z $URL ]; then
|
||||
echo "The artifactory instance url must be specified as the first argument!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
#first argument is error message.
|
||||
exit_with_error () {
|
||||
echo "Error: $1"
|
||||
if [ -f "${NEWFILEPATH}" ]; then
|
||||
rm -rf "${NEWFILEPATH}" 2
|
||||
fi
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo "----------------------------------------------------------"
|
||||
echo "Logs for ${LOCALBASE}"
|
||||
|
||||
echo -e "Local Target: ${LOCALPATH}"
|
||||
echo -e "Remote Source: ${REMOTEPATH}"
|
||||
|
||||
if [ -z "$LOCALPATH" ]; then
|
||||
exit_with_error "Missing local path argument"
|
||||
fi
|
||||
|
||||
if [ -z "$REMOTEPATH" ]; then
|
||||
exit_with_error "Missing filename path argument"
|
||||
fi
|
||||
|
||||
#get local and remote checksums for comparison
|
||||
echo -e "Getting checksums..."
|
||||
echo -e "URL: ${URL}/api/storage/${REMOTEPATH}"
|
||||
JSON=$(curl --header "X-JFrog-Art-Api: ${ARTIFACTORY_APIKEY}" -X GET "${URL}/api/storage/${REMOTEPATH}")
|
||||
CHECKSUM=$(echo "$JSON" | python3 -c 'import sys, json; print (json.load(sys.stdin)["checksums"]["sha1"])')
|
||||
if [[ -z "$CHECKSUM" ]]; then
|
||||
exit_with_error "No Checksum found in json: ${JSON}"
|
||||
fi
|
||||
echo "Remote checksum ${CHECKSUM}"
|
||||
if [[ -f "${VERSIONFILE}" ]]; then
|
||||
OLDCHECKSUM=$(cat "${VERSIONFILE}")
|
||||
else
|
||||
OLDCHECKSUM="none"
|
||||
mkdir -p $(dirname ${VERSIONFILE}) && touch "$VERSIONFILE"
|
||||
fi
|
||||
echo "Local checksum ${OLDCHECKSUM}"
|
||||
|
||||
#get new framework if no original framework, no local checksum, or remote checksum is different from local.
|
||||
if [ ! -e "${LOCALPATH}" ] || [ -z "$OLDCHECKSUM" ] || [ "$CHECKSUM" != "$OLDCHECKSUM" ]; then
|
||||
echo "Downloading..."
|
||||
echo -e "URL: ${URL}/${REMOTEPATH}"
|
||||
curl --header "X-JFrog-Art-Api: ${ARTIFACTORY_APIKEY}" -f -X GET "$URL/$REMOTEPATH" --output "${NEWFILEPATH}"
|
||||
if [ $? -eq 0 ] && [ -e "${NEWFILEPATH}" ]; then
|
||||
echo "Finished Downloading, begin unzip"
|
||||
unzip -q -o "${NEWFILEPATH}" -d "${LOCALDIR}"
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Finished unzipping, remove zip"
|
||||
rm -rf "${NEWFILEPATH}"
|
||||
echo "Writing new checksum to file"
|
||||
echo "${CHECKSUM}" > "${VERSIONFILE}"
|
||||
echo "Successfully downloaded and unzipped archive."
|
||||
else
|
||||
exit_with_error "Error unzipping"
|
||||
fi
|
||||
else
|
||||
exit_with_error "Failed to download"
|
||||
fi
|
||||
else
|
||||
echo "Successful, No New Version"
|
||||
fi
|
||||
86
Scripts/upload_framework.sh
Executable file
86
Scripts/upload_framework.sh
Executable file
@ -0,0 +1,86 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
# upload_framework.sh
|
||||
#
|
||||
# Uploads an iOS framework to Artificatory given the local path as the first argument and the remote project name in Verizon OneArtifactory for the second argument.
|
||||
#
|
||||
# An API key from Artifcatory is required in the api_key.private file before uploading.
|
||||
#
|
||||
# The script will replace [VER] in the provided remote path with the version found in the framework bundle.
|
||||
#
|
||||
# Copied from Hedden, Kyle Matthew on 3/2/18.
|
||||
# Copyright © 2018 Verizon. All rights reserved.
|
||||
|
||||
URL=$1
|
||||
LOCALPATH=$2
|
||||
REMOTEPATH=$3
|
||||
FRAMEWORKVER=$4
|
||||
|
||||
if [ -z $URL ]; then
|
||||
echo "The artifactory instance url must be specified as the first argument!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ">>> UPLOAD START <<<"
|
||||
echo "Local path: ${LOCALPATH}"
|
||||
echo "Remote path: ${REMOTEPATH}"
|
||||
|
||||
cat "${LOCALPATH}/Info.plist"
|
||||
|
||||
LOCALBASE=$(basename "${LOCALPATH}")
|
||||
LOCALDIR=$(dirname "${LOCALPATH}")
|
||||
|
||||
# Replace the [VER] placeholders with the found version.
|
||||
REMOTEPATH="${REMOTEPATH//\[VER\]/$FRAMEWORKVER}"
|
||||
echo -e "Resolved path: \t\t${REMOTEPATH}"
|
||||
|
||||
if [ -z $ARTIFACTORY_APIKEY ]; then
|
||||
# Read the API key from a private file.
|
||||
read -r APIKEY < "api_key.private"
|
||||
else
|
||||
APIKEY=$ARTIFACTORY_APIKEY
|
||||
fi
|
||||
|
||||
if [ -z $APIKEY ]; then
|
||||
read -p "Artifactory API Key:" APIKEY
|
||||
echo $APIKEY >> api_key.private
|
||||
fi
|
||||
|
||||
echo -e "API Key: \t\t${APIKEY}"
|
||||
|
||||
# Zip the framework & DSYM for uploading.
|
||||
pushd $LOCALDIR
|
||||
echo -e "---------\nZipping: \t\t${LOCALBASE}.zip"
|
||||
zip -r -X "${LOCALBASE}.zip" $LOCALBASE
|
||||
# Generate framework's SHA-1 checksum.
|
||||
CHECKSUM=$(shasum -a 1 "${LOCALBASE}.zip" | cut -d " " -f 1)
|
||||
echo -e "SHA-1 Checksum: \t${CHECKSUM}"
|
||||
if [ -e ${LOCALBASE}.dSYM ]; then
|
||||
echo -e "---------\nZipping: \t\t${LOCALBASE}.dSYM.zip"
|
||||
zip -r -X "${LOCALBASE}.dSYM.zip" $LOCALBASE.dSYM
|
||||
# Generate its SHA-1 checksum for dsym.
|
||||
DSYM_CHECKSUM=$(shasum -a 1 "${LOCALBASE}.dSYM.zip" | cut -d " " -f 1)
|
||||
echo -e "SHA-1 Checksum: \t${DSYM_CHECKSUM}"
|
||||
fi
|
||||
popd
|
||||
mv ${LOCALPATH}.zip .
|
||||
if [ -e ${LOCALPATH}.dSYM.zip ]; then
|
||||
mv ${LOCALPATH}.dSYM.zip .
|
||||
fi
|
||||
|
||||
# Upload framework to Artifactory.
|
||||
echo -e "---------\nUploading to: \t\t${URL}/${REMOTEPATH}.zip"
|
||||
curl --header "X-JFrog-Art-Api: ${APIKEY}" --header "X-Checksum-Sha1: ${CHECKSUM}" -X PUT "${URL}/${REMOTEPATH}.zip" -T "${LOCALBASE}.zip"
|
||||
|
||||
# Cleanup.
|
||||
rm "${LOCALBASE}.zip"
|
||||
|
||||
if [ -e ${LOCALBASE}.dSYM.zip ]; then
|
||||
# Upload dsym Artifactory.
|
||||
echo -e "---------\nUploading to: \t\t${URL}/${REMOTEPATH}.dSYM.zip"
|
||||
curl --header "X-JFrog-Art-Api: ${APIKEY}" --header "X-Checksum-Sha1: ${DSYM_CHECKSUM}" -X PUT "${URL}/${REMOTEPATH}.dSYM.zip" -T "${LOCALBASE}.dSYM.zip"
|
||||
# Cleanup dsym.
|
||||
rm ${LOCALBASE}.dSYM.zip
|
||||
fi
|
||||
|
||||
echo -e "\n\n<<< UPLOAD COMPLETE >>>\n\n"
|
||||
25
Scripts/upload_vds_frameworks.sh
Executable file
25
Scripts/upload_vds_frameworks.sh
Executable file
@ -0,0 +1,25 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
# upload_core_ui_frameworks.sh
|
||||
#
|
||||
# Uploads all compiled framework flavors in MVMCoreUI to Artifactory with the SNAPSHOT classifier. This is to avoid accidently clobbering a release build of a particular version. Remove the classifier in Artificatory to make a release.
|
||||
#
|
||||
# Copied from Hedden, Kyle Matthew on 3/2/18.
|
||||
#
|
||||
|
||||
BUILD_SETTINGS=$(xcodebuild -showBuildSettings -project ../VDS.xcodeproj)
|
||||
FRAMEWORK_VERSION=$(echo "$BUILD_SETTINGS" | grep -w -o 'MARKETING_VERSION = .*' | cut -d\ -f3-)
|
||||
if [ $(git tag --list | grep "v${FRAMEWORK_VERSION}") ]; then
|
||||
echo This version tag has already been committed! Aborting!
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create new aggregate builds
|
||||
|
||||
if [ -z $ARTIFACTORY_URL ]; then
|
||||
ARTIFACTORY_URL="https://oneartifactoryprod.verizon.com/artifactory"
|
||||
fi
|
||||
|
||||
# Upload
|
||||
BUILD_DIR=$(echo "$BUILD_SETTINGS" | grep -w -o 'BUILD_DIR = .*' | cut -d\ -f3-)
|
||||
./upload_framework.sh $ARTIFACTORY_URL "${BUILD_DIR}/universal/VDS.xcframework" BPHV_MobileFirst_IOS/com/vzw/hss/myverizon/VDS/[VER]/VDS-[VER]-Debug-SNAPSHOT $FRAMEWORK_VERSION
|
||||
@ -9,18 +9,12 @@
|
||||
/* Begin PBXBuildFile section */
|
||||
5F21D7BF28DCEB3D003E7CD6 /* Useable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */; };
|
||||
5FC35BE328D51405004EBEAC /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FC35BE228D51405004EBEAC /* Button.swift */; };
|
||||
5FC35BE528D51414004EBEAC /* ButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FC35BE428D51413004EBEAC /* ButtonModel.swift */; };
|
||||
EA1F265D28B944F00033E859 /* CollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA1F265B28B944F00033E859 /* CollectionView.swift */; };
|
||||
EA1F265E28B944F00033E859 /* CollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA1F265C28B944F00033E859 /* CollectionViewCell.swift */; };
|
||||
EA1F266428B945070033E859 /* RadioSwatchGroupModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA1F266028B945070033E859 /* RadioSwatchGroupModel.swift */; };
|
||||
EA1F266528B945070033E859 /* RadioSwatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA1F266128B945070033E859 /* RadioSwatch.swift */; };
|
||||
EA1F266628B945070033E859 /* RadioSwatchGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA1F266228B945070033E859 /* RadioSwatchGroup.swift */; };
|
||||
EA1F266728B945070033E859 /* RadioSwatchModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA1F266328B945070033E859 /* RadioSwatchModel.swift */; };
|
||||
EA336171288B19200071C351 /* VDS.docc in Sources */ = {isa = PBXBuildFile; fileRef = EA336170288B19200071C351 /* VDS.docc */; };
|
||||
EA336177288B19210071C351 /* VDS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA33616C288B19200071C351 /* VDS.framework */; };
|
||||
EA33617C288B19210071C351 /* VDSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA33617B288B19210071C351 /* VDSTests.swift */; };
|
||||
EA33617D288B19210071C351 /* VDS.h in Headers */ = {isa = PBXBuildFile; fileRef = EA33616F288B19200071C351 /* VDS.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
EA3361A2288B1E840071C351 /* ToggleModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361A1288B1E840071C351 /* ToggleModel.swift */; };
|
||||
EA3361A8288B23300071C351 /* UIColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361A7288B23300071C351 /* UIColor.swift */; };
|
||||
EA3361AA288B25E40071C351 /* Disabling.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361A9288B25E40071C351 /* Disabling.swift */; };
|
||||
EA3361AD288B26190071C351 /* DataTrackable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361AC288B26190071C351 /* DataTrackable.swift */; };
|
||||
@ -28,7 +22,7 @@
|
||||
EA3361B6288B2A410071C351 /* Control.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361B5288B2A410071C351 /* Control.swift */; };
|
||||
EA3361B8288B2AAA0071C351 /* ViewProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361B7288B2AAA0071C351 /* ViewProtocol.swift */; };
|
||||
EA3361BD288B2C760071C351 /* TypeAlias.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361BC288B2C760071C351 /* TypeAlias.swift */; };
|
||||
EA3361BF288B2EA60071C351 /* ModelHandlerable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361BE288B2EA60071C351 /* ModelHandlerable.swift */; };
|
||||
EA3361BF288B2EA60071C351 /* Handlerable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361BE288B2EA60071C351 /* Handlerable.swift */; };
|
||||
EA3361C328902D960071C351 /* Toggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361C228902D960071C351 /* Toggle.swift */; };
|
||||
EA3361C5289030FC0071C351 /* Accessable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361C4289030FC0071C351 /* Accessable.swift */; };
|
||||
EA3361C9289054C50071C351 /* Surfaceable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361C8289054C50071C351 /* Surfaceable.swift */; };
|
||||
@ -41,45 +35,34 @@
|
||||
EA3362302891EB4A0071C351 /* Fonts.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA33622F2891EB4A0071C351 /* Fonts.swift */; };
|
||||
EA33623E2892EE950071C351 /* UIDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA33623D2892EE950071C351 /* UIDevice.swift */; };
|
||||
EA3362402892EF6C0071C351 /* Label.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA33623F2892EF6B0071C351 /* Label.swift */; };
|
||||
EA3362432892EFF20071C351 /* LabelModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3362422892EFF20071C351 /* LabelModel.swift */; };
|
||||
EA3362452892F9130071C351 /* Labelable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3362442892F9130071C351 /* Labelable.swift */; };
|
||||
EA33624728931B050071C351 /* Initable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA33624628931B050071C351 /* Initable.swift */; };
|
||||
EA3C3B4C2894823E000CA526 /* AnyProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3C3B4B2894823E000CA526 /* AnyProxy.swift */; };
|
||||
EA4DB18528CA967F00103EE3 /* SelectorGroupHandlerBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA4DB18428CA967F00103EE3 /* SelectorGroupHandlerBase.swift */; };
|
||||
EA4DB2FD28D3D0CA00103EE3 /* AnyEquatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA4DB2FC28D3D0CA00103EE3 /* AnyEquatable.swift */; };
|
||||
EA4DB30028DCBC9900103EE3 /* BadgeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA4DB2FF28DCBC9900103EE3 /* BadgeModel.swift */; };
|
||||
EA4DB30228DCBCA500103EE3 /* Badge.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA4DB30128DCBCA500103EE3 /* Badge.swift */; };
|
||||
EA84F6B128B94A2500D67ABC /* CodableColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA84F6B028B94A2500D67ABC /* CodableColor.swift */; };
|
||||
EA89200228AECF2A006B9984 /* UIButton+Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89200128AECF2A006B9984 /* UIButton+Publisher.swift */; };
|
||||
EA89200428AECF4B006B9984 /* UITextField+Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89200328AECF4B006B9984 /* UITextField+Publisher.swift */; };
|
||||
EA89200628B526D6006B9984 /* CheckboxGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89200528B526D6006B9984 /* CheckboxGroup.swift */; };
|
||||
EA89200828B526E0006B9984 /* CheckboxGroupModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89200728B526E0006B9984 /* CheckboxGroupModel.swift */; };
|
||||
EA89200D28B530FD006B9984 /* RadioBoxModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89200C28B530FD006B9984 /* RadioBoxModel.swift */; };
|
||||
EA89200F28B53921006B9984 /* Selectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89200E28B53921006B9984 /* Selectable.swift */; };
|
||||
EA89201328B568D8006B9984 /* RadioBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89201228B568D8006B9984 /* RadioBox.swift */; };
|
||||
EA89201528B56CF4006B9984 /* RadioBoxGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89201428B56CF4006B9984 /* RadioBoxGroup.swift */; };
|
||||
EA89201728B56CFF006B9984 /* RadioBoxGroupModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89201628B56CFF006B9984 /* RadioBoxGroupModel.swift */; };
|
||||
EAA5EEA928EB3ED0003B3210 /* EntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA5EEA828EB3ED0003B3210 /* EntryField.swift */; };
|
||||
EAA5EEAB28EB3ED9003B3210 /* EntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA5EEAA28EB3ED9003B3210 /* EntryFieldModel.swift */; };
|
||||
EAA5EEB128EB6A5A003B3210 /* TextEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA5EEB028EB6A5A003B3210 /* TextEntryFieldModel.swift */; };
|
||||
EAA5EEB328EB6A66003B3210 /* TextEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA5EEB228EB6A66003B3210 /* TextEntryField.swift */; };
|
||||
EAA5EEB528ECBFB4003B3210 /* ImageLabelAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA5EEB428ECBFB4003B3210 /* ImageLabelAttribute.swift */; };
|
||||
EAA5EEB728ECC03A003B3210 /* ToolTipLabelAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA5EEB628ECC03A003B3210 /* ToolTipLabelAttribute.swift */; };
|
||||
EAA5EEB928ECD24B003B3210 /* Icons.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EAA5EEB828ECD24B003B3210 /* Icons.xcassets */; };
|
||||
EAA5EEE028F49DB3003B3210 /* Colorable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA5EEDF28F49DB3003B3210 /* Colorable.swift */; };
|
||||
EAA5EEE428F5B855003B3210 /* VerizonNHGDS-Light.otf in Resources */ = {isa = PBXBuildFile; fileRef = EAA5EEE328F5B855003B3210 /* VerizonNHGDS-Light.otf */; };
|
||||
EAB1D29A28A5611D00DAE764 /* SelectorGroupModelable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D29928A5611D00DAE764 /* SelectorGroupModelable.swift */; };
|
||||
EAA5EEEF28F5C908003B3210 /* VDSTypographyTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAA5EEEC28F5C908003B3210 /* VDSTypographyTokens.xcframework */; };
|
||||
EAA5EEF128F5C909003B3210 /* VDSColorTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAA5EEED28F5C908003B3210 /* VDSColorTokens.xcframework */; };
|
||||
EAA5EEF328F5C909003B3210 /* VDSFormControlsTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAA5EEEE28F5C908003B3210 /* VDSFormControlsTokens.xcframework */; };
|
||||
EAB1D29C28A5618900DAE764 /* RadioButtonGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D29B28A5618900DAE764 /* RadioButtonGroup.swift */; };
|
||||
EAB1D29E28A5619500DAE764 /* RadioButtonGroupModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D29D28A5619500DAE764 /* RadioButtonGroupModel.swift */; };
|
||||
EAB1D2A128A598FE00DAE764 /* UsesAutoLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2A028A598FE00DAE764 /* UsesAutoLayout.swift */; };
|
||||
EAB1D2A328A5994800DAE764 /* Debuggable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2A228A5994800DAE764 /* Debuggable.swift */; };
|
||||
EAB1D2CD28ABE76100DAE764 /* Withable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2CC28ABE76000DAE764 /* Withable.swift */; };
|
||||
EAB1D2CF28ABEF2B00DAE764 /* Typography.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2CE28ABEF2B00DAE764 /* Typography.swift */; };
|
||||
EAB1D2E628AE842000DAE764 /* Publisher+Bind.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2E328AE842000DAE764 /* Publisher+Bind.swift */; };
|
||||
EAB1D2EA28AE84AA00DAE764 /* UIControlPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2E928AE84AA00DAE764 /* UIControlPublisher.swift */; };
|
||||
EAC925842911C63100091998 /* Colorable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA5EEDF28F49DB3003B3210 /* Colorable.swift */; };
|
||||
EAC9258C2911C9DE00091998 /* TextEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC925872911C9DE00091998 /* TextEntryField.swift */; };
|
||||
EAC9258D2911C9DE00091998 /* TextEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC925882911C9DE00091998 /* TextEntryFieldModel.swift */; };
|
||||
EAC9258E2911C9DE00091998 /* EntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC9258A2911C9DE00091998 /* EntryFieldModel.swift */; };
|
||||
EAC9258F2911C9DE00091998 /* EntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC9258B2911C9DE00091998 /* EntryField.swift */; };
|
||||
EAD8D2C128BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD8D2C028BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift */; };
|
||||
EAF7F0952899861000B287F5 /* Checkbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0932899861000B287F5 /* Checkbox.swift */; };
|
||||
EAF7F0962899861000B287F5 /* CheckboxModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0942899861000B287F5 /* CheckboxModel.swift */; };
|
||||
EAF7F09A2899B17200B287F5 /* CATransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0992899B17200B287F5 /* CATransaction.swift */; };
|
||||
EAF7F09E289AAEC000B287F5 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F09D289AAEC000B287F5 /* Constants.swift */; };
|
||||
EAF7F0A0289AB7EC00B287F5 /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F09F289AB7EC00B287F5 /* View.swift */; };
|
||||
@ -94,12 +77,7 @@
|
||||
EAF7F0B5289C126F00B287F5 /* UILabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0B4289C126F00B287F5 /* UILabel.swift */; };
|
||||
EAF7F0B7289C12A600B287F5 /* UITapGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0B6289C12A600B287F5 /* UITapGestureRecognizer.swift */; };
|
||||
EAF7F0B9289C139800B287F5 /* ColorConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0B8289C139800B287F5 /* ColorConfiguration.swift */; };
|
||||
EAF7F0BB289D80ED00B287F5 /* Modelable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0BA289D80ED00B287F5 /* Modelable.swift */; };
|
||||
EAF7F0F8289DB1AC00B287F5 /* VDSFormControlsTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAF7F0E8289DB0DA00B287F5 /* VDSFormControlsTokens.xcframework */; };
|
||||
EAF7F0F9289DB1AC00B287F5 /* VDSTypographyTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA3361F32891D5290071C351 /* VDSTypographyTokens.xcframework */; };
|
||||
EAF7F0FB289DB1AC00B287F5 /* VDSColorTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAF7F0E9289DB0DA00B287F5 /* VDSColorTokens.xcframework */; };
|
||||
EAF7F11728A1475A00B287F5 /* RadioButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F11528A1475A00B287F5 /* RadioButton.swift */; };
|
||||
EAF7F11828A1475A00B287F5 /* RadioButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F11628A1475A00B287F5 /* RadioButtonModel.swift */; };
|
||||
EAF7F13328A2A16500B287F5 /* AttachmentLabelAttributeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F13228A2A16500B287F5 /* AttachmentLabelAttributeModel.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
@ -116,21 +94,13 @@
|
||||
/* Begin PBXFileReference section */
|
||||
5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Useable.swift; sourceTree = "<group>"; };
|
||||
5FC35BE228D51405004EBEAC /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = "<group>"; };
|
||||
5FC35BE428D51413004EBEAC /* ButtonModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonModel.swift; sourceTree = "<group>"; };
|
||||
EA1F265B28B944F00033E859 /* CollectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionView.swift; sourceTree = "<group>"; };
|
||||
EA1F265C28B944F00033E859 /* CollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewCell.swift; sourceTree = "<group>"; };
|
||||
EA1F266028B945070033E859 /* RadioSwatchGroupModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioSwatchGroupModel.swift; sourceTree = "<group>"; };
|
||||
EA1F266128B945070033E859 /* RadioSwatch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioSwatch.swift; sourceTree = "<group>"; };
|
||||
EA1F266228B945070033E859 /* RadioSwatchGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioSwatchGroup.swift; sourceTree = "<group>"; };
|
||||
EA1F266328B945070033E859 /* RadioSwatchModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioSwatchModel.swift; sourceTree = "<group>"; };
|
||||
EA33616C288B19200071C351 /* VDS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = VDS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
EA33616F288B19200071C351 /* VDS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VDS.h; sourceTree = "<group>"; };
|
||||
EA336170288B19200071C351 /* VDS.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = VDS.docc; sourceTree = "<group>"; };
|
||||
EA336176288B19210071C351 /* VDSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = VDSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
EA33617B288B19210071C351 /* VDSTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VDSTests.swift; sourceTree = "<group>"; };
|
||||
EA33618E288B1C0C0071C351 /* VDSColorTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSColorTokens.xcframework; path = "/Users/mattbruce/Documents/Projects/iPhone/Frameworks/MVA-JSONCreator/JSONCreator_iOS/../SharedFrameworks/VDSColorTokens.xcframework"; sourceTree = "<absolute>"; };
|
||||
EA33618F288B1C0C0071C351 /* VDSFormControlsTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSFormControlsTokens.xcframework; path = "/Users/mattbruce/Documents/Projects/iPhone/Frameworks/MVA-JSONCreator/JSONCreator_iOS/../SharedFrameworks/VDSFormControlsTokens.xcframework"; sourceTree = "<absolute>"; };
|
||||
EA3361A1288B1E840071C351 /* ToggleModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToggleModel.swift; sourceTree = "<group>"; };
|
||||
EA3361A7288B23300071C351 /* UIColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIColor.swift; sourceTree = "<group>"; };
|
||||
EA3361A9288B25E40071C351 /* Disabling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Disabling.swift; sourceTree = "<group>"; };
|
||||
EA3361AC288B26190071C351 /* DataTrackable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataTrackable.swift; sourceTree = "<group>"; };
|
||||
@ -138,11 +108,10 @@
|
||||
EA3361B5288B2A410071C351 /* Control.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Control.swift; sourceTree = "<group>"; };
|
||||
EA3361B7288B2AAA0071C351 /* ViewProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewProtocol.swift; sourceTree = "<group>"; };
|
||||
EA3361BC288B2C760071C351 /* TypeAlias.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypeAlias.swift; sourceTree = "<group>"; };
|
||||
EA3361BE288B2EA60071C351 /* ModelHandlerable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModelHandlerable.swift; sourceTree = "<group>"; };
|
||||
EA3361BE288B2EA60071C351 /* Handlerable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Handlerable.swift; sourceTree = "<group>"; };
|
||||
EA3361C228902D960071C351 /* Toggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toggle.swift; sourceTree = "<group>"; };
|
||||
EA3361C4289030FC0071C351 /* Accessable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Accessable.swift; sourceTree = "<group>"; };
|
||||
EA3361C8289054C50071C351 /* Surfaceable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Surfaceable.swift; sourceTree = "<group>"; };
|
||||
EA3361F32891D5290071C351 /* VDSTypographyTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSTypographyTokens.xcframework; path = ../SharedFrameworks/VDSTypographyTokens.xcframework; sourceTree = "<group>"; };
|
||||
EA3362002891E14C0071C351 /* VerizonNHGeTX-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "VerizonNHGeTX-Bold.otf"; sourceTree = "<group>"; };
|
||||
EA3362012891E14D0071C351 /* VerizonNHGeDS-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "VerizonNHGeDS-Bold.otf"; sourceTree = "<group>"; };
|
||||
EA3362022891E14D0071C351 /* VerizonNHGeTX-Regular.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "VerizonNHGeTX-Regular.otf"; sourceTree = "<group>"; };
|
||||
@ -152,45 +121,34 @@
|
||||
EA33622F2891EB4A0071C351 /* Fonts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fonts.swift; sourceTree = "<group>"; };
|
||||
EA33623D2892EE950071C351 /* UIDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIDevice.swift; sourceTree = "<group>"; };
|
||||
EA33623F2892EF6B0071C351 /* Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = "<group>"; };
|
||||
EA3362422892EFF20071C351 /* LabelModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelModel.swift; sourceTree = "<group>"; };
|
||||
EA3362442892F9130071C351 /* Labelable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Labelable.swift; sourceTree = "<group>"; };
|
||||
EA33624628931B050071C351 /* Initable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Initable.swift; sourceTree = "<group>"; };
|
||||
EA3C3B4B2894823E000CA526 /* AnyProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyProxy.swift; sourceTree = "<group>"; };
|
||||
EA4DB18428CA967F00103EE3 /* SelectorGroupHandlerBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectorGroupHandlerBase.swift; sourceTree = "<group>"; };
|
||||
EA4DB2FC28D3D0CA00103EE3 /* AnyEquatable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyEquatable.swift; sourceTree = "<group>"; };
|
||||
EA4DB2FF28DCBC9900103EE3 /* BadgeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgeModel.swift; sourceTree = "<group>"; };
|
||||
EA4DB30128DCBCA500103EE3 /* Badge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Badge.swift; sourceTree = "<group>"; };
|
||||
EA84F6B028B94A2500D67ABC /* CodableColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodableColor.swift; sourceTree = "<group>"; };
|
||||
EA89200128AECF2A006B9984 /* UIButton+Publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIButton+Publisher.swift"; sourceTree = "<group>"; };
|
||||
EA89200328AECF4B006B9984 /* UITextField+Publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextField+Publisher.swift"; sourceTree = "<group>"; };
|
||||
EA89200528B526D6006B9984 /* CheckboxGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxGroup.swift; sourceTree = "<group>"; };
|
||||
EA89200728B526E0006B9984 /* CheckboxGroupModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxGroupModel.swift; sourceTree = "<group>"; };
|
||||
EA89200C28B530FD006B9984 /* RadioBoxModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioBoxModel.swift; sourceTree = "<group>"; };
|
||||
EA89200E28B53921006B9984 /* Selectable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Selectable.swift; sourceTree = "<group>"; };
|
||||
EA89201228B568D8006B9984 /* RadioBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioBox.swift; sourceTree = "<group>"; };
|
||||
EA89201428B56CF4006B9984 /* RadioBoxGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioBoxGroup.swift; sourceTree = "<group>"; };
|
||||
EA89201628B56CFF006B9984 /* RadioBoxGroupModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioBoxGroupModel.swift; sourceTree = "<group>"; };
|
||||
EAA5EEA828EB3ED0003B3210 /* EntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntryField.swift; sourceTree = "<group>"; };
|
||||
EAA5EEAA28EB3ED9003B3210 /* EntryFieldModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntryFieldModel.swift; sourceTree = "<group>"; };
|
||||
EAA5EEB028EB6A5A003B3210 /* TextEntryFieldModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextEntryFieldModel.swift; sourceTree = "<group>"; };
|
||||
EAA5EEB228EB6A66003B3210 /* TextEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextEntryField.swift; sourceTree = "<group>"; };
|
||||
EAA5EEB428ECBFB4003B3210 /* ImageLabelAttribute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageLabelAttribute.swift; sourceTree = "<group>"; };
|
||||
EAA5EEB628ECC03A003B3210 /* ToolTipLabelAttribute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolTipLabelAttribute.swift; sourceTree = "<group>"; };
|
||||
EAA5EEB828ECD24B003B3210 /* Icons.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Icons.xcassets; sourceTree = "<group>"; };
|
||||
EAA5EEDF28F49DB3003B3210 /* Colorable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Colorable.swift; sourceTree = "<group>"; };
|
||||
EAA5EEE328F5B855003B3210 /* VerizonNHGDS-Light.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "VerizonNHGDS-Light.otf"; sourceTree = "<group>"; };
|
||||
EAB1D29928A5611D00DAE764 /* SelectorGroupModelable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectorGroupModelable.swift; sourceTree = "<group>"; };
|
||||
EAA5EEEC28F5C908003B3210 /* VDSTypographyTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSTypographyTokens.xcframework; path = ../SharedFrameworks/VDSTypographyTokens.xcframework; sourceTree = "<group>"; };
|
||||
EAA5EEED28F5C908003B3210 /* VDSColorTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSColorTokens.xcframework; path = ../SharedFrameworks/VDSColorTokens.xcframework; sourceTree = "<group>"; };
|
||||
EAA5EEEE28F5C908003B3210 /* VDSFormControlsTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSFormControlsTokens.xcframework; path = ../SharedFrameworks/VDSFormControlsTokens.xcframework; sourceTree = "<group>"; };
|
||||
EAB1D29B28A5618900DAE764 /* RadioButtonGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButtonGroup.swift; sourceTree = "<group>"; };
|
||||
EAB1D29D28A5619500DAE764 /* RadioButtonGroupModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButtonGroupModel.swift; sourceTree = "<group>"; };
|
||||
EAB1D2A028A598FE00DAE764 /* UsesAutoLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UsesAutoLayout.swift; sourceTree = "<group>"; };
|
||||
EAB1D2A228A5994800DAE764 /* Debuggable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Debuggable.swift; sourceTree = "<group>"; };
|
||||
EAB1D2CC28ABE76000DAE764 /* Withable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Withable.swift; sourceTree = "<group>"; };
|
||||
EAB1D2CE28ABEF2B00DAE764 /* Typography.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Typography.swift; sourceTree = "<group>"; };
|
||||
EAB1D2E328AE842000DAE764 /* Publisher+Bind.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Publisher+Bind.swift"; sourceTree = "<group>"; };
|
||||
EAB1D2E928AE84AA00DAE764 /* UIControlPublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIControlPublisher.swift; sourceTree = "<group>"; };
|
||||
EAC925872911C9DE00091998 /* TextEntryField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextEntryField.swift; sourceTree = "<group>"; };
|
||||
EAC925882911C9DE00091998 /* TextEntryFieldModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextEntryFieldModel.swift; sourceTree = "<group>"; };
|
||||
EAC9258A2911C9DE00091998 /* EntryFieldModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EntryFieldModel.swift; sourceTree = "<group>"; };
|
||||
EAC9258B2911C9DE00091998 /* EntryField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EntryField.swift; sourceTree = "<group>"; };
|
||||
EAD8D2C028BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIGestureRecognizer+Publisher.swift"; sourceTree = "<group>"; };
|
||||
EAF7F0932899861000B287F5 /* Checkbox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Checkbox.swift; sourceTree = "<group>"; };
|
||||
EAF7F0942899861000B287F5 /* CheckboxModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckboxModel.swift; sourceTree = "<group>"; };
|
||||
EAF7F0992899B17200B287F5 /* CATransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CATransaction.swift; sourceTree = "<group>"; };
|
||||
EAF7F09D289AAEC000B287F5 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
|
||||
EAF7F09F289AB7EC00B287F5 /* View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = View.swift; sourceTree = "<group>"; };
|
||||
@ -205,13 +163,7 @@
|
||||
EAF7F0B4289C126F00B287F5 /* UILabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UILabel.swift; sourceTree = "<group>"; };
|
||||
EAF7F0B6289C12A600B287F5 /* UITapGestureRecognizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITapGestureRecognizer.swift; sourceTree = "<group>"; };
|
||||
EAF7F0B8289C139800B287F5 /* ColorConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorConfiguration.swift; sourceTree = "<group>"; };
|
||||
EAF7F0BA289D80ED00B287F5 /* Modelable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modelable.swift; sourceTree = "<group>"; };
|
||||
EAF7F0E8289DB0DA00B287F5 /* VDSFormControlsTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSFormControlsTokens.xcframework; path = ../SharedFrameworks/VDSFormControlsTokens.xcframework; sourceTree = "<group>"; };
|
||||
EAF7F0E9289DB0DA00B287F5 /* VDSColorTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSColorTokens.xcframework; path = ../SharedFrameworks/VDSColorTokens.xcframework; sourceTree = "<group>"; };
|
||||
EAF7F0EA289DB0DA00B287F5 /* VDSAccessibilityTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSAccessibilityTokens.xcframework; path = ../SharedFrameworks/VDSAccessibilityTokens.xcframework; sourceTree = "<group>"; };
|
||||
EAF7F0EB289DB0DA00B287F5 /* VDSLayoutTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSLayoutTokens.xcframework; path = ../SharedFrameworks/VDSLayoutTokens.xcframework; sourceTree = "<group>"; };
|
||||
EAF7F11528A1475A00B287F5 /* RadioButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioButton.swift; sourceTree = "<group>"; };
|
||||
EAF7F11628A1475A00B287F5 /* RadioButtonModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioButtonModel.swift; sourceTree = "<group>"; };
|
||||
EAF7F13228A2A16500B287F5 /* AttachmentLabelAttributeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentLabelAttributeModel.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
@ -220,9 +172,9 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
EAF7F0F8289DB1AC00B287F5 /* VDSFormControlsTokens.xcframework in Frameworks */,
|
||||
EAF7F0F9289DB1AC00B287F5 /* VDSTypographyTokens.xcframework in Frameworks */,
|
||||
EAF7F0FB289DB1AC00B287F5 /* VDSColorTokens.xcframework in Frameworks */,
|
||||
EAA5EEF328F5C909003B3210 /* VDSFormControlsTokens.xcframework in Frameworks */,
|
||||
EAA5EEEF28F5C908003B3210 /* VDSTypographyTokens.xcframework in Frameworks */,
|
||||
EAA5EEF128F5C909003B3210 /* VDSColorTokens.xcframework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -241,7 +193,6 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5FC35BE228D51405004EBEAC /* Button.swift */,
|
||||
5FC35BE428D51413004EBEAC /* ButtonModel.swift */,
|
||||
);
|
||||
path = Button;
|
||||
sourceTree = "<group>";
|
||||
@ -250,9 +201,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EA1F266128B945070033E859 /* RadioSwatch.swift */,
|
||||
EA1F266328B945070033E859 /* RadioSwatchModel.swift */,
|
||||
EA1F266228B945070033E859 /* RadioSwatchGroup.swift */,
|
||||
EA1F266028B945070033E859 /* RadioSwatchGroupModel.swift */,
|
||||
);
|
||||
path = RadioSwatch;
|
||||
sourceTree = "<group>";
|
||||
@ -284,7 +233,6 @@
|
||||
EA3361A6288B23240071C351 /* Extensions */,
|
||||
EA3361DF2891D0F10071C351 /* Fonts */,
|
||||
EA3361AB288B25EC0071C351 /* Protocols */,
|
||||
EAB1D29F28A598D000DAE764 /* PropertyWrappers */,
|
||||
EAB1D2E228AE842000DAE764 /* Publishers */,
|
||||
EAB1D2D028ABEF3100DAE764 /* Typography */,
|
||||
EA3361B9288B2BE30071C351 /* Utilities */,
|
||||
@ -306,13 +254,9 @@
|
||||
EA33618D288B1C0C0071C351 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EAF7F0EA289DB0DA00B287F5 /* VDSAccessibilityTokens.xcframework */,
|
||||
EAF7F0E9289DB0DA00B287F5 /* VDSColorTokens.xcframework */,
|
||||
EAF7F0E8289DB0DA00B287F5 /* VDSFormControlsTokens.xcframework */,
|
||||
EAF7F0EB289DB0DA00B287F5 /* VDSLayoutTokens.xcframework */,
|
||||
EA3361F32891D5290071C351 /* VDSTypographyTokens.xcframework */,
|
||||
EA33618E288B1C0C0071C351 /* VDSColorTokens.xcframework */,
|
||||
EA33618F288B1C0C0071C351 /* VDSFormControlsTokens.xcframework */,
|
||||
EAA5EEED28F5C908003B3210 /* VDSColorTokens.xcframework */,
|
||||
EAA5EEEE28F5C908003B3210 /* VDSFormControlsTokens.xcframework */,
|
||||
EAA5EEEC28F5C908003B3210 /* VDSTypographyTokens.xcframework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
@ -327,7 +271,7 @@
|
||||
EA89200B28B530F0006B9984 /* RadioBox */,
|
||||
EAF7F11428A1470D00B287F5 /* RadioButton */,
|
||||
EA1F265F28B945070033E859 /* RadioSwatch */,
|
||||
EAA5EEA728EB3EBC003B3210 /* TextFields */,
|
||||
EAC925852911C9DE00091998 /* TextFields */,
|
||||
EA3361A0288B1E6F0071C351 /* Toggle */,
|
||||
);
|
||||
path = Components;
|
||||
@ -337,7 +281,6 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EA3361C228902D960071C351 /* Toggle.swift */,
|
||||
EA3361A1288B1E840071C351 /* ToggleModel.swift */,
|
||||
);
|
||||
path = Toggle;
|
||||
sourceTree = "<group>";
|
||||
@ -365,13 +308,9 @@
|
||||
EA3361A9288B25E40071C351 /* Disabling.swift */,
|
||||
EAF7F0A1289AFB3900B287F5 /* Errorable.swift */,
|
||||
EA3361AE288B26310071C351 /* FormFieldable.swift */,
|
||||
EA3361BE288B2EA60071C351 /* Handlerable.swift */,
|
||||
EA33624628931B050071C351 /* Initable.swift */,
|
||||
EA3362442892F9130071C351 /* Labelable.swift */,
|
||||
EAF7F0BA289D80ED00B287F5 /* Modelable.swift */,
|
||||
EA3361BE288B2EA60071C351 /* ModelHandlerable.swift */,
|
||||
EAF7F0A5289B0CE000B287F5 /* Resetable.swift */,
|
||||
EA89200E28B53921006B9984 /* Selectable.swift */,
|
||||
EAB1D29928A5611D00DAE764 /* SelectorGroupModelable.swift */,
|
||||
EA3361C8289054C50071C351 /* Surfaceable.swift */,
|
||||
EA3361B7288B2AAA0071C351 /* ViewProtocol.swift */,
|
||||
EAB1D2CC28ABE76000DAE764 /* Withable.swift */,
|
||||
@ -383,8 +322,6 @@
|
||||
EA3361B4288B2A360071C351 /* Classes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EA1F265B28B944F00033E859 /* CollectionView.swift */,
|
||||
EA1F265C28B944F00033E859 /* CollectionViewCell.swift */,
|
||||
EAF7F0B8289C139800B287F5 /* ColorConfiguration.swift */,
|
||||
EAF7F09D289AAEC000B287F5 /* Constants.swift */,
|
||||
EA3361B5288B2A410071C351 /* Control.swift */,
|
||||
@ -436,7 +373,6 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EA33623F2892EF6B0071C351 /* Label.swift */,
|
||||
EA3362422892EFF20071C351 /* LabelModel.swift */,
|
||||
EAF7F0A9289B13EF00B287F5 /* Attributes */,
|
||||
);
|
||||
path = Label;
|
||||
@ -446,7 +382,6 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EA4DB30128DCBCA500103EE3 /* Badge.swift */,
|
||||
EA4DB2FF28DCBC9900103EE3 /* BadgeModel.swift */,
|
||||
);
|
||||
path = Badge;
|
||||
sourceTree = "<group>";
|
||||
@ -455,51 +390,11 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EA89201228B568D8006B9984 /* RadioBox.swift */,
|
||||
EA89200C28B530FD006B9984 /* RadioBoxModel.swift */,
|
||||
EA89201428B56CF4006B9984 /* RadioBoxGroup.swift */,
|
||||
EA89201628B56CFF006B9984 /* RadioBoxGroupModel.swift */,
|
||||
);
|
||||
path = RadioBox;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
EAA5EEA728EB3EBC003B3210 /* TextFields */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EAA5EEAF28EB6A49003B3210 /* TextEntryField */,
|
||||
EAA5EEAE28EB6A3D003B3210 /* EntryField */,
|
||||
);
|
||||
path = TextFields;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
EAA5EEAE28EB6A3D003B3210 /* EntryField */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EAA5EEA828EB3ED0003B3210 /* EntryField.swift */,
|
||||
EAA5EEAA28EB3ED9003B3210 /* EntryFieldModel.swift */,
|
||||
);
|
||||
path = EntryField;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
EAA5EEAF28EB6A49003B3210 /* TextEntryField */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EAA5EEB228EB6A66003B3210 /* TextEntryField.swift */,
|
||||
EAA5EEB028EB6A5A003B3210 /* TextEntryFieldModel.swift */,
|
||||
);
|
||||
path = TextEntryField;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
EAB1D29F28A598D000DAE764 /* PropertyWrappers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EA3C3B4B2894823E000CA526 /* AnyProxy.swift */,
|
||||
EA84F6B028B94A2500D67ABC /* CodableColor.swift */,
|
||||
EAB1D2A228A5994800DAE764 /* Debuggable.swift */,
|
||||
EAB1D2A028A598FE00DAE764 /* UsesAutoLayout.swift */,
|
||||
);
|
||||
path = PropertyWrappers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
EAB1D2D028ABEF3100DAE764 /* Typography */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -520,13 +415,38 @@
|
||||
path = Publishers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
EAC925852911C9DE00091998 /* TextFields */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EAC925862911C9DE00091998 /* TextEntryField */,
|
||||
EAC925892911C9DE00091998 /* EntryField */,
|
||||
);
|
||||
path = TextFields;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
EAC925862911C9DE00091998 /* TextEntryField */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EAC925872911C9DE00091998 /* TextEntryField.swift */,
|
||||
EAC925882911C9DE00091998 /* TextEntryFieldModel.swift */,
|
||||
);
|
||||
path = TextEntryField;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
EAC925892911C9DE00091998 /* EntryField */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EAC9258A2911C9DE00091998 /* EntryFieldModel.swift */,
|
||||
EAC9258B2911C9DE00091998 /* EntryField.swift */,
|
||||
);
|
||||
path = EntryField;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
EAF7F092289985E200B287F5 /* Checkbox */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EAF7F0932899861000B287F5 /* Checkbox.swift */,
|
||||
EAF7F0942899861000B287F5 /* CheckboxModel.swift */,
|
||||
EA89200528B526D6006B9984 /* CheckboxGroup.swift */,
|
||||
EA89200728B526E0006B9984 /* CheckboxGroupModel.swift */,
|
||||
);
|
||||
path = Checkbox;
|
||||
sourceTree = "<group>";
|
||||
@ -539,10 +459,10 @@
|
||||
EAF7F13228A2A16500B287F5 /* AttachmentLabelAttributeModel.swift */,
|
||||
EAF7F0B0289B177F00B287F5 /* ColorLabelAttribute.swift */,
|
||||
EAF7F0AA289B13FD00B287F5 /* FontLabelAttribute.swift */,
|
||||
EAF7F0AC289B142900B287F5 /* StrikeThroughLabelAttribute.swift */,
|
||||
EAA5EEB428ECBFB4003B3210 /* ImageLabelAttribute.swift */,
|
||||
EAF7F0AE289B144C00B287F5 /* UnderlineLabelAttribute.swift */,
|
||||
EAF7F0AC289B142900B287F5 /* StrikeThroughLabelAttribute.swift */,
|
||||
EAA5EEB628ECC03A003B3210 /* ToolTipLabelAttribute.swift */,
|
||||
EAF7F0AE289B144C00B287F5 /* UnderlineLabelAttribute.swift */,
|
||||
);
|
||||
path = Attributes;
|
||||
sourceTree = "<group>";
|
||||
@ -551,9 +471,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EAF7F11528A1475A00B287F5 /* RadioButton.swift */,
|
||||
EAF7F11628A1475A00B287F5 /* RadioButtonModel.swift */,
|
||||
EAB1D29B28A5618900DAE764 /* RadioButtonGroup.swift */,
|
||||
EAB1D29D28A5619500DAE764 /* RadioButtonGroupModel.swift */,
|
||||
);
|
||||
path = RadioButton;
|
||||
sourceTree = "<group>";
|
||||
@ -678,36 +596,27 @@
|
||||
EA89200428AECF4B006B9984 /* UITextField+Publisher.swift in Sources */,
|
||||
EA3361C328902D960071C351 /* Toggle.swift in Sources */,
|
||||
EAF7F0A0289AB7EC00B287F5 /* View.swift in Sources */,
|
||||
EAF7F11828A1475A00B287F5 /* RadioButtonModel.swift in Sources */,
|
||||
EA89200D28B530FD006B9984 /* RadioBoxModel.swift in Sources */,
|
||||
EA1F266728B945070033E859 /* RadioSwatchModel.swift in Sources */,
|
||||
EAA5EEB328EB6A66003B3210 /* TextEntryField.swift in Sources */,
|
||||
EA89201328B568D8006B9984 /* RadioBox.swift in Sources */,
|
||||
EA84F6B128B94A2500D67ABC /* CodableColor.swift in Sources */,
|
||||
EAA5EEE028F49DB3003B3210 /* Colorable.swift in Sources */,
|
||||
EAC9258C2911C9DE00091998 /* TextEntryField.swift in Sources */,
|
||||
EA3362402892EF6C0071C351 /* Label.swift in Sources */,
|
||||
EAC9258D2911C9DE00091998 /* TextEntryFieldModel.swift in Sources */,
|
||||
EAF7F0B3289B1ADC00B287F5 /* ActionLabelAttribute.swift in Sources */,
|
||||
EA33622E2891EA3C0071C351 /* DispatchQueue+Once.swift in Sources */,
|
||||
EA4DB2FD28D3D0CA00103EE3 /* AnyEquatable.swift in Sources */,
|
||||
EAA5EEB728ECC03A003B3210 /* ToolTipLabelAttribute.swift in Sources */,
|
||||
EAF7F0AF289B144C00B287F5 /* UnderlineLabelAttribute.swift in Sources */,
|
||||
EAC925842911C63100091998 /* Colorable.swift in Sources */,
|
||||
EA3361C5289030FC0071C351 /* Accessable.swift in Sources */,
|
||||
EA33622C2891E73B0071C351 /* FontProtocol.swift in Sources */,
|
||||
EAA5EEAB28EB3ED9003B3210 /* EntryFieldModel.swift in Sources */,
|
||||
EAA5EEA928EB3ED0003B3210 /* EntryField.swift in Sources */,
|
||||
EAF7F11728A1475A00B287F5 /* RadioButton.swift in Sources */,
|
||||
EAB1D2A128A598FE00DAE764 /* UsesAutoLayout.swift in Sources */,
|
||||
EAB1D2CD28ABE76100DAE764 /* Withable.swift in Sources */,
|
||||
EAF7F0952899861000B287F5 /* Checkbox.swift in Sources */,
|
||||
EA89201728B56CFF006B9984 /* RadioBoxGroupModel.swift in Sources */,
|
||||
EA3361C9289054C50071C351 /* Surfaceable.swift in Sources */,
|
||||
EA3361A2288B1E840071C351 /* ToggleModel.swift in Sources */,
|
||||
EA1F265D28B944F00033E859 /* CollectionView.swift in Sources */,
|
||||
EA4DB30228DCBCA500103EE3 /* Badge.swift in Sources */,
|
||||
EA3362432892EFF20071C351 /* LabelModel.swift in Sources */,
|
||||
EA33624728931B050071C351 /* Initable.swift in Sources */,
|
||||
EAF7F0A4289B017C00B287F5 /* LabelAttributeModel.swift in Sources */,
|
||||
EAF7F0B1289B177F00B287F5 /* ColorLabelAttribute.swift in Sources */,
|
||||
EAC9258F2911C9DE00091998 /* EntryField.swift in Sources */,
|
||||
EAB1D2EA28AE84AA00DAE764 /* UIControlPublisher.swift in Sources */,
|
||||
EAF7F13328A2A16500B287F5 /* AttachmentLabelAttributeModel.swift in Sources */,
|
||||
EA89200628B526D6006B9984 /* CheckboxGroup.swift in Sources */,
|
||||
@ -716,44 +625,30 @@
|
||||
EA3361BD288B2C760071C351 /* TypeAlias.swift in Sources */,
|
||||
EAB1D2CF28ABEF2B00DAE764 /* Typography.swift in Sources */,
|
||||
EAF7F09A2899B17200B287F5 /* CATransaction.swift in Sources */,
|
||||
EA89200F28B53921006B9984 /* Selectable.swift in Sources */,
|
||||
EAB1D29E28A5619500DAE764 /* RadioButtonGroupModel.swift in Sources */,
|
||||
EAF7F0A2289AFB3900B287F5 /* Errorable.swift in Sources */,
|
||||
EA3C3B4C2894823E000CA526 /* AnyProxy.swift in Sources */,
|
||||
5FC35BE528D51414004EBEAC /* ButtonModel.swift in Sources */,
|
||||
EA3361AF288B26310071C351 /* FormFieldable.swift in Sources */,
|
||||
EAB1D29A28A5611D00DAE764 /* SelectorGroupModelable.swift in Sources */,
|
||||
EAF7F0BB289D80ED00B287F5 /* Modelable.swift in Sources */,
|
||||
EAA5EEB128EB6A5A003B3210 /* TextEntryFieldModel.swift in Sources */,
|
||||
EA89201528B56CF4006B9984 /* RadioBoxGroup.swift in Sources */,
|
||||
EA4DB30028DCBC9900103EE3 /* BadgeModel.swift in Sources */,
|
||||
EAF7F09E289AAEC000B287F5 /* Constants.swift in Sources */,
|
||||
EA1F266528B945070033E859 /* RadioSwatch.swift in Sources */,
|
||||
EA4DB18528CA967F00103EE3 /* SelectorGroupHandlerBase.swift in Sources */,
|
||||
EAC9258E2911C9DE00091998 /* EntryFieldModel.swift in Sources */,
|
||||
EA89200228AECF2A006B9984 /* UIButton+Publisher.swift in Sources */,
|
||||
EAF7F0AB289B13FD00B287F5 /* FontLabelAttribute.swift in Sources */,
|
||||
EAB1D29C28A5618900DAE764 /* RadioButtonGroup.swift in Sources */,
|
||||
EA336171288B19200071C351 /* VDS.docc in Sources */,
|
||||
EAA5EEB528ECBFB4003B3210 /* ImageLabelAttribute.swift in Sources */,
|
||||
EAF7F0962899861000B287F5 /* CheckboxModel.swift in Sources */,
|
||||
EAB1D2E628AE842000DAE764 /* Publisher+Bind.swift in Sources */,
|
||||
EA3361AA288B25E40071C351 /* Disabling.swift in Sources */,
|
||||
EA89200828B526E0006B9984 /* CheckboxGroupModel.swift in Sources */,
|
||||
EA3361B6288B2A410071C351 /* Control.swift in Sources */,
|
||||
EAB1D2A328A5994800DAE764 /* Debuggable.swift in Sources */,
|
||||
5F21D7BF28DCEB3D003E7CD6 /* Useable.swift in Sources */,
|
||||
EAF7F0B7289C12A600B287F5 /* UITapGestureRecognizer.swift in Sources */,
|
||||
EA3362452892F9130071C351 /* Labelable.swift in Sources */,
|
||||
EA3361AD288B26190071C351 /* DataTrackable.swift in Sources */,
|
||||
EAA5EEE228F4C62C003B3210 /* Colorable.swift in Sources */,
|
||||
EA33623E2892EE950071C351 /* UIDevice.swift in Sources */,
|
||||
EA3362302891EB4A0071C351 /* Fonts.swift in Sources */,
|
||||
EA1F265E28B944F00033E859 /* CollectionViewCell.swift in Sources */,
|
||||
EAF7F0AD289B142900B287F5 /* StrikeThroughLabelAttribute.swift in Sources */,
|
||||
EA3361B8288B2AAA0071C351 /* ViewProtocol.swift in Sources */,
|
||||
EA3361BF288B2EA60071C351 /* ModelHandlerable.swift in Sources */,
|
||||
EA3361BF288B2EA60071C351 /* Handlerable.swift in Sources */,
|
||||
EA3361A8288B23300071C351 /* UIColor.swift in Sources */,
|
||||
EA1F266428B945070033E859 /* RadioSwatchGroupModel.swift in Sources */,
|
||||
EA1F266628B945070033E859 /* RadioSwatchGroup.swift in Sources */,
|
||||
5FC35BE328D51405004EBEAC /* Button.swift in Sources */,
|
||||
);
|
||||
@ -901,6 +796,7 @@
|
||||
EA336181288B19210071C351 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEFINES_MODULE = YES;
|
||||
@ -908,6 +804,7 @@
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../../SharedFrameworks";
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
@ -923,7 +820,7 @@
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_INSTALL_OBJC_HEADER = NO;
|
||||
SWIFT_INSTALL_OBJC_HEADER = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
@ -932,6 +829,7 @@
|
||||
EA336182288B19210071C351 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEFINES_MODULE = YES;
|
||||
@ -939,6 +837,7 @@
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../../SharedFrameworks";
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
@ -954,7 +853,7 @@
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_INSTALL_OBJC_HEADER = NO;
|
||||
SWIFT_INSTALL_OBJC_HEADER = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
|
||||
@ -1,110 +0,0 @@
|
||||
//
|
||||
// CollectionView.swift
|
||||
// VDS
|
||||
//
|
||||
// Created by Matt Bruce on 8/25/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Combine
|
||||
|
||||
open class CollectionView<ModelType: Modelable>: UICollectionView, ModelHandlerable, ViewProtocol, Resettable {
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Combine Properties
|
||||
//--------------------------------------------------
|
||||
@Published public var model: ModelType = ModelType()
|
||||
public var modelPublisher: Published<ModelType>.Publisher { $model }
|
||||
public var subscribers = Set<AnyCancellable>()
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
private var initialSetupPerformed = false
|
||||
|
||||
@Proxy(\.model.surface)
|
||||
open var surface: Surface
|
||||
|
||||
@Proxy(\.model.disabled)
|
||||
open var disabled: Bool {
|
||||
didSet {
|
||||
self.isEnabled = !disabled
|
||||
}
|
||||
}
|
||||
|
||||
open var isEnabled: Bool {
|
||||
get { !model.disabled }
|
||||
set {
|
||||
//create local vars for clear coding
|
||||
let disabled = !newValue
|
||||
if model.disabled != disabled {
|
||||
model.disabled = disabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializers
|
||||
//--------------------------------------------------
|
||||
required public init() {
|
||||
super.init(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
|
||||
initialSetup()
|
||||
}
|
||||
|
||||
public required init(with model: ModelType) {
|
||||
super.init(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
|
||||
initialSetup()
|
||||
set(with: model)
|
||||
}
|
||||
|
||||
public required init(with model: ModelType, collectionViewLayout layout: UICollectionViewLayout) {
|
||||
super.init(frame: .zero, collectionViewLayout: layout)
|
||||
initialSetup()
|
||||
set(with: model)
|
||||
}
|
||||
|
||||
public override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
|
||||
super.init(frame: frame, collectionViewLayout: layout)
|
||||
initialSetup()
|
||||
set(with: model)
|
||||
}
|
||||
|
||||
public required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
initialSetup()
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Setup
|
||||
//--------------------------------------------------
|
||||
|
||||
public func initialSetup() {
|
||||
if !initialSetupPerformed {
|
||||
initialSetupPerformed = true
|
||||
setupUpdateView()
|
||||
setup()
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Overrides
|
||||
//--------------------------------------------------
|
||||
open func updateView(viewModel: ModelType) {
|
||||
fatalError("Implement updateView")
|
||||
}
|
||||
|
||||
open func reset() {
|
||||
backgroundColor = .clear
|
||||
if let model = model as? Resettable {
|
||||
model.reset()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ViewProtocol
|
||||
/// Will be called only once.
|
||||
open func setup() {
|
||||
translatesAutoresizingMaskIntoConstraints = false
|
||||
insetsLayoutMarginsFromSafeArea = false
|
||||
}
|
||||
}
|
||||
@ -1,97 +0,0 @@
|
||||
//
|
||||
// CollectionViewCell.swift
|
||||
// VDS
|
||||
//
|
||||
// Created by Matt Bruce on 8/25/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Combine
|
||||
|
||||
open class CollectionViewCell<ModelHandlerType: ModelHandlerable & UIView>: UICollectionViewCell, ViewProtocol {
|
||||
public typealias ModelType = ModelHandlerType.ModelType
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
private var initialSetupPerformed = false
|
||||
|
||||
public var modelHandler: ModelHandlerType = ModelHandlerType()
|
||||
|
||||
@Proxy(\.modelHandler.model.surface)
|
||||
open var surface: Surface
|
||||
|
||||
@Proxy(\.modelHandler.model.disabled)
|
||||
open var disabled: Bool
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializers
|
||||
//--------------------------------------------------
|
||||
required public init() {
|
||||
super.init(frame: .zero)
|
||||
initialSetup()
|
||||
}
|
||||
|
||||
public required init(with model: ModelType) {
|
||||
super.init(frame: .zero)
|
||||
initialSetup()
|
||||
set(with: model)
|
||||
}
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
initialSetup()
|
||||
}
|
||||
|
||||
public required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
initialSetup()
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Setup
|
||||
//--------------------------------------------------
|
||||
|
||||
public func initialSetup() {
|
||||
if !initialSetupPerformed {
|
||||
initialSetupPerformed = true
|
||||
setup()
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Overrides
|
||||
//--------------------------------------------------
|
||||
open func shouldUpdateView(viewModel: ModelType) -> Bool {
|
||||
return modelHandler.shouldUpdateView(viewModel: viewModel)
|
||||
}
|
||||
|
||||
open func updateView(viewModel: ModelType) {
|
||||
modelHandler.updateView(viewModel: viewModel)
|
||||
}
|
||||
|
||||
public func set(with model: ModelType) {
|
||||
modelHandler.set(with: model)
|
||||
}
|
||||
|
||||
// MARK: - ViewProtocol
|
||||
/// Will be called only once.
|
||||
open func setup() {
|
||||
translatesAutoresizingMaskIntoConstraints = false
|
||||
insetsLayoutMarginsFromSafeArea = false
|
||||
|
||||
addSubview(modelHandler)
|
||||
|
||||
modelHandler.topAnchor.constraint(equalTo: topAnchor).isActive = true
|
||||
modelHandler.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
|
||||
modelHandler.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
|
||||
modelHandler.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
|
||||
}
|
||||
|
||||
open override func prepareForReuse() {
|
||||
super.prepareForReuse()
|
||||
if let handler = modelHandler as? Resettable {
|
||||
handler.reset()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -23,13 +23,13 @@ public typealias ObjectColorable = Colorable & Initable & ObjectWithable
|
||||
///
|
||||
/// let textColor = config.getColor(model) //returns .black
|
||||
final public class SurfaceColorConfiguration: ObjectColorable {
|
||||
public typealias ModelType = Surfaceable
|
||||
public typealias ObjectType = Surfaceable
|
||||
public var lightColor: UIColor = .clear
|
||||
public var darkColor: UIColor = .clear
|
||||
|
||||
required public init(){}
|
||||
public func getColor(_ viewModel: any ModelType) -> UIColor {
|
||||
return viewModel.surface == .light ? lightColor : darkColor
|
||||
public func getColor(_ object: any ObjectType) -> UIColor {
|
||||
return object.surface == .light ? lightColor : darkColor
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,8 +42,8 @@ public protocol DisabledSurfaceColorable: ObjectColorable {
|
||||
}
|
||||
|
||||
extension DisabledSurfaceColorable {
|
||||
public func getDisabledColor<M: Surfaceable & Disabling> (_ viewModel: M) -> UIColor {
|
||||
viewModel.disabled ? disabled.getColor(viewModel) : enabled.getColor(viewModel)
|
||||
public func getDisabledColor<M: Surfaceable & Disabling> (_ object: M) -> UIColor {
|
||||
object.disabled ? disabled.getColor(object) : enabled.getColor(object)
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,14 +69,14 @@ extension DisabledSurfaceColorable {
|
||||
///
|
||||
///
|
||||
final public class DisabledSurfaceColorConfiguration: DisabledSurfaceColorable {
|
||||
public typealias ModelType = Surfaceable & Disabling
|
||||
public typealias ObjectType = Surfaceable & Disabling
|
||||
public var disabled = SurfaceColorConfiguration()
|
||||
public var enabled = SurfaceColorConfiguration()
|
||||
|
||||
required public init(){}
|
||||
|
||||
public func getColor(_ viewModel: any ModelType) -> UIColor {
|
||||
getDisabledColor(viewModel)
|
||||
public func getColor(_ object: any ObjectType) -> UIColor {
|
||||
getDisabledColor(object)
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,11 +84,11 @@ final public class DisabledSurfaceColorConfiguration: DisabledSurfaceColorable {
|
||||
///MARK -- BinaryColorable
|
||||
///-------------------------------------------------------------------
|
||||
public protocol BinaryColorable{
|
||||
var userTrueColor: Bool { get }
|
||||
var useTrueColor: Bool { get }
|
||||
}
|
||||
|
||||
extension BinaryColorable where Self: Selectable {
|
||||
public var userTrueColor: Bool { return selected }
|
||||
extension BinaryColorable where Self: Control {
|
||||
public var useTrueColor: Bool { return isSelected }
|
||||
}
|
||||
|
||||
///-------------------------------------------------------------------
|
||||
@ -100,8 +100,8 @@ public protocol BinarySurfaceColorable: ObjectColorable {
|
||||
}
|
||||
|
||||
extension BinarySurfaceColorable {
|
||||
public func getBinaryColor<M: Surfaceable & BinaryColorable>(_ viewModel: M) -> UIColor {
|
||||
viewModel.userTrueColor ? forTrue.getColor(viewModel) : forFalse.getColor(viewModel)
|
||||
public func getBinaryColor<M: Surfaceable & BinaryColorable>(_ object: M) -> UIColor {
|
||||
object.useTrueColor ? forTrue.getColor(object) : forFalse.getColor(object)
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,14 +126,14 @@ extension BinarySurfaceColorable {
|
||||
///
|
||||
///
|
||||
final public class BinarySurfaceColorConfiguration: BinarySurfaceColorable {
|
||||
public typealias ModelType = Surfaceable & BinaryColorable
|
||||
public typealias ObjectType = Surfaceable & BinaryColorable
|
||||
public var forTrue = SurfaceColorConfiguration()
|
||||
public var forFalse = SurfaceColorConfiguration()
|
||||
|
||||
required public init(){}
|
||||
|
||||
public func getColor(_ viewModel: any ModelType) -> UIColor {
|
||||
getBinaryColor(viewModel)
|
||||
public func getColor(_ object: any ObjectType) -> UIColor {
|
||||
getBinaryColor(object)
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,8 +147,8 @@ public protocol BinaryDisabledSurfaceColorable: ObjectColorable {
|
||||
}
|
||||
|
||||
extension BinaryDisabledSurfaceColorable {
|
||||
public func getBinaryColor<M: Disabling & Surfaceable & BinaryColorable>(_ viewModel: M) -> UIColor {
|
||||
viewModel.userTrueColor ? forTrue.getColor(viewModel) : forFalse.getColor(viewModel)
|
||||
public func getBinaryColor<M: Disabling & Surfaceable & BinaryColorable>(_ object: M) -> UIColor {
|
||||
object.useTrueColor ? forTrue.getColor(object) : forFalse.getColor(object)
|
||||
}
|
||||
}
|
||||
|
||||
@ -179,13 +179,13 @@ extension BinaryDisabledSurfaceColorable {
|
||||
///
|
||||
///
|
||||
final public class BinaryDisabledSurfaceColorConfiguration: BinaryDisabledSurfaceColorable {
|
||||
public typealias ModelType = Disabling & Surfaceable & BinaryColorable
|
||||
public typealias ObjectType = Disabling & Surfaceable & BinaryColorable
|
||||
public var forTrue = DisabledSurfaceColorConfiguration()
|
||||
public var forFalse = DisabledSurfaceColorConfiguration()
|
||||
|
||||
required public init(){}
|
||||
|
||||
public func getColor(_ viewModel: any ModelType) -> UIColor {
|
||||
getBinaryColor(viewModel)
|
||||
public func getColor(_ object: any ObjectType) -> UIColor {
|
||||
getBinaryColor(object)
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,6 +8,6 @@
|
||||
import Foundation
|
||||
|
||||
public struct Constants {
|
||||
public static let ModelStateDebounce = 0.02
|
||||
public static let StateDebounce = 0.0 //.001
|
||||
public static let PaddingOne = 10.0
|
||||
}
|
||||
|
||||
@ -9,44 +9,34 @@ import Foundation
|
||||
import UIKit
|
||||
import Combine
|
||||
|
||||
|
||||
open class Control<ModelType: Modelable>: UIControl, ModelHandlerable, ViewProtocol, Resettable {
|
||||
@objc(VDSControl)
|
||||
open class Control: UIControl, Handlerable, ViewProtocol, Resettable {
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Combine Properties
|
||||
//--------------------------------------------------
|
||||
@Published public var model: ModelType = ModelType()
|
||||
public var modelPublisher: Published<ModelType>.Publisher { $model }
|
||||
public var subject = PassthroughSubject<Void, Never>()
|
||||
public var subscribers = Set<AnyCancellable>()
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
private var initialSetupPerformed = false
|
||||
|
||||
open var surface: Surface = .light { didSet { didChange() } }
|
||||
|
||||
//if set to true this will call the
|
||||
//defaultAction() in the class
|
||||
public var executeDefaultAction = true
|
||||
|
||||
@Proxy(\.model.surface)
|
||||
open var surface: Surface
|
||||
|
||||
@Proxy(\.model.disabled)
|
||||
open var disabled: Bool {
|
||||
didSet {
|
||||
self.isEnabled = !disabled
|
||||
}
|
||||
}
|
||||
open var disabled: Bool = false { didSet { isEnabled = !disabled } }
|
||||
|
||||
open override var isSelected: Bool { didSet { didChange() } }
|
||||
|
||||
open override var isEnabled: Bool {
|
||||
get { !model.disabled }
|
||||
get { !disabled }
|
||||
set {
|
||||
//create local vars for clear coding
|
||||
let disabled = !newValue
|
||||
if model.disabled != disabled {
|
||||
model.disabled = disabled
|
||||
if disabled != !newValue {
|
||||
disabled = !newValue
|
||||
}
|
||||
isUserInteractionEnabled = isEnabled
|
||||
didChange()
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,12 +48,6 @@ open class Control<ModelType: Modelable>: UIControl, ModelHandlerable, ViewProto
|
||||
initialSetup()
|
||||
}
|
||||
|
||||
public required init(with model: ModelType) {
|
||||
super.init(frame: .zero)
|
||||
initialSetup()
|
||||
set(with: model)
|
||||
}
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
super.init(frame: .zero)
|
||||
initialSetup()
|
||||
@ -78,10 +62,9 @@ open class Control<ModelType: Modelable>: UIControl, ModelHandlerable, ViewProto
|
||||
// MARK: - Setup
|
||||
//--------------------------------------------------
|
||||
|
||||
public func initialSetup() {
|
||||
open func initialSetup() {
|
||||
if !initialSetupPerformed {
|
||||
initialSetupPerformed = true
|
||||
setupUpdateView()
|
||||
setup()
|
||||
}
|
||||
}
|
||||
@ -96,15 +79,17 @@ open class Control<ModelType: Modelable>: UIControl, ModelHandlerable, ViewProto
|
||||
//--------------------------------------------------
|
||||
// MARK: - Overrides
|
||||
//--------------------------------------------------
|
||||
open func updateView(viewModel: ModelType) {
|
||||
fatalError("Implement updateView")
|
||||
open override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
updateView()
|
||||
}
|
||||
|
||||
open func updateView() {}
|
||||
|
||||
open func reset() {
|
||||
backgroundColor = .clear
|
||||
if let model = model as? Resettable {
|
||||
model.reset()
|
||||
}
|
||||
surface = .light
|
||||
disabled = false
|
||||
}
|
||||
|
||||
// MARK: - ViewProtocol
|
||||
|
||||
@ -8,12 +8,12 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
public class SelectorGroupHandlerBase<GroupModelType: SelectorGroupModelable, ModelHandlerType: Control<GroupModelType.SelectorModelType>>: Control<GroupModelType> {
|
||||
public class SelectorGroupHandlerBase<HandlerType: Control>: Control {
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Public Properties
|
||||
//--------------------------------------------------
|
||||
public var selectorViews: [ModelHandlerType] = []
|
||||
public var selectorViews: [HandlerType] = []
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Overrides
|
||||
@ -34,68 +34,19 @@ public class SelectorGroupHandlerBase<GroupModelType: SelectorGroupModelable, Mo
|
||||
}
|
||||
}
|
||||
|
||||
public func findSelectorView(id: UUID?) -> ModelHandlerType? {
|
||||
return selectorViews.first(where: { existingSelectorView in
|
||||
return existingSelectorView.model.id == id
|
||||
})
|
||||
}
|
||||
|
||||
public func getCachedSelector(viewModel: ModelHandlerType.ModelType) -> ModelHandlerType.ModelType? {
|
||||
if let index = model.selectors.firstIndex(where: { element in
|
||||
return element.id == viewModel.id
|
||||
}) {
|
||||
return model.selectors[index]
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public func replace(viewModel: ModelHandlerType.ModelType){
|
||||
if let index = model.selectors.firstIndex(where: { element in
|
||||
return element.id == viewModel.id
|
||||
}) {
|
||||
model.selectors[index] = viewModel
|
||||
}
|
||||
}
|
||||
|
||||
public func createModelHandler(selector: ModelHandlerType.ModelType) -> ModelHandlerType {
|
||||
//create view
|
||||
let newSelectorView = ModelHandlerType(with: selector)
|
||||
|
||||
//add model update to the subscribers
|
||||
newSelectorView
|
||||
.modelPublisher
|
||||
.sink { [weak self] model in
|
||||
self?.replace(viewModel: model)
|
||||
}
|
||||
.store(in: &subscribers)
|
||||
|
||||
//add the selectedPublisher for the change
|
||||
newSelectorView
|
||||
.publisher(for: .touchUpInside)
|
||||
.sink { [weak self] control in
|
||||
self?.didSelect(control)
|
||||
}
|
||||
.store(in: &subscribers)
|
||||
|
||||
return newSelectorView
|
||||
|
||||
}
|
||||
|
||||
open func didSelect(_ selectedControl: ModelHandlerType) {
|
||||
open func didSelect(_ selectedControl: HandlerType) {
|
||||
fatalError("Must override didSelect")
|
||||
}
|
||||
|
||||
public func valueChanged() {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + Constants.ModelStateDebounce) { [weak self] in
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + Constants.StateDebounce) { [weak self] in
|
||||
self?.sendActions(for: .valueChanged)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class SelectorGroupSelectedHandlerBase<GroupModelType: SelectorGroupSelectedModelable, ModelHandlerType: Control<GroupModelType.SelectorModelType>>: SelectorGroupHandlerBase<GroupModelType, ModelHandlerType> where GroupModelType.SelectorModelType == ModelHandlerType.ModelType {
|
||||
|
||||
public var selectedModel: ModelHandlerType.ModelType? {
|
||||
return model.selectedModel
|
||||
public class SelectorGroupSelectedHandlerBase<HandlerType: Control>: SelectorGroupHandlerBase<HandlerType>{
|
||||
public var selectedHandler: HandlerType? {
|
||||
return selectorViews.filter { $0.isSelected == true }.first
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,39 +10,32 @@ import UIKit
|
||||
import Combine
|
||||
|
||||
|
||||
open class View<ModelType: Modelable>: UIView, ModelHandlerable, ViewProtocol, Resettable {
|
||||
@objc(VDSView)
|
||||
open class View: UIView, Handlerable, ViewProtocol, Resettable {
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Combine Properties
|
||||
//--------------------------------------------------
|
||||
@Published public var model: ModelType = ModelType()
|
||||
public var modelPublisher: Published<ModelType>.Publisher { $model }
|
||||
public var subject = PassthroughSubject<Void, Never>()
|
||||
public var subscribers = Set<AnyCancellable>()
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
private var initialSetupPerformed = false
|
||||
|
||||
@Proxy(\.model.surface)
|
||||
open var surface: Surface
|
||||
|
||||
@Proxy(\.model.disabled)
|
||||
open var disabled: Bool {
|
||||
didSet {
|
||||
self.isEnabled = !disabled
|
||||
}
|
||||
}
|
||||
open var surface: Surface = .light { didSet { didChange() }}
|
||||
|
||||
open var disabled: Bool = false { didSet { isEnabled = !disabled } }
|
||||
|
||||
open var isEnabled: Bool {
|
||||
get { !model.disabled }
|
||||
get { !disabled }
|
||||
set {
|
||||
//create local vars for clear coding
|
||||
let disabled = !newValue
|
||||
if model.disabled != disabled {
|
||||
model.disabled = disabled
|
||||
if disabled != !newValue {
|
||||
disabled = !newValue
|
||||
}
|
||||
isUserInteractionEnabled = isEnabled
|
||||
didChange()
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,12 +46,6 @@ open class View<ModelType: Modelable>: UIView, ModelHandlerable, ViewProtocol, R
|
||||
super.init(frame: .zero)
|
||||
initialSetup()
|
||||
}
|
||||
|
||||
public required init(with model: ModelType) {
|
||||
super.init(frame: .zero)
|
||||
initialSetup()
|
||||
set(with: model)
|
||||
}
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
super.init(frame: .zero)
|
||||
@ -74,26 +61,27 @@ open class View<ModelType: Modelable>: UIView, ModelHandlerable, ViewProtocol, R
|
||||
// MARK: - Setup
|
||||
//--------------------------------------------------
|
||||
|
||||
public func initialSetup() {
|
||||
open func initialSetup() {
|
||||
if !initialSetupPerformed {
|
||||
initialSetupPerformed = true
|
||||
setupUpdateView()
|
||||
setup()
|
||||
}
|
||||
}
|
||||
|
||||
open override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
updateView()
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Overrides
|
||||
//--------------------------------------------------
|
||||
open func updateView(viewModel: ModelType) {
|
||||
fatalError("Implement updateView")
|
||||
}
|
||||
open func updateView() {}
|
||||
|
||||
open func reset() {
|
||||
backgroundColor = .clear
|
||||
if let model = model as? Resettable {
|
||||
model.reset()
|
||||
}
|
||||
surface = .light
|
||||
disabled = false
|
||||
}
|
||||
|
||||
// MARK: - ViewProtocol
|
||||
|
||||
@ -11,44 +11,37 @@ import VDSColorTokens
|
||||
import VDSFormControlsTokens
|
||||
import Combine
|
||||
|
||||
public class Badge: BadgeBase<DefaultBadgeModel>{}
|
||||
public enum BadgeFillColor: String, Codable, CaseIterable {
|
||||
case red, yellow, green, orange, blue, black, white
|
||||
}
|
||||
|
||||
open class BadgeBase<ModelType: BadgeModel>: View<ModelType> {
|
||||
@objc(VDSBadge)
|
||||
public class Badge: View, Accessable {
|
||||
|
||||
private var label = Label()
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Public Properties
|
||||
//--------------------------------------------------
|
||||
@Proxy(\.model.fillColor)
|
||||
open var fillColor: BadgeFillColor
|
||||
open var fillColor: BadgeFillColor = .red { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.text)
|
||||
open var text: String
|
||||
open var text: String = "" { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.maxWidth)
|
||||
open var maxWidth: CGFloat?
|
||||
open var maxWidth: CGFloat? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.numberOfLines)
|
||||
open var numberOfLines: Int
|
||||
open var numberOfLines: Int = 1 { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityHintEnabled)
|
||||
open var accessibilityHintEnabled: String?
|
||||
open var accessibilityHintEnabled: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityHintDisabled)
|
||||
open var accessibilityHintDisabled: String?
|
||||
open var accessibilityHintDisabled: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityValueEnabled)
|
||||
open var accessibilityValueEnabled: String?
|
||||
open var accessibilityValueEnabled: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityValueDisabled)
|
||||
open var accessibilityValueDisabled: String?
|
||||
open var accessibilityValueDisabled: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityLabelEnabled)
|
||||
open var accessibilityLabelEnabled: String?
|
||||
open var accessibilityLabelEnabled: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityLabelDisabled)
|
||||
open var accessibilityLabelDisabled: String?
|
||||
open var accessibilityLabelDisabled: String? { didSet { didChange() }}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Constraints
|
||||
@ -90,9 +83,9 @@ open class BadgeBase<ModelType: BadgeModel>: View<ModelType> {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Configuration
|
||||
//--------------------------------------------------
|
||||
public func backgroundColor(for fillColor: BadgeFillColor) -> UIColor {
|
||||
public func backgroundColor() -> UIColor {
|
||||
var config: SurfaceColorConfiguration
|
||||
switch model.fillColor {
|
||||
switch fillColor {
|
||||
case .red:
|
||||
config = SurfaceColorConfiguration().with {
|
||||
$0.lightColor = VDSColor.backgroundBrandhighlight
|
||||
@ -130,10 +123,10 @@ open class BadgeBase<ModelType: BadgeModel>: View<ModelType> {
|
||||
}
|
||||
}
|
||||
|
||||
return config.getColor(model)
|
||||
return config.getColor(self)
|
||||
}
|
||||
|
||||
public func textColorConfiguration(for fillColor: BadgeFillColor) -> AnyColorable {
|
||||
public func textColorConfiguration() -> AnyColorable {
|
||||
|
||||
switch fillColor {
|
||||
|
||||
@ -165,19 +158,23 @@ open class BadgeBase<ModelType: BadgeModel>: View<ModelType> {
|
||||
//--------------------------------------------------
|
||||
// MARK: - State
|
||||
//--------------------------------------------------
|
||||
open override func updateView(viewModel: ModelType) {
|
||||
backgroundColor = backgroundColor(for: viewModel.fillColor)
|
||||
open override func updateView() {
|
||||
backgroundColor = backgroundColor()
|
||||
label.textColorConfiguration = textColorConfiguration()
|
||||
label.numberOfLines = numberOfLines
|
||||
label.textPosition = .left
|
||||
label.typograpicalStyle = .BoldBodySmall
|
||||
label.text = text
|
||||
label.surface = surface
|
||||
label.disabled = disabled
|
||||
|
||||
label.textColorConfiguration = textColorConfiguration(for: viewModel.fillColor)
|
||||
label.numberOfLines = viewModel.numberOfLines
|
||||
|
||||
if let maxWidth = viewModel.maxWidth, let minWidth = minWidthConstraint?.constant, maxWidth > minWidth {
|
||||
if let maxWidth = maxWidth, let minWidth = minWidthConstraint?.constant, maxWidth > minWidth {
|
||||
maxWidthConstraint?.constant = maxWidth
|
||||
maxWidthConstraint?.isActive = true
|
||||
} else {
|
||||
maxWidthConstraint?.isActive = false
|
||||
}
|
||||
label.set(with: viewModel.label)
|
||||
|
||||
setAccessibilityLabel()
|
||||
}
|
||||
|
||||
|
||||
@ -1,53 +0,0 @@
|
||||
//
|
||||
// BadgeModel.swift
|
||||
// VDS
|
||||
//
|
||||
// Created by Matt Bruce on 9/22/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import VDSColorTokens
|
||||
|
||||
public enum BadgeFillColor: String, Codable, CaseIterable {
|
||||
case red, yellow, green, orange, blue, black, white
|
||||
}
|
||||
|
||||
public protocol BadgeModel: Modelable, Accessable {
|
||||
var fillColor: BadgeFillColor { get set }
|
||||
var text: String { get set }
|
||||
var maxWidth: CGFloat? { get set }
|
||||
var numberOfLines: Int { get set }
|
||||
}
|
||||
|
||||
extension BadgeModel {
|
||||
public var label: DefaultLabelModel {
|
||||
var model = DefaultLabelModel()
|
||||
model.textPosition = .left
|
||||
model.typograpicalStyle = .BoldBodySmall
|
||||
model.text = text
|
||||
model.surface = surface
|
||||
model.disabled = disabled
|
||||
return model
|
||||
}
|
||||
}
|
||||
|
||||
public struct DefaultBadgeModel: BadgeModel {
|
||||
public var id = UUID()
|
||||
public var fillColor: BadgeFillColor = .red
|
||||
public var text: String = ""
|
||||
public var maxWidth: CGFloat?
|
||||
public var numberOfLines: Int = 1
|
||||
|
||||
public var surface: Surface = .light
|
||||
public var disabled: Bool = false
|
||||
|
||||
public var accessibilityHintEnabled: String?
|
||||
public var accessibilityHintDisabled: String?
|
||||
public var accessibilityValueEnabled: String?
|
||||
public var accessibilityValueDisabled: String?
|
||||
public var accessibilityLabelEnabled: String?
|
||||
public var accessibilityLabelDisabled: String?
|
||||
|
||||
public init() { }
|
||||
}
|
||||
@ -11,15 +11,18 @@ import VDSColorTokens
|
||||
import VDSFormControlsTokens
|
||||
import Combine
|
||||
|
||||
public class Button:ButtonBase<DefaultButtonModel>{}
|
||||
public enum ButtonSize: String, Codable, CaseIterable {
|
||||
case large
|
||||
case small
|
||||
}
|
||||
|
||||
open class ButtonBase<ModelType: ButtonModel>: UIButton, ModelHandlerable, ViewProtocol, Resettable {
|
||||
@objc(VDSButton)
|
||||
open class Button: UIButton, Handlerable, ViewProtocol, Resettable, Useable {
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Combine Properties
|
||||
//--------------------------------------------------
|
||||
@Published public var model: ModelType = ModelType()
|
||||
public var modelPublisher: Published<ModelType>.Publisher { $model }
|
||||
public var subject = PassthroughSubject<Void, Never>()
|
||||
public var subscribers = Set<AnyCancellable>()
|
||||
|
||||
//--------------------------------------------------
|
||||
@ -32,39 +35,29 @@ open class ButtonBase<ModelType: ButtonModel>: UIButton, ModelHandlerable, ViewP
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
@Proxy(\.model.surface)
|
||||
open var surface: Surface
|
||||
open var text: String? { didSet { didChange() } }
|
||||
|
||||
@Proxy(\.model.disabled)
|
||||
open var disabled: Bool {
|
||||
didSet {
|
||||
isEnabled = !disabled
|
||||
}
|
||||
}
|
||||
open var use: Use = .primary { didSet { didChange() }}
|
||||
|
||||
open var size: ButtonSize = .large { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.text)
|
||||
open var text: String?
|
||||
open var width: CGFloat? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.use)
|
||||
open var use: Use
|
||||
|
||||
@Proxy(\.model.size)
|
||||
open var size: ButtonSize
|
||||
|
||||
@Proxy(\.model.width)
|
||||
open var width: CGFloat?
|
||||
open var surface: Surface = .light { didSet { didChange() }}
|
||||
|
||||
open var disabled: Bool = false { didSet { isEnabled = !disabled } }
|
||||
|
||||
open override var isEnabled: Bool {
|
||||
get { !model.disabled }
|
||||
get { !disabled }
|
||||
set {
|
||||
//create local vars for clear coding
|
||||
let disabled = !newValue
|
||||
if model.disabled != disabled {
|
||||
model.disabled = disabled
|
||||
if disabled != !newValue {
|
||||
disabled = !newValue
|
||||
}
|
||||
isUserInteractionEnabled = isEnabled
|
||||
didChange()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Configuration Properties
|
||||
@ -113,16 +106,9 @@ open class ButtonBase<ModelType: ButtonModel>: UIButton, ModelHandlerable, ViewP
|
||||
initialSetup()
|
||||
}
|
||||
|
||||
public required init(with model: ModelType) {
|
||||
super.init(frame: .zero)
|
||||
initialSetup()
|
||||
set(with: model)
|
||||
}
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
super.init(frame: .zero)
|
||||
initialSetup()
|
||||
set(with: model)
|
||||
}
|
||||
|
||||
public required init?(coder: NSCoder) {
|
||||
@ -138,7 +124,6 @@ open class ButtonBase<ModelType: ButtonModel>: UIButton, ModelHandlerable, ViewP
|
||||
translatesAutoresizingMaskIntoConstraints = false
|
||||
accessibilityCustomActions = []
|
||||
accessibilityTraits = .staticText
|
||||
setupUpdateView()
|
||||
setup()
|
||||
}
|
||||
|
||||
@ -149,39 +134,44 @@ open class ButtonBase<ModelType: ButtonModel>: UIButton, ModelHandlerable, ViewP
|
||||
|
||||
//only 1 of the 2 widths can be on at the same time
|
||||
widthConstraint = widthAnchor.constraint(equalToConstant: 0)
|
||||
minWidthConstraint = widthAnchor.constraint(greaterThanOrEqualToConstant: model.size.minimumWidth)
|
||||
minWidthConstraint = widthAnchor.constraint(greaterThanOrEqualToConstant: size.minimumWidth)
|
||||
|
||||
//height
|
||||
heightConstraint = heightAnchor.constraint(equalToConstant: model.size.height)
|
||||
heightConstraint = heightAnchor.constraint(equalToConstant: size.height)
|
||||
heightConstraint?.isActive = true
|
||||
}
|
||||
|
||||
open func reset() {
|
||||
model = ModelType()
|
||||
surface = .light
|
||||
disabled = false
|
||||
use = .primary
|
||||
width = nil
|
||||
size = .large
|
||||
accessibilityCustomActions = []
|
||||
accessibilityTraits = .staticText
|
||||
}
|
||||
|
||||
open override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
updateView()
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Overrides
|
||||
//--------------------------------------------------
|
||||
open func updateView(viewModel: ModelType) {
|
||||
open func updateView() {
|
||||
|
||||
let bgColor = buttonBackgroundColorConfiguration.getColor(viewModel)
|
||||
let borderColor = buttonBorderColorConfiguration.getColor(viewModel)
|
||||
let titleColor = buttonTitleColorConfiguration.getColor(viewModel)
|
||||
let borderWidth = viewModel.use == .secondary ? 1.0 : 0.0
|
||||
let buttonHeight = viewModel.size.height
|
||||
let bgColor = buttonBackgroundColorConfiguration.getColor(self)
|
||||
let borderColor = buttonBorderColorConfiguration.getColor(self)
|
||||
let titleColor = buttonTitleColorConfiguration.getColor(self)
|
||||
let borderWidth = use == .secondary ? 1.0 : 0.0
|
||||
let buttonHeight = size.height
|
||||
let cornerRadius = buttonHeight / 2
|
||||
let minWidth = viewModel.size.minimumWidth
|
||||
let font = viewModel.size == .large ? TypographicalStyle.BoldBodyLarge.font : TypographicalStyle.BoldBodySmall.font
|
||||
let edgeInsets = viewModel.size.edgeInsets
|
||||
let minWidth = size.minimumWidth
|
||||
let font = size == .large ? TypographicalStyle.BoldBodyLarge.font : TypographicalStyle.BoldBodySmall.font
|
||||
let edgeInsets = size.edgeInsets
|
||||
|
||||
if let text = viewModel.text {
|
||||
setTitle(text, for: .normal)
|
||||
} else {
|
||||
setTitle("No ViewModel Text", for: .normal)
|
||||
}
|
||||
setTitle(text ?? "No Text", for: .normal)
|
||||
titleLabel?.font = font
|
||||
backgroundColor = bgColor
|
||||
setTitleColor(titleColor, for: .normal)
|
||||
@ -193,7 +183,7 @@ open class ButtonBase<ModelType: ButtonModel>: UIButton, ModelHandlerable, ViewP
|
||||
minWidthConstraint?.constant = minWidth
|
||||
heightConstraint?.constant = buttonHeight
|
||||
|
||||
if let width = viewModel.width, width > minWidth {
|
||||
if let width, width > minWidth {
|
||||
widthConstraint?.constant = width
|
||||
widthConstraint?.isActive = true
|
||||
minWidthConstraint?.isActive = false
|
||||
@ -202,20 +192,20 @@ open class ButtonBase<ModelType: ButtonModel>: UIButton, ModelHandlerable, ViewP
|
||||
minWidthConstraint?.isActive = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - PRIVATE
|
||||
//--------------------------------------------------
|
||||
|
||||
private class UseableColorConfiguration: ObjectColorable {
|
||||
typealias ModelType = Disabling & Surfaceable & Useable
|
||||
typealias ObjectType = Disabling & Surfaceable & Useable
|
||||
public var primary = DisabledSurfaceColorConfiguration()
|
||||
public var secondary = DisabledSurfaceColorConfiguration()
|
||||
|
||||
required public init(){}
|
||||
|
||||
public func getColor(_ viewModel: ModelType) -> UIColor {
|
||||
return viewModel.use == .primary ? primary.getColor(viewModel) : secondary.getColor(viewModel)
|
||||
public func getColor(_ object: ObjectType) -> UIColor {
|
||||
return object.use == .primary ? primary.getColor(object) : secondary.getColor(object)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,34 +0,0 @@
|
||||
//
|
||||
// ButtonModel.swift
|
||||
// VDS
|
||||
//
|
||||
// Created by Jarrod Courtney on 9/16/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
public enum ButtonSize: String, Codable, CaseIterable {
|
||||
case large
|
||||
case small
|
||||
}
|
||||
|
||||
public protocol ButtonModel: Modelable, Useable {
|
||||
var text: String? { get set }
|
||||
var width: CGFloat? { get set }
|
||||
var size: ButtonSize { get set }
|
||||
var use: Use { get set }
|
||||
}
|
||||
|
||||
public struct DefaultButtonModel: ButtonModel {
|
||||
|
||||
public var id = UUID()
|
||||
public var text: String?
|
||||
public var typograpicalStyle: TypographicalStyle = .BoldBodyLarge
|
||||
public var surface: Surface = .light
|
||||
public var use: Use = .primary
|
||||
public var disabled: Bool = false
|
||||
public var width: CGFloat?
|
||||
public var size: ButtonSize = .large
|
||||
public init(){}
|
||||
}
|
||||
@ -11,9 +11,11 @@ import VDSColorTokens
|
||||
import VDSFormControlsTokens
|
||||
import Combine
|
||||
|
||||
public class Checkbox: CheckboxBase<DefaultCheckboxModel>{}
|
||||
@objc(VDSCheckbox)
|
||||
public class Checkbox: CheckboxBase{}
|
||||
|
||||
public class SoloCheckbox: CheckboxBase<DefaultCheckboxModel>{
|
||||
@objc(VDSSoloCheckbox)
|
||||
public class SoloCheckbox: CheckboxBase{
|
||||
public override func initialSetup() {
|
||||
super.initialSetup()
|
||||
publisher(for: .touchUpInside)
|
||||
@ -23,11 +25,36 @@ public class SoloCheckbox: CheckboxBase<DefaultCheckboxModel>{
|
||||
}
|
||||
}
|
||||
|
||||
open class CheckboxBase<ModelType: CheckboxModel>: Control<ModelType> {
|
||||
@objc(VDSCheckboxBase)
|
||||
open class CheckboxBase: Control, Accessable, DataTrackable, BinaryColorable, Errorable {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializers
|
||||
//--------------------------------------------------
|
||||
required public init() {
|
||||
super.init(frame: .zero)
|
||||
}
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
super.init(frame: .zero)
|
||||
}
|
||||
|
||||
public required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Private Properties
|
||||
//--------------------------------------------------
|
||||
private var shouldShowError: Bool {
|
||||
guard showError && !disabled && errorText?.isEmpty == false else { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
private var shouldShowLabels: Bool {
|
||||
guard labelText?.isEmpty == false || childText?.isEmpty == false else { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
private var mainStackView: UIStackView = {
|
||||
return UIStackView().with {
|
||||
$0.translatesAutoresizingMaskIntoConstraints = false
|
||||
@ -67,60 +94,42 @@ open class CheckboxBase<ModelType: CheckboxModel>: Control<ModelType> {
|
||||
}()
|
||||
|
||||
//can't bind to @Proxy
|
||||
open override var isSelected: Bool {
|
||||
get { model.selected }
|
||||
set {
|
||||
if model.selected != newValue {
|
||||
model.selected = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
open override var isSelected: Bool { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.labelText)
|
||||
open var labelText: String?
|
||||
open var labelText: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.childText)
|
||||
open var childText: String?
|
||||
open var labelTextAttributes: [any LabelAttributeModel]? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.showError)
|
||||
open var showError: Bool
|
||||
|
||||
@Proxy(\.model.errorText)
|
||||
open var errorText: String?
|
||||
open var childText: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.inputId)
|
||||
open var inputId: String?
|
||||
open var childTextAttributes: [any LabelAttributeModel]? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.value)
|
||||
open var value: AnyHashable?
|
||||
open var showError: Bool = false { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.dataAnalyticsTrack)
|
||||
open var dataAnalyticsTrack: String?
|
||||
open var errorText: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.dataClickStream)
|
||||
open var dataClickStream: String?
|
||||
open var inputId: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.dataTrack)
|
||||
open var dataTrack: String?
|
||||
open var value: AnyHashable? { didSet { didChange() }}
|
||||
|
||||
open var dataAnalyticsTrack: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityHintEnabled)
|
||||
open var accessibilityHintEnabled: String?
|
||||
open var dataClickStream: String? { didSet { didChange() }}
|
||||
|
||||
open var dataTrack: String? { didSet { didChange() }}
|
||||
|
||||
open var accessibilityHintEnabled: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityHintDisabled)
|
||||
open var accessibilityHintDisabled: String?
|
||||
open var accessibilityHintDisabled: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityValueEnabled)
|
||||
open var accessibilityValueEnabled: String?
|
||||
open var accessibilityValueEnabled: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityValueDisabled)
|
||||
open var accessibilityValueDisabled: String?
|
||||
open var accessibilityValueDisabled: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityLabelEnabled)
|
||||
open var accessibilityLabelEnabled: String?
|
||||
open var accessibilityLabelEnabled: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityLabelDisabled)
|
||||
open var accessibilityLabelDisabled: String?
|
||||
|
||||
open var accessibilityLabelDisabled: String? { didSet { didChange() }}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Constraints
|
||||
//--------------------------------------------------
|
||||
@ -159,7 +168,7 @@ open class CheckboxBase<ModelType: CheckboxModel>: Control<ModelType> {
|
||||
selectorWidthConstraint = selectorView.widthAnchor.constraint(equalToConstant: selectorSize.width)
|
||||
selectorWidthConstraint?.isActive = true
|
||||
|
||||
updateSelector(model)
|
||||
updateSelector()
|
||||
|
||||
mainStackView.topAnchor.constraint(equalTo: topAnchor).isActive = true
|
||||
mainStackView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
|
||||
@ -168,22 +177,32 @@ open class CheckboxBase<ModelType: CheckboxModel>: Control<ModelType> {
|
||||
|
||||
}
|
||||
|
||||
func updateLabels(_ viewModel: ModelType) {
|
||||
func updateLabels() {
|
||||
|
||||
//deal with labels
|
||||
if viewModel.shouldShowLabels {
|
||||
if shouldShowLabels {
|
||||
//add the stackview to hold the 2 labels
|
||||
//top label
|
||||
if let labelModel = viewModel.labelModel {
|
||||
primaryLabel.set(with: labelModel)
|
||||
if let labelText {
|
||||
primaryLabel.textPosition = .left
|
||||
primaryLabel.typograpicalStyle = .BoldBodyLarge
|
||||
primaryLabel.text = labelText
|
||||
primaryLabel.surface = surface
|
||||
primaryLabel.disabled = disabled
|
||||
primaryLabel.attributes = labelTextAttributes
|
||||
primaryLabel.isHidden = false
|
||||
} else {
|
||||
primaryLabel.isHidden = true
|
||||
}
|
||||
|
||||
//bottom label
|
||||
if let childModel = viewModel.childModel {
|
||||
secondaryLabel.set(with: childModel)
|
||||
if let childText {
|
||||
secondaryLabel.textPosition = .left
|
||||
secondaryLabel.typograpicalStyle = .BodyLarge
|
||||
secondaryLabel.text = childText
|
||||
secondaryLabel.surface = surface
|
||||
secondaryLabel.disabled = disabled
|
||||
secondaryLabel.attributes = childTextAttributes
|
||||
secondaryLabel.isHidden = false
|
||||
} else {
|
||||
secondaryLabel.isHidden = true
|
||||
@ -199,20 +218,23 @@ open class CheckboxBase<ModelType: CheckboxModel>: Control<ModelType> {
|
||||
}
|
||||
|
||||
//either add/remove the error from the main stack
|
||||
if let errorModel = model.errorModel, model.shouldShowError {
|
||||
errorLabel.set(with: errorModel)
|
||||
if let errorText, shouldShowError {
|
||||
errorLabel.textPosition = .left
|
||||
errorLabel.typograpicalStyle = .BodyMedium
|
||||
errorLabel.text = errorText
|
||||
errorLabel.surface = surface
|
||||
errorLabel.disabled = disabled
|
||||
mainStackView.spacing = 8
|
||||
errorLabel.isHidden = false
|
||||
} else {
|
||||
mainStackView.spacing = 0
|
||||
errorLabel.isHidden = true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override func reset() {
|
||||
super.reset()
|
||||
updateSelector(model)
|
||||
updateSelector()
|
||||
setAccessibilityLabel()
|
||||
}
|
||||
|
||||
@ -229,14 +251,12 @@ open class CheckboxBase<ModelType: CheckboxModel>: Control<ModelType> {
|
||||
//--------------------------------------------------
|
||||
// MARK: - State
|
||||
//--------------------------------------------------
|
||||
open override func updateView(viewModel: ModelType) {
|
||||
updateLabels(viewModel)
|
||||
updateSelector(viewModel)
|
||||
open override func updateView() {
|
||||
updateLabels()
|
||||
updateSelector()
|
||||
setAccessibilityHint()
|
||||
setAccessibilityValue(viewModel.selected)
|
||||
setAccessibilityLabel(viewModel.selected)
|
||||
setNeedsLayout()
|
||||
layoutIfNeeded()
|
||||
setAccessibilityValue(isSelected)
|
||||
setAccessibilityLabel(isSelected)
|
||||
}
|
||||
|
||||
|
||||
@ -294,11 +314,11 @@ open class CheckboxBase<ModelType: CheckboxModel>: Control<ModelType> {
|
||||
return checkboxSize
|
||||
}
|
||||
|
||||
open func updateSelector(_ viewModel: ModelType) {
|
||||
open func updateSelector() {
|
||||
//get the colors
|
||||
let backgroundColor = checkboxBackgroundColorConfiguration.getColor(viewModel)
|
||||
let borderColor = checkboxBorderColorConfiguration.getColor(viewModel)
|
||||
let checkColor = checkboxCheckColorConfiguration.getColor(viewModel)
|
||||
let backgroundColor = checkboxBackgroundColorConfiguration.getColor(self)
|
||||
let borderColor = checkboxBorderColorConfiguration.getColor(self)
|
||||
let checkColor = checkboxCheckColorConfiguration.getColor(self)
|
||||
|
||||
if let shapeLayer = shapeLayer, let sublayers = layer.sublayers, sublayers.contains(shapeLayer) {
|
||||
shapeLayer.removeFromSuperlayer()
|
||||
@ -340,7 +360,7 @@ open class CheckboxBase<ModelType: CheckboxModel>: Control<ModelType> {
|
||||
shapeLayer.lineJoin = .miter
|
||||
shapeLayer.lineWidth = 2
|
||||
CATransaction.withDisabledAnimations {
|
||||
shapeLayer.strokeEnd = model.selected ? 1 : 0
|
||||
shapeLayer.strokeEnd = isSelected ? 1 : 0
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -350,21 +370,21 @@ open class CheckboxBase<ModelType: CheckboxModel>: Control<ModelType> {
|
||||
// MARK: - Color Class Configurations
|
||||
//--------------------------------------------------
|
||||
internal class ErrorBinaryDisabledSurfaceColorConfiguration: BinaryDisabledSurfaceColorable {
|
||||
typealias ModelType = Errorable & Disabling & Surfaceable & BinaryColorable
|
||||
typealias ObjectType = Errorable & Disabling & Surfaceable & BinaryColorable
|
||||
var error = BinarySurfaceColorConfiguration()
|
||||
var forTrue = DisabledSurfaceColorConfiguration()
|
||||
var forFalse = DisabledSurfaceColorConfiguration()
|
||||
|
||||
required init() {}
|
||||
|
||||
func getColor(_ viewModel: ModelType) -> UIColor {
|
||||
func getColor(_ object: ObjectType) -> UIColor {
|
||||
//only show error is enabled and showError == true
|
||||
let showErrorColor = !viewModel.disabled && viewModel.showError
|
||||
let showErrorColor = !object.disabled && object.showError
|
||||
|
||||
if showErrorColor {
|
||||
return error.getColor(viewModel)
|
||||
return error.getColor(object)
|
||||
} else {
|
||||
return getBinaryColor(viewModel)
|
||||
return getBinaryColor(object)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,7 +8,8 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
public class CheckboxGroup: CheckboxGroupBase<DefaultCheckboxGroupModel, Checkbox> {
|
||||
@objc(VDSCheckboxGroup)
|
||||
public class CheckboxGroup: CheckboxGroupBase<Checkbox> {
|
||||
public override func didSelect(_ selectedControl: Checkbox) {
|
||||
selectedControl.toggle()
|
||||
if selectedControl.isSelected, showError{
|
||||
@ -18,13 +19,30 @@ public class CheckboxGroup: CheckboxGroupBase<DefaultCheckboxGroupModel, Checkbo
|
||||
}
|
||||
}
|
||||
|
||||
public class CheckboxGroupBase<GroupModelType: CheckboxGroupModel, ModelHandlerType: CheckboxBase<GroupModelType.SelectorModelType>>: SelectorGroupHandlerBase<GroupModelType, ModelHandlerType> {
|
||||
public class CheckboxGroupBase<HandlerType: CheckboxBase>: SelectorGroupHandlerBase<HandlerType> {
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Public Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public override var selectorViews: [HandlerType] {
|
||||
didSet {
|
||||
for selector in selectorViews {
|
||||
if !mainStackView.arrangedSubviews.contains(selector) {
|
||||
selector
|
||||
.publisher(for: .touchUpInside)
|
||||
.sink { [weak self] handler in
|
||||
self?.didSelect(handler)
|
||||
}.store(in: &subscribers)
|
||||
mainStackView.addArrangedSubview(selector)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var _showError: Bool = false
|
||||
public var showError: Bool {
|
||||
get { model.showError }
|
||||
get { _showError }
|
||||
set {
|
||||
var newShowError = newValue
|
||||
let anySelected = selectorViews.filter { $0.isSelected == true }.count > 0
|
||||
@ -34,8 +52,7 @@ public class CheckboxGroupBase<GroupModelType: CheckboxGroupModel, ModelHandlerT
|
||||
selectorViews.forEach { handler in
|
||||
handler.showError = newShowError
|
||||
}
|
||||
model.showError = newShowError
|
||||
|
||||
_showError = newShowError
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,24 +84,8 @@ public class CheckboxGroupBase<GroupModelType: CheckboxGroupModel, ModelHandlerT
|
||||
mainStackView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
|
||||
mainStackView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
|
||||
}
|
||||
|
||||
open override func updateView(viewModel: ModelType) {
|
||||
for selectorModel in viewModel.selectors {
|
||||
//see if view is there for the model
|
||||
if let foundSelectorView = findSelectorView(id: selectorModel.id) {
|
||||
foundSelectorView.set(with: selectorModel)
|
||||
} else {
|
||||
|
||||
//create view
|
||||
let newSelectorView = createModelHandler(selector: selectorModel)
|
||||
|
||||
self.selectorViews.append(newSelectorView)
|
||||
mainStackView.addArrangedSubview(newSelectorView)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var selectedModelHandlers: [ModelHandlerType]? {
|
||||
public var selectedHandlers: [HandlerType]? {
|
||||
let selected = selectorViews.filter{ $0.isSelected == true }
|
||||
guard selected.count > 0 else { return nil }
|
||||
return selected
|
||||
|
||||
@ -1,29 +0,0 @@
|
||||
//
|
||||
// CheckboxGroupModel.swift
|
||||
// VDS
|
||||
//
|
||||
// Created by Matt Bruce on 8/23/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol CheckboxGroupModel: SelectorGroupModelable, Errorable where SelectorModelType: CheckboxModel {
|
||||
|
||||
}
|
||||
|
||||
public struct DefaultCheckboxGroupModel: CheckboxGroupModel {
|
||||
public typealias SelectorModelType = DefaultCheckboxModel
|
||||
public var id = UUID()
|
||||
public var inputId: String?
|
||||
public var value: AnyHashable?
|
||||
public var surface: Surface = .light
|
||||
public var disabled: Bool = false
|
||||
public var selectors: [SelectorModelType]
|
||||
public var showError: Bool = false
|
||||
public var errorText: String?
|
||||
public init() { selectors = [] }
|
||||
public init(selectors: [SelectorModelType]){
|
||||
self.selectors = selectors
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,123 +0,0 @@
|
||||
//
|
||||
// ToggleModel.swift
|
||||
// VDS
|
||||
//
|
||||
// Created by Matt Bruce on 7/22/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
public protocol CheckboxModel: Modelable, FormFieldable, Errorable, DataTrackable, Accessable, Selectable, BinaryColorable {
|
||||
var labelText: String? { get set }
|
||||
var labelTextAttributes: [any LabelAttributeModel]? { get set }
|
||||
var childText: String? { get set }
|
||||
var childTextAttributes: [any LabelAttributeModel]? { get set }
|
||||
}
|
||||
|
||||
extension CheckboxModel {
|
||||
|
||||
public var shouldShowError: Bool {
|
||||
guard showError && !disabled && errorText?.isEmpty == false else { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
public var shouldShowLabels: Bool {
|
||||
guard labelText?.isEmpty == false || childText?.isEmpty == false else { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
public var labelModel: DefaultLabelModel? {
|
||||
guard let labelText = labelText else { return nil }
|
||||
var model = DefaultLabelModel()
|
||||
model.textPosition = .left
|
||||
model.typograpicalStyle = .BoldBodyLarge
|
||||
model.text = labelText
|
||||
model.surface = surface
|
||||
model.disabled = disabled
|
||||
model.attributes = labelTextAttributes
|
||||
return model
|
||||
}
|
||||
|
||||
public var childModel: DefaultLabelModel? {
|
||||
guard let childText = childText else { return nil }
|
||||
var model = DefaultLabelModel()
|
||||
model.textPosition = .left
|
||||
model.typograpicalStyle = .BodyLarge
|
||||
model.text = childText
|
||||
model.surface = surface
|
||||
model.disabled = disabled
|
||||
model.attributes = childTextAttributes
|
||||
return model
|
||||
}
|
||||
|
||||
public var errorModel: DefaultLabelModel? {
|
||||
guard let errorText = errorText, showError else { return nil }
|
||||
var model = DefaultLabelModel()
|
||||
model.textPosition = .left
|
||||
model.typograpicalStyle = .BodyMedium
|
||||
model.text = errorText
|
||||
model.surface = surface
|
||||
model.disabled = disabled
|
||||
return model
|
||||
}
|
||||
}
|
||||
|
||||
public struct DefaultCheckboxModel: CheckboxModel {
|
||||
public var id = UUID()
|
||||
public var selected: Bool = false
|
||||
public var labelText: String?
|
||||
public var labelTextAttributes: [any LabelAttributeModel]?
|
||||
public var childText: String?
|
||||
public var childTextAttributes: [any LabelAttributeModel]?
|
||||
|
||||
public var showError: Bool = false
|
||||
public var errorText: String?
|
||||
|
||||
public var inputId: String?
|
||||
public var value: AnyHashable?
|
||||
|
||||
public var surface: Surface = .light
|
||||
public var disabled: Bool = false
|
||||
|
||||
public var dataAnalyticsTrack: String?
|
||||
public var dataClickStream: String?
|
||||
public var dataTrack: String?
|
||||
public var accessibilityHintEnabled: String?
|
||||
public var accessibilityHintDisabled: String?
|
||||
public var accessibilityValueEnabled: String?
|
||||
public var accessibilityValueDisabled: String?
|
||||
public var accessibilityLabelEnabled: String?
|
||||
public var accessibilityLabelDisabled: String?
|
||||
|
||||
public init() {}
|
||||
|
||||
public static func == (lhs: DefaultCheckboxModel, rhs: DefaultCheckboxModel) -> Bool {
|
||||
lhs.isEqual(rhs)
|
||||
}
|
||||
|
||||
public func isEqual(_ equatable: DefaultCheckboxModel) -> Bool {
|
||||
return id == equatable.id
|
||||
&& selected == equatable.selected
|
||||
&& labelText == equatable.labelText
|
||||
&& labelTextAttributes == equatable.labelTextAttributes
|
||||
&& childText == equatable.childText
|
||||
&& childTextAttributes == equatable.childTextAttributes
|
||||
&& showError == equatable.showError
|
||||
&& errorText == equatable.errorText
|
||||
&& inputId == equatable.inputId
|
||||
&& value == equatable.value
|
||||
&& surface == equatable.surface
|
||||
&& disabled == equatable.disabled
|
||||
&& dataAnalyticsTrack == equatable.dataAnalyticsTrack
|
||||
&& dataClickStream == equatable.dataClickStream
|
||||
&& dataTrack == equatable.dataTrack
|
||||
&& accessibilityHintEnabled == equatable.accessibilityHintEnabled
|
||||
&& accessibilityHintDisabled == equatable.accessibilityHintDisabled
|
||||
&& accessibilityValueEnabled == equatable.accessibilityValueEnabled
|
||||
&& accessibilityValueDisabled == equatable.accessibilityValueDisabled
|
||||
&& accessibilityLabelEnabled == equatable.accessibilityLabelEnabled
|
||||
&& accessibilityLabelDisabled == equatable.accessibilityLabelDisabled
|
||||
}
|
||||
|
||||
}
|
||||
@ -33,7 +33,7 @@ public struct UnderlineLabelAttribute: LabelAttributeModel {
|
||||
}
|
||||
}
|
||||
|
||||
public init(location: Int, length: Int, style: UnderlineStyle = .single, color: UIColor = .black, pattern: UnderlineStyle.Pattern? = nil) {
|
||||
public init(location: Int, length: Int, style: UnderlineStyle = .single, color: UIColor? = nil, pattern: UnderlineStyle.Pattern? = nil) {
|
||||
self.location = location
|
||||
self.length = length
|
||||
self.color = color
|
||||
|
||||
@ -10,57 +10,46 @@ import UIKit
|
||||
import VDSColorTokens
|
||||
import Combine
|
||||
|
||||
public class Label:LabelBase<DefaultLabelModel>{}
|
||||
@objc(VDSLabel)
|
||||
public class Label: LabelBase {}
|
||||
|
||||
open class LabelBase<ModelType: LabelModel>: UILabel, ModelHandlerable, ViewProtocol, Resettable {
|
||||
@objc(VDSLabelBase)
|
||||
open class LabelBase: UILabel, Handlerable, ViewProtocol, Resettable {
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Combine Properties
|
||||
//--------------------------------------------------
|
||||
@Published public var model: ModelType = ModelType()
|
||||
public var modelPublisher: Published<ModelType>.Publisher { $model }
|
||||
public var subject = PassthroughSubject<Void, Never>()
|
||||
public var subscribers = Set<AnyCancellable>()
|
||||
|
||||
public var hasChanged: Bool = false
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
@Proxy(\.model.surface)
|
||||
open var surface: Surface
|
||||
open var surface: Surface = .light { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.disabled)
|
||||
open var disabled: Bool {
|
||||
didSet {
|
||||
self.isEnabled = !disabled
|
||||
}
|
||||
}
|
||||
open var disabled: Bool = false { didSet { isEnabled = !disabled } }
|
||||
|
||||
open var attributes: [any LabelAttributeModel]? { didSet { didChange() }}
|
||||
|
||||
open var typograpicalStyle: TypographicalStyle = .defaultStyle { didSet { didChange() }}
|
||||
|
||||
open var textPosition: TextPosition = .left { didSet { didChange() }}
|
||||
|
||||
open override var isEnabled: Bool {
|
||||
get { !model.disabled }
|
||||
get { !disabled }
|
||||
set {
|
||||
//create local vars for clear coding
|
||||
let disabled = !newValue
|
||||
if model.disabled != disabled {
|
||||
model.disabled = disabled
|
||||
if disabled != !newValue {
|
||||
disabled = !newValue
|
||||
}
|
||||
isUserInteractionEnabled = isEnabled
|
||||
didChange()
|
||||
}
|
||||
}
|
||||
|
||||
@Proxy(\.model.attributes)
|
||||
open var attributes: [any LabelAttributeModel]?
|
||||
|
||||
@Proxy(\.model.typograpicalStyle)
|
||||
open var typograpicalStyle: TypographicalStyle
|
||||
|
||||
@Proxy(\.model.textPosition)
|
||||
open var textPosition: TextPosition
|
||||
|
||||
//can't use @Proxy here
|
||||
override open var text: String? {
|
||||
didSet {
|
||||
if model.text != oldValue {
|
||||
model.text = text
|
||||
}
|
||||
didChange()
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,17 +70,10 @@ open class LabelBase<ModelType: LabelModel>: UILabel, ModelHandlerable, ViewProt
|
||||
super.init(frame: .zero)
|
||||
initialSetup()
|
||||
}
|
||||
|
||||
public required init(with model: ModelType) {
|
||||
super.init(frame: .zero)
|
||||
initialSetup()
|
||||
set(with: model)
|
||||
}
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
super.init(frame: .zero)
|
||||
initialSetup()
|
||||
set(with: model)
|
||||
}
|
||||
|
||||
public required init?(coder: NSCoder) {
|
||||
@ -109,37 +91,38 @@ open class LabelBase<ModelType: LabelModel>: UILabel, ModelHandlerable, ViewProt
|
||||
translatesAutoresizingMaskIntoConstraints = false
|
||||
accessibilityCustomActions = []
|
||||
accessibilityTraits = .staticText
|
||||
setupUpdateView()
|
||||
setup()
|
||||
}
|
||||
|
||||
open func setup() {}
|
||||
|
||||
open func reset() {
|
||||
surface = .light
|
||||
disabled = false
|
||||
attributes = nil
|
||||
typograpicalStyle = .defaultStyle
|
||||
textPosition = .left
|
||||
text = nil
|
||||
attributedText = nil
|
||||
textColor = .black
|
||||
font = TypographicalStyle.BodyLarge.font
|
||||
textAlignment = .left
|
||||
accessibilityCustomActions = []
|
||||
accessibilityTraits = .staticText
|
||||
numberOfLines = 0
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Overrides
|
||||
//--------------------------------------------------
|
||||
open func updateView(viewModel: ModelType) {
|
||||
textAlignment = viewModel.textPosition.textAlignment
|
||||
textColor = textColorConfiguration.getColor(viewModel)
|
||||
open override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
updateView()
|
||||
}
|
||||
|
||||
open func updateView() {
|
||||
textAlignment = textPosition.textAlignment
|
||||
textColor = textColorConfiguration.getColor(self)
|
||||
font = typograpicalStyle.font
|
||||
|
||||
if let vdsFont = viewModel.font {
|
||||
font = vdsFont
|
||||
} else {
|
||||
font = TypographicalStyle.defaultStyle.font
|
||||
}
|
||||
|
||||
if let text = viewModel.text, let font = font, let textColor = textColor {
|
||||
if let text = text, let font = font, let textColor = textColor {
|
||||
//clear the arrays holding actions
|
||||
accessibilityCustomActions = []
|
||||
actions = []
|
||||
@ -149,9 +132,9 @@ open class LabelBase<ModelType: LabelModel>: UILabel, ModelHandlerable, ViewProt
|
||||
let mutableText = NSMutableAttributedString(string: text, attributes: startingAttributes)
|
||||
|
||||
//set the local lineHeight/lineSpacing attributes
|
||||
setStyleAttributes(viewModel: viewModel, attributedString: mutableText)
|
||||
setStyleAttributes(attributedString: mutableText)
|
||||
|
||||
if let attributes = viewModel.attributes {
|
||||
if let attributes = attributes {
|
||||
//loop through the models attributes
|
||||
for attribute in attributes {
|
||||
|
||||
@ -170,42 +153,40 @@ open class LabelBase<ModelType: LabelModel>: UILabel, ModelHandlerable, ViewProt
|
||||
}
|
||||
|
||||
//only enabled if enabled and has actions
|
||||
isUserInteractionEnabled = !viewModel.disabled && !actions.isEmpty
|
||||
isUserInteractionEnabled = !disabled && !actions.isEmpty
|
||||
|
||||
//set the attributed text
|
||||
attributedText = mutableText
|
||||
} else {
|
||||
text = viewModel.text
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private Attributes
|
||||
private func setStyleAttributes(viewModel: ModelType, attributedString: NSMutableAttributedString) {
|
||||
private func setStyleAttributes(attributedString: NSMutableAttributedString) {
|
||||
//get the range
|
||||
let entireRange = NSRange(location: 0, length: attributedString.length)
|
||||
|
||||
//set letterSpacing
|
||||
if viewModel.typograpicalStyle.letterSpacing > 0.0 {
|
||||
attributedString.addAttribute(.kern, value: viewModel.typograpicalStyle.letterSpacing, range: entireRange)
|
||||
if typograpicalStyle.letterSpacing > 0.0 {
|
||||
attributedString.addAttribute(.kern, value: typograpicalStyle.letterSpacing, range: entireRange)
|
||||
}
|
||||
|
||||
//set lineHeight
|
||||
if viewModel.typograpicalStyle.lineHeight > 0.0 {
|
||||
let lineHeight = viewModel.typograpicalStyle.lineHeight
|
||||
if typograpicalStyle.lineHeight > 0.0 {
|
||||
let lineHeight = typograpicalStyle.lineHeight
|
||||
let adjustment = lineHeight > font.lineHeight ? 2.0 : 1.0
|
||||
let baselineOffset = (lineHeight - font.lineHeight) / 2.0 / adjustment
|
||||
let paragraph = NSMutableParagraphStyle().with {
|
||||
$0.maximumLineHeight = lineHeight
|
||||
$0.minimumLineHeight = lineHeight
|
||||
$0.alignment = viewModel.textPosition.textAlignment
|
||||
$0.alignment = textPosition.textAlignment
|
||||
$0.lineBreakMode = lineBreakMode
|
||||
}
|
||||
attributedString.addAttribute(.baselineOffset, value: baselineOffset, range: entireRange)
|
||||
attributedString.addAttribute( .paragraphStyle, value: paragraph, range: entireRange)
|
||||
|
||||
} else if viewModel.textPosition != .left {
|
||||
} else if textPosition != .left {
|
||||
let paragraph = NSMutableParagraphStyle().with {
|
||||
$0.alignment = viewModel.textPosition.textAlignment
|
||||
$0.alignment = textPosition.textAlignment
|
||||
$0.lineBreakMode = lineBreakMode
|
||||
}
|
||||
attributedString.addAttribute( .paragraphStyle, value: paragraph, range: entireRange)
|
||||
|
||||
@ -1,40 +0,0 @@
|
||||
//
|
||||
// VDSLabelModel.swift
|
||||
// VDS
|
||||
//
|
||||
// Created by Matt Bruce on 7/28/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
public protocol LabelModel: Modelable, Labelable, Equatable, AnyEquatable {
|
||||
var text: String? { get set }
|
||||
var attributes: [any LabelAttributeModel]? { get set }
|
||||
}
|
||||
|
||||
public struct DefaultLabelModel: LabelModel {
|
||||
public static func == (lhs: DefaultLabelModel, rhs: DefaultLabelModel) -> Bool {
|
||||
lhs.isEqual(rhs)
|
||||
}
|
||||
|
||||
public func isEqual(_ equatable: DefaultLabelModel) -> Bool {
|
||||
return id == equatable.id
|
||||
&& attributes == equatable.attributes
|
||||
&& text == equatable.text
|
||||
&& surface == equatable.surface
|
||||
&& typograpicalStyle == equatable.typograpicalStyle
|
||||
&& textPosition == equatable.textPosition
|
||||
&& surface == equatable.surface
|
||||
&& disabled == equatable.disabled
|
||||
}
|
||||
|
||||
public var id = UUID()
|
||||
public var text: String?
|
||||
public var attributes: [any LabelAttributeModel]?
|
||||
public var typograpicalStyle: TypographicalStyle = .BodySmall
|
||||
public var textPosition: TextPosition = .left
|
||||
public var surface: Surface = .light
|
||||
public var disabled: Bool = false
|
||||
public init(){}
|
||||
}
|
||||
@ -11,9 +11,11 @@ import VDSColorTokens
|
||||
import VDSFormControlsTokens
|
||||
import Combine
|
||||
|
||||
public class RadioBox: RadioBoxBase<DefaultRadioBoxModel>{}
|
||||
@objc(VDSRadioBox)
|
||||
public class RadioBox: RadioBoxBase{}
|
||||
|
||||
public class SolorRadioBox: RadioBoxBase<DefaultRadioBoxModel>{
|
||||
@objc(VDSSoloRadioBox)
|
||||
public class SoloRadioBox: RadioBoxBase{
|
||||
|
||||
public override func initialSetup() {
|
||||
super.initialSetup()
|
||||
@ -24,8 +26,23 @@ public class SolorRadioBox: RadioBoxBase<DefaultRadioBoxModel>{
|
||||
}
|
||||
}
|
||||
|
||||
open class RadioBoxBase<ModelType: RadioBoxModel>: Control<ModelType> {
|
||||
@objc(VDSRadioBoxBase)
|
||||
open class RadioBoxBase: Control, BinaryColorable, Accessable, DataTrackable{
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializers
|
||||
//--------------------------------------------------
|
||||
required public init() {
|
||||
super.init(frame: .zero)
|
||||
}
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
super.init(frame: .zero)
|
||||
}
|
||||
|
||||
public required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Private Properties
|
||||
//--------------------------------------------------
|
||||
@ -67,61 +84,42 @@ open class RadioBoxBase<ModelType: RadioBoxModel>: Control<ModelType> {
|
||||
$0.translatesAutoresizingMaskIntoConstraints = false
|
||||
}
|
||||
}()
|
||||
|
||||
//can't bind to @Proxy
|
||||
open override var isSelected: Bool {
|
||||
get { model.selected }
|
||||
set {
|
||||
if model.selected != newValue {
|
||||
model.selected = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Proxy(\.model.text)
|
||||
open var text: String
|
||||
open var text: String = "Default Text" { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.subText)
|
||||
open var subText: String?
|
||||
open var textAttributes: [any LabelAttributeModel]? { didSet { didChange() }}
|
||||
|
||||
open var subText: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.subTextRight)
|
||||
open var subTextRight: String?
|
||||
open var subTextAttributes: [any LabelAttributeModel]? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.strikethrough)
|
||||
open var strikethrough: Bool
|
||||
open var subTextRight: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.inputId)
|
||||
open var inputId: String?
|
||||
open var subTextRightAttributes: [any LabelAttributeModel]? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.value)
|
||||
open var value: AnyHashable?
|
||||
open var strikethrough: Bool = false { didSet { didChange() }}
|
||||
|
||||
open var inputId: String? { didSet { didChange() }}
|
||||
|
||||
open var value: AnyHashable? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.dataAnalyticsTrack)
|
||||
open var dataAnalyticsTrack: String?
|
||||
open var dataAnalyticsTrack: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.dataClickStream)
|
||||
open var dataClickStream: String?
|
||||
open var dataClickStream: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.dataTrack)
|
||||
open var dataTrack: String?
|
||||
open var dataTrack: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityHintEnabled)
|
||||
open var accessibilityHintEnabled: String?
|
||||
open var accessibilityHintEnabled: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityHintDisabled)
|
||||
open var accessibilityHintDisabled: String?
|
||||
open var accessibilityHintDisabled: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityValueEnabled)
|
||||
open var accessibilityValueEnabled: String?
|
||||
open var accessibilityValueEnabled: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityValueDisabled)
|
||||
open var accessibilityValueDisabled: String?
|
||||
open var accessibilityValueDisabled: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityLabelEnabled)
|
||||
open var accessibilityLabelEnabled: String?
|
||||
open var accessibilityLabelEnabled: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityLabelDisabled)
|
||||
open var accessibilityLabelDisabled: String?
|
||||
open var accessibilityLabelDisabled: String? { didSet { didChange() }}
|
||||
|
||||
//functions
|
||||
//--------------------------------------------------
|
||||
@ -153,7 +151,7 @@ open class RadioBoxBase<ModelType: RadioBoxModel>: Control<ModelType> {
|
||||
selectorLeftLabelStackView.spacing = 4
|
||||
selectorLeftLabelStackView.isHidden = false
|
||||
|
||||
updateSelector(model)
|
||||
updateSelector()
|
||||
|
||||
selectorView.topAnchor.constraint(equalTo: topAnchor).isActive = true
|
||||
selectorView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
|
||||
@ -167,23 +165,38 @@ open class RadioBoxBase<ModelType: RadioBoxModel>: Control<ModelType> {
|
||||
|
||||
}
|
||||
|
||||
func updateLabels(_ viewModel: ModelType) {
|
||||
func updateLabels() {
|
||||
|
||||
//add the stackview to hold the 2 labels
|
||||
//text label
|
||||
textLabel.set(with: viewModel.textModel)
|
||||
|
||||
textLabel.textPosition = .left
|
||||
textLabel.typograpicalStyle = .BoldBodyLarge
|
||||
textLabel.text = text
|
||||
textLabel.surface = surface
|
||||
textLabel.disabled = disabled
|
||||
textLabel.attributes = textAttributes
|
||||
|
||||
//subText label
|
||||
if let subTextModel = viewModel.subTextModel {
|
||||
subTextLabel.set(with: subTextModel)
|
||||
if let subText {
|
||||
subTextLabel.textPosition = .left
|
||||
subTextLabel.typograpicalStyle = .BodyLarge
|
||||
subTextLabel.text = subText
|
||||
subTextLabel.surface = surface
|
||||
subTextLabel.disabled = disabled
|
||||
subTextLabel.attributes = subTextAttributes
|
||||
subTextLabel.isHidden = false
|
||||
} else {
|
||||
subTextLabel.isHidden = true
|
||||
}
|
||||
|
||||
//subTextRight label
|
||||
if let subTextRightModel = viewModel.subTextRightModel {
|
||||
subTextRightLabel.set(with: subTextRightModel)
|
||||
if let subTextRight {
|
||||
subTextRightLabel.textPosition = .right
|
||||
subTextRightLabel.typograpicalStyle = .BodyLarge
|
||||
subTextRightLabel.text = subTextRight
|
||||
subTextRightLabel.surface = surface
|
||||
subTextRightLabel.disabled = disabled
|
||||
subTextRightLabel.attributes = subTextRightAttributes
|
||||
subTextRightLabel.isHidden = false
|
||||
} else {
|
||||
subTextRightLabel.isHidden = true
|
||||
@ -192,7 +205,7 @@ open class RadioBoxBase<ModelType: RadioBoxModel>: Control<ModelType> {
|
||||
|
||||
public override func reset() {
|
||||
super.reset()
|
||||
updateSelector(model)
|
||||
updateSelector()
|
||||
setAccessibilityLabel()
|
||||
}
|
||||
|
||||
@ -206,14 +219,12 @@ open class RadioBoxBase<ModelType: RadioBoxModel>: Control<ModelType> {
|
||||
//--------------------------------------------------
|
||||
// MARK: - State
|
||||
//--------------------------------------------------
|
||||
open override func updateView(viewModel: ModelType) {
|
||||
updateLabels(viewModel)
|
||||
updateSelector(viewModel)
|
||||
open override func updateView() {
|
||||
updateLabels()
|
||||
updateSelector()
|
||||
setAccessibilityHint()
|
||||
setAccessibilityValue(viewModel.selected)
|
||||
setAccessibilityLabel(viewModel.selected)
|
||||
setNeedsLayout()
|
||||
layoutIfNeeded()
|
||||
setAccessibilityValue(isSelected)
|
||||
setAccessibilityLabel(isSelected)
|
||||
}
|
||||
|
||||
|
||||
@ -256,11 +267,11 @@ open class RadioBoxBase<ModelType: RadioBoxModel>: Control<ModelType> {
|
||||
|
||||
private var shapeLayer: CAShapeLayer?
|
||||
|
||||
open func updateSelector(_ viewModel: ModelType) {
|
||||
open func updateSelector() {
|
||||
//get the colors
|
||||
let backgroundColor = radioBoxBackgroundColorConfiguration.getColor(viewModel)
|
||||
let borderColor = radioBoxBorderColorConfiguration.getColor(viewModel)
|
||||
let borderWidth = viewModel.selected ? selectorBorderWidthSelected : selectorBorderWidth
|
||||
let backgroundColor = radioBoxBackgroundColorConfiguration.getColor(self)
|
||||
let borderColor = radioBoxBorderColorConfiguration.getColor(self)
|
||||
let borderWidth = isSelected ? selectorBorderWidthSelected : selectorBorderWidth
|
||||
|
||||
selectorView.backgroundColor = backgroundColor
|
||||
selectorView.layer.borderColor = borderColor.cgColor
|
||||
@ -278,12 +289,12 @@ open class RadioBoxBase<ModelType: RadioBoxModel>: Control<ModelType> {
|
||||
|
||||
open override func draw(_ layer: CALayer, in ctx: CGContext) {
|
||||
|
||||
let borderColor = radioBoxBorderColorConfiguration.getColor(model)
|
||||
let borderColor = radioBoxBorderColorConfiguration.getColor(self)
|
||||
|
||||
shapeLayer?.removeFromSuperlayer()
|
||||
shapeLayer = nil
|
||||
|
||||
if model.strikethrough {
|
||||
if strikethrough {
|
||||
let bounds = selectorView.bounds
|
||||
let length = max(bounds.size.height, bounds.size.width)
|
||||
guard length > 0.0, shapeLayer == nil else { return }
|
||||
|
||||
@ -8,7 +8,8 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
public class RadioBoxGroup: RadioBoxGroupBase<DefaultRadioBoxGroupModel, RadioBox> {
|
||||
@objc(VDSRadioBoxGroup)
|
||||
public class RadioBoxGroup: RadioBoxGroupBase<RadioBox> {
|
||||
|
||||
public override func didSelect(_ selectedControl: RadioBox) {
|
||||
let oldSelectedControl = selectorViews.filter { $0.isSelected == true }.first
|
||||
@ -18,8 +19,26 @@ public class RadioBoxGroup: RadioBoxGroupBase<DefaultRadioBoxGroupModel, RadioBo
|
||||
}
|
||||
}
|
||||
|
||||
public class RadioBoxGroupBase<GroupModelType: RadioBoxGroupModel, ModelHandlerType: RadioBoxBase<GroupModelType.SelectorModelType>>: SelectorGroupSelectedHandlerBase<GroupModelType, ModelHandlerType> {
|
||||
|
||||
public class RadioBoxGroupBase<HandlerType: RadioBoxBase>: SelectorGroupSelectedHandlerBase<HandlerType> {
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Public Properties
|
||||
//--------------------------------------------------
|
||||
public override var selectorViews: [HandlerType] {
|
||||
didSet {
|
||||
for selector in selectorViews {
|
||||
if !mainStackView.arrangedSubviews.contains(selector) {
|
||||
selector
|
||||
.publisher(for: .touchUpInside)
|
||||
.sink { [weak self] handler in
|
||||
self?.didSelect(handler)
|
||||
}.store(in: &subscribers)
|
||||
mainStackView.addArrangedSubview(selector)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Private Properties
|
||||
//--------------------------------------------------
|
||||
@ -68,31 +87,4 @@ public class RadioBoxGroupBase<GroupModelType: RadioBoxGroupModel, ModelHandlerT
|
||||
}
|
||||
}.store(in: &subscribers)
|
||||
}
|
||||
|
||||
open override func updateView(viewModel: ModelType) {
|
||||
for selectorModel in viewModel.selectors {
|
||||
//see if view is there for the model
|
||||
if let foundSelectorView = findSelectorView(id: selectorModel.id) {
|
||||
foundSelectorView.set(with: selectorModel)
|
||||
} else {
|
||||
|
||||
//create view
|
||||
let newSelectorView = createModelHandler(selector: selectorModel)
|
||||
|
||||
self.selectorViews.append(newSelectorView)
|
||||
mainStackView.addArrangedSubview(newSelectorView)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var selectedModelHandler: ModelHandlerType? {
|
||||
if let index = selectorViews.firstIndex(where: { element in
|
||||
return element.isSelected == true
|
||||
}) {
|
||||
return selectorViews[index]
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,26 +0,0 @@
|
||||
//
|
||||
// RadioBoxGroupModel.swift
|
||||
// VDS
|
||||
//
|
||||
// Created by Matt Bruce on 8/23/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol RadioBoxGroupModel: SelectorGroupSelectedModelable where SelectorModelType: RadioBoxModel { }
|
||||
|
||||
public struct DefaultRadioBoxGroupModel: RadioBoxGroupModel {
|
||||
public typealias SelectorModelType = DefaultRadioBoxModel
|
||||
public var id = UUID()
|
||||
public var inputId: String?
|
||||
public var value: AnyHashable?
|
||||
public var surface: Surface = .light
|
||||
public var disabled: Bool = false
|
||||
public var selectors: [SelectorModelType]
|
||||
public var showError: Bool = false
|
||||
public var errorText: String?
|
||||
public init() { selectors = [] }
|
||||
public init(selectors: [SelectorModelType]){
|
||||
self.selectors = selectors
|
||||
}
|
||||
}
|
||||
@ -1,122 +0,0 @@
|
||||
//
|
||||
// RadioBoxModel.swift
|
||||
// VDS
|
||||
//
|
||||
// Created by Matt Bruce on 8/23/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
public protocol RadioBoxModel: Modelable, FormFieldable, DataTrackable, Accessable, Selectable, BinaryColorable {
|
||||
var text: String { get set }
|
||||
var textAttributes: [any LabelAttributeModel]? { get set }
|
||||
var subText: String? { get set }
|
||||
var subTextAttributes: [any LabelAttributeModel]? { get set }
|
||||
var subTextRight: String? { get set }
|
||||
var subTextRightAttributes: [any LabelAttributeModel]? { get set }
|
||||
var strikethrough: Bool { get set }
|
||||
}
|
||||
|
||||
extension RadioBoxModel {
|
||||
|
||||
public var textModel: DefaultLabelModel {
|
||||
var model = DefaultLabelModel()
|
||||
model.textPosition = .left
|
||||
model.typograpicalStyle = .BoldBodyLarge
|
||||
model.text = text
|
||||
model.surface = surface
|
||||
model.disabled = disabled
|
||||
model.attributes = textAttributes
|
||||
return model
|
||||
}
|
||||
|
||||
public var subTextModel: DefaultLabelModel? {
|
||||
guard let subText else { return nil }
|
||||
var model = DefaultLabelModel()
|
||||
model.textPosition = .left
|
||||
model.typograpicalStyle = .BodyLarge
|
||||
model.text = subText
|
||||
model.surface = surface
|
||||
model.disabled = disabled
|
||||
model.attributes = subTextAttributes
|
||||
return model
|
||||
}
|
||||
|
||||
public var subTextRightModel: DefaultLabelModel? {
|
||||
guard let subTextRight else { return nil }
|
||||
var model = DefaultLabelModel()
|
||||
model.textPosition = .right
|
||||
model.typograpicalStyle = .BodyLarge
|
||||
model.text = subTextRight
|
||||
model.surface = surface
|
||||
model.disabled = disabled
|
||||
model.attributes = subTextRightAttributes
|
||||
return model
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public struct DefaultRadioBoxModel: RadioBoxModel {
|
||||
public var id = UUID()
|
||||
public var selected: Bool = false
|
||||
public var text: String = "Default Text"
|
||||
public var textAttributes: [any LabelAttributeModel]?
|
||||
public var subText: String?
|
||||
public var subTextAttributes: [any LabelAttributeModel]?
|
||||
public var subTextRight: String?
|
||||
public var subTextRightAttributes: [any LabelAttributeModel]?
|
||||
public var selectedAccentColor: UIColor?
|
||||
public var strikethrough: Bool = false
|
||||
|
||||
public var inputId: String?
|
||||
public var value: AnyHashable?
|
||||
|
||||
public var surface: Surface = .light
|
||||
public var disabled: Bool = false
|
||||
|
||||
public var dataAnalyticsTrack: String?
|
||||
public var dataClickStream: String?
|
||||
public var dataTrack: String?
|
||||
public var accessibilityHintEnabled: String?
|
||||
public var accessibilityHintDisabled: String?
|
||||
public var accessibilityValueEnabled: String?
|
||||
public var accessibilityValueDisabled: String?
|
||||
public var accessibilityLabelEnabled: String?
|
||||
public var accessibilityLabelDisabled: String?
|
||||
|
||||
public init() {}
|
||||
|
||||
public static func == (lhs: DefaultRadioBoxModel, rhs: DefaultRadioBoxModel) -> Bool {
|
||||
lhs.isEqual(rhs)
|
||||
}
|
||||
|
||||
public func isEqual(_ equatable: DefaultRadioBoxModel) -> Bool {
|
||||
return id == equatable.id
|
||||
&& selected == equatable.selected
|
||||
&& text == equatable.text
|
||||
&& textAttributes == equatable.textAttributes
|
||||
&& subText == equatable.subText
|
||||
&& subTextAttributes == equatable.subTextAttributes
|
||||
&& subTextRight == equatable.subTextRight
|
||||
&& subTextRightAttributes == equatable.subTextRightAttributes
|
||||
&& selectedAccentColor == equatable.selectedAccentColor
|
||||
&& strikethrough == equatable.strikethrough
|
||||
|
||||
&& inputId == equatable.inputId
|
||||
&& value == equatable.value
|
||||
|
||||
&& surface == equatable.surface
|
||||
&& disabled == equatable.disabled
|
||||
|
||||
&& dataAnalyticsTrack == equatable.dataAnalyticsTrack
|
||||
&& dataClickStream == equatable.dataClickStream
|
||||
&& dataTrack == equatable.dataTrack
|
||||
&& accessibilityHintEnabled == equatable.accessibilityHintEnabled
|
||||
&& accessibilityHintDisabled == equatable.accessibilityHintDisabled
|
||||
&& accessibilityValueEnabled == equatable.accessibilityValueEnabled
|
||||
&& accessibilityValueDisabled == equatable.accessibilityValueDisabled
|
||||
&& accessibilityLabelEnabled == equatable.accessibilityLabelEnabled
|
||||
&& accessibilityLabelDisabled == equatable.accessibilityLabelDisabled
|
||||
}
|
||||
}
|
||||
@ -10,7 +10,8 @@ import UIKit
|
||||
import VDSColorTokens
|
||||
import VDSFormControlsTokens
|
||||
|
||||
public class RadioButton: RadioButtonBase<DefaultRadioButtonModel>{
|
||||
@objc(VDSRadioButton)
|
||||
public class RadioButton: RadioButtonBase {
|
||||
//for groups allows "toggle"
|
||||
open override func toggle() {
|
||||
//removed error
|
||||
@ -21,7 +22,8 @@ public class RadioButton: RadioButtonBase<DefaultRadioButtonModel>{
|
||||
}
|
||||
}
|
||||
|
||||
public class SoloRadioButton: RadioButtonBase<DefaultRadioButtonModel>{
|
||||
@objc(VDSSoloRadioButton)
|
||||
public class SoloRadioButton: RadioButtonBase {
|
||||
public override func initialSetup() {
|
||||
super.initialSetup()
|
||||
publisher(for: .touchUpInside)
|
||||
@ -31,11 +33,36 @@ public class SoloRadioButton: RadioButtonBase<DefaultRadioButtonModel>{
|
||||
}
|
||||
}
|
||||
|
||||
open class RadioButtonBase<ModelType: RadioButtonModel>: Control<ModelType> {
|
||||
@objc(VDSRadioButtonBase)
|
||||
open class RadioButtonBase: Control, Accessable, DataTrackable, BinaryColorable, Errorable {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializers
|
||||
//--------------------------------------------------
|
||||
required public init() {
|
||||
super.init(frame: .zero)
|
||||
}
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
super.init(frame: .zero)
|
||||
}
|
||||
|
||||
public required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Private Properties
|
||||
//--------------------------------------------------
|
||||
private var shouldShowError: Bool {
|
||||
guard showError && !disabled && errorText?.isEmpty == false else { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
private var shouldShowLabels: Bool {
|
||||
guard labelText?.isEmpty == false || childText?.isEmpty == false else { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
private var mainStackView: UIStackView = {
|
||||
return UIStackView().with {
|
||||
$0.translatesAutoresizingMaskIntoConstraints = false
|
||||
@ -73,61 +100,40 @@ open class RadioButtonBase<ModelType: RadioButtonModel>: Control<ModelType> {
|
||||
$0.translatesAutoresizingMaskIntoConstraints = false
|
||||
}
|
||||
}()
|
||||
|
||||
//can't bind to @Proxy
|
||||
open override var isSelected: Bool {
|
||||
get { model.selected }
|
||||
set {
|
||||
if model.selected != newValue {
|
||||
model.selected = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Proxy(\.model.labelText)
|
||||
open var labelText: String?
|
||||
open var labelText: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.childText)
|
||||
open var childText: String?
|
||||
open var labelTextAttributes: [any LabelAttributeModel]? { didSet { didChange() }}
|
||||
|
||||
open var childText: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.showError)
|
||||
open var showError: Bool
|
||||
|
||||
@Proxy(\.model.errorText)
|
||||
open var errorText: String?
|
||||
open var childTextAttributes: [any LabelAttributeModel]? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.inputId)
|
||||
open var inputId: String?
|
||||
open var showError: Bool = false { didSet { didChange() }}
|
||||
|
||||
open var errorText: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.value)
|
||||
open var value: AnyHashable?
|
||||
|
||||
@Proxy(\.model.dataAnalyticsTrack)
|
||||
open var dataAnalyticsTrack: String?
|
||||
open var inputId: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.dataClickStream)
|
||||
open var dataClickStream: String?
|
||||
open var value: AnyHashable? { didSet { didChange() }}
|
||||
|
||||
open var dataAnalyticsTrack: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.dataTrack)
|
||||
open var dataTrack: String?
|
||||
open var dataClickStream: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityHintEnabled)
|
||||
open var accessibilityHintEnabled: String?
|
||||
open var dataTrack: String? { didSet { didChange() }}
|
||||
|
||||
open var accessibilityHintEnabled: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityHintDisabled)
|
||||
open var accessibilityHintDisabled: String?
|
||||
open var accessibilityHintDisabled: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityValueEnabled)
|
||||
open var accessibilityValueEnabled: String?
|
||||
open var accessibilityValueEnabled: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityValueDisabled)
|
||||
open var accessibilityValueDisabled: String?
|
||||
open var accessibilityValueDisabled: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityLabelEnabled)
|
||||
open var accessibilityLabelEnabled: String?
|
||||
open var accessibilityLabelEnabled: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityLabelDisabled)
|
||||
open var accessibilityLabelDisabled: String?
|
||||
open var accessibilityLabelDisabled: String? { didSet { didChange() }}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Constraints
|
||||
@ -167,7 +173,7 @@ open class RadioButtonBase<ModelType: RadioButtonModel>: Control<ModelType> {
|
||||
selectorWidthConstraint = selectorView.widthAnchor.constraint(equalToConstant: selectorSize.width)
|
||||
selectorWidthConstraint?.isActive = true
|
||||
|
||||
updateSelector(model)
|
||||
updateSelector()
|
||||
|
||||
mainStackView.topAnchor.constraint(equalTo: topAnchor).isActive = true
|
||||
mainStackView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
|
||||
@ -176,22 +182,32 @@ open class RadioButtonBase<ModelType: RadioButtonModel>: Control<ModelType> {
|
||||
|
||||
}
|
||||
|
||||
func updateLabels(_ viewModel: ModelType) {
|
||||
func updateLabels() {
|
||||
|
||||
//deal with labels
|
||||
if viewModel.shouldShowLabels {
|
||||
if shouldShowLabels {
|
||||
//add the stackview to hold the 2 labels
|
||||
//top label
|
||||
if let labelModel = viewModel.labelModel {
|
||||
primaryLabel.set(with: labelModel)
|
||||
if let labelText {
|
||||
primaryLabel.textPosition = .left
|
||||
primaryLabel.typograpicalStyle = .BoldBodyLarge
|
||||
primaryLabel.text = labelText
|
||||
primaryLabel.surface = surface
|
||||
primaryLabel.disabled = disabled
|
||||
primaryLabel.attributes = labelTextAttributes
|
||||
primaryLabel.isHidden = false
|
||||
} else {
|
||||
primaryLabel.isHidden = true
|
||||
}
|
||||
|
||||
//bottom label
|
||||
if let childModel = viewModel.childModel {
|
||||
secondaryLabel.set(with: childModel)
|
||||
if let childText {
|
||||
secondaryLabel.textPosition = .left
|
||||
secondaryLabel.typograpicalStyle = .BodyLarge
|
||||
secondaryLabel.text = childText
|
||||
secondaryLabel.surface = surface
|
||||
secondaryLabel.disabled = disabled
|
||||
secondaryLabel.attributes = childTextAttributes
|
||||
secondaryLabel.isHidden = false
|
||||
} else {
|
||||
secondaryLabel.isHidden = true
|
||||
@ -207,8 +223,12 @@ open class RadioButtonBase<ModelType: RadioButtonModel>: Control<ModelType> {
|
||||
}
|
||||
|
||||
//either add/remove the error from the main stack
|
||||
if let errorModel = model.errorModel, model.shouldShowError {
|
||||
errorLabel.set(with: errorModel)
|
||||
if let errorText, shouldShowError {
|
||||
errorLabel.textPosition = .left
|
||||
errorLabel.typograpicalStyle = .BodyMedium
|
||||
errorLabel.text = errorText
|
||||
errorLabel.surface = surface
|
||||
errorLabel.disabled = disabled
|
||||
mainStackView.spacing = 8
|
||||
errorLabel.isHidden = false
|
||||
} else {
|
||||
@ -220,7 +240,7 @@ open class RadioButtonBase<ModelType: RadioButtonModel>: Control<ModelType> {
|
||||
|
||||
public override func reset() {
|
||||
super.reset()
|
||||
updateSelector(model)
|
||||
updateSelector()
|
||||
setAccessibilityLabel()
|
||||
}
|
||||
|
||||
@ -239,14 +259,12 @@ open class RadioButtonBase<ModelType: RadioButtonModel>: Control<ModelType> {
|
||||
//--------------------------------------------------
|
||||
// MARK: - State
|
||||
//--------------------------------------------------
|
||||
open override func updateView(viewModel: ModelType) {
|
||||
updateLabels(viewModel)
|
||||
updateSelector(viewModel)
|
||||
open override func updateView() {
|
||||
updateLabels()
|
||||
updateSelector()
|
||||
setAccessibilityHint()
|
||||
setAccessibilityValue(viewModel.selected)
|
||||
setAccessibilityLabel(viewModel.selected)
|
||||
setNeedsLayout()
|
||||
layoutIfNeeded()
|
||||
setAccessibilityValue(isSelected)
|
||||
setAccessibilityLabel(isSelected)
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
@ -295,7 +313,7 @@ open class RadioButtonBase<ModelType: RadioButtonModel>: Control<ModelType> {
|
||||
radioButtonSize
|
||||
}
|
||||
|
||||
open func updateSelector(_ viewModel: ModelType) {
|
||||
open func updateSelector() {
|
||||
|
||||
if let shapeLayer = shapeLayer, let sublayers = layer.sublayers, sublayers.contains(shapeLayer) {
|
||||
shapeLayer.removeFromSuperlayer()
|
||||
@ -307,9 +325,9 @@ open class RadioButtonBase<ModelType: RadioButtonModel>: Control<ModelType> {
|
||||
guard length > 0.0, shapeLayer == nil else { return }
|
||||
|
||||
//get the colors
|
||||
let backgroundColor = radioButtonBackgroundColorConfiguration.getColor(viewModel)
|
||||
let borderColor = radioButtonBorderColorConfiguration.getColor(viewModel)
|
||||
let radioSelectedColor = radioButtonCheckColorConfiguration.getColor(viewModel)
|
||||
let backgroundColor = radioButtonBackgroundColorConfiguration.getColor(self)
|
||||
let borderColor = radioButtonBorderColorConfiguration.getColor(self)
|
||||
let radioSelectedColor = radioButtonCheckColorConfiguration.getColor(self)
|
||||
|
||||
selectorView.backgroundColor = backgroundColor
|
||||
selectorView.layer.borderColor = borderColor.cgColor
|
||||
|
||||
@ -8,11 +8,11 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
public class RadioButtonGroup: RadioButtonGroupBase<DefaultRadioButtonGroupModel, RadioButton> {
|
||||
@objc(VDSRadioButtonGroup)
|
||||
public class RadioButtonGroup: RadioButtonGroupBase<RadioButton> {
|
||||
|
||||
public override func didSelect(_ selectedControl: RadioButton) {
|
||||
let oldSelectedControl = selectorViews.filter { $0.isSelected == true }.first
|
||||
oldSelectedControl?.toggle()
|
||||
selectedHandler?.toggle()
|
||||
selectedControl.toggle()
|
||||
if showError {
|
||||
showError = false
|
||||
@ -21,22 +21,38 @@ public class RadioButtonGroup: RadioButtonGroupBase<DefaultRadioButtonGroupModel
|
||||
}
|
||||
}
|
||||
|
||||
public class RadioButtonGroupBase<GroupModelType: RadioButtonGroupModel, ModelHandlerType: RadioButtonBase<GroupModelType.SelectorModelType>>: SelectorGroupSelectedHandlerBase<GroupModelType, ModelHandlerType> {
|
||||
public class RadioButtonGroupBase<HandlerType: RadioButtonBase>: SelectorGroupSelectedHandlerBase<HandlerType> {
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Public Properties
|
||||
//--------------------------------------------------
|
||||
public override var selectorViews: [HandlerType] {
|
||||
didSet {
|
||||
for selector in selectorViews {
|
||||
if !mainStackView.arrangedSubviews.contains(selector) {
|
||||
selector
|
||||
.publisher(for: .touchUpInside)
|
||||
.sink { [weak self] handler in
|
||||
self?.didSelect(handler)
|
||||
}.store(in: &subscribers)
|
||||
mainStackView.addArrangedSubview(selector)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var _showError: Bool = false
|
||||
public var showError: Bool {
|
||||
get { model.showError }
|
||||
get { _showError }
|
||||
set {
|
||||
var newShowError = newValue
|
||||
if selectedModel != nil, newShowError {
|
||||
if selectedHandler != nil, newShowError {
|
||||
newShowError = false
|
||||
}
|
||||
selectorViews.forEach { handler in
|
||||
handler.showError = newShowError
|
||||
}
|
||||
model.showError = newShowError
|
||||
_showError = newShowError
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,30 +84,4 @@ public class RadioButtonGroupBase<GroupModelType: RadioButtonGroupModel, ModelHa
|
||||
mainStackView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
|
||||
mainStackView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
|
||||
}
|
||||
|
||||
open override func updateView(viewModel: ModelType) {
|
||||
for selectorModel in viewModel.selectors {
|
||||
//see if view is there for the model
|
||||
if let foundSelectorView = findSelectorView(id: selectorModel.id) {
|
||||
foundSelectorView.set(with: selectorModel)
|
||||
} else {
|
||||
|
||||
//create view
|
||||
let newSelectorView = createModelHandler(selector: selectorModel)
|
||||
|
||||
self.selectorViews.append(newSelectorView)
|
||||
mainStackView.addArrangedSubview(newSelectorView)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var selectedModelHandler: ModelHandlerType? {
|
||||
if let index = selectorViews.firstIndex(where: { element in
|
||||
return element.isSelected == true
|
||||
}) {
|
||||
return selectorViews[index]
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,30 +0,0 @@
|
||||
//
|
||||
// RadioButtonGroupModel.swift
|
||||
// VDS
|
||||
//
|
||||
// Created by Matt Bruce on 8/11/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol RadioButtonGroupModel: SelectorGroupSelectedModelable, Errorable where SelectorModelType: RadioButtonModel { }
|
||||
|
||||
extension RadioButtonGroupModel {
|
||||
public var errorText: String? { return nil }
|
||||
}
|
||||
|
||||
public struct DefaultRadioButtonGroupModel: RadioButtonGroupModel {
|
||||
public typealias SelectorModelType = DefaultRadioButtonModel
|
||||
public var id = UUID()
|
||||
public var inputId: String?
|
||||
public var value: AnyHashable?
|
||||
public var surface: Surface = .light
|
||||
public var disabled: Bool = false
|
||||
public var selectors: [SelectorModelType]
|
||||
public var showError: Bool = false
|
||||
public var errorText: String?
|
||||
public init() { selectors = [] }
|
||||
public init(selectors: [SelectorModelType]){
|
||||
self.selectors = selectors
|
||||
}
|
||||
}
|
||||
@ -1,128 +0,0 @@
|
||||
//
|
||||
// ToggleModel.swift
|
||||
// VDS
|
||||
//
|
||||
// Created by Matt Bruce on 7/22/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
public protocol RadioButtonModel: Modelable, FormFieldable, Errorable, DataTrackable, Accessable, Selectable, BinaryColorable {
|
||||
var labelText: String? { get set }
|
||||
var labelTextAttributes: [any LabelAttributeModel]? { get set }
|
||||
var childText: String? { get set }
|
||||
var childTextAttributes: [any LabelAttributeModel]? { get set }
|
||||
}
|
||||
|
||||
extension RadioButtonModel {
|
||||
|
||||
public var shouldShowError: Bool {
|
||||
guard showError && !disabled && errorText?.isEmpty == false else { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
public var shouldShowLabels: Bool {
|
||||
guard labelText?.isEmpty == false || childText?.isEmpty == false else { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
public var labelModel: DefaultLabelModel? {
|
||||
guard let labelText = labelText else { return nil }
|
||||
var model = DefaultLabelModel()
|
||||
model.textPosition = .left
|
||||
model.typograpicalStyle = .BoldBodyLarge
|
||||
model.text = labelText
|
||||
model.surface = surface
|
||||
model.disabled = disabled
|
||||
model.attributes = labelTextAttributes
|
||||
return model
|
||||
}
|
||||
|
||||
public var childModel: DefaultLabelModel? {
|
||||
guard let childText = childText else { return nil }
|
||||
var model = DefaultLabelModel()
|
||||
model.textPosition = .left
|
||||
model.typograpicalStyle = .BodyLarge
|
||||
model.text = childText
|
||||
model.surface = surface
|
||||
model.disabled = disabled
|
||||
model.attributes = childTextAttributes
|
||||
return model
|
||||
}
|
||||
|
||||
public var errorModel: DefaultLabelModel? {
|
||||
guard let errorText = errorText, showError else { return nil }
|
||||
var model = DefaultLabelModel()
|
||||
model.textPosition = .left
|
||||
model.typograpicalStyle = .BodyMedium
|
||||
model.text = errorText
|
||||
model.surface = surface
|
||||
model.disabled = disabled
|
||||
return model
|
||||
}
|
||||
}
|
||||
|
||||
public struct DefaultRadioButtonModel: RadioButtonModel {
|
||||
public var id = UUID()
|
||||
public var selected: Bool = false
|
||||
|
||||
public var labelText: String?
|
||||
public var labelTextAttributes: [any LabelAttributeModel]?
|
||||
public var childText: String?
|
||||
public var childTextAttributes: [any LabelAttributeModel]?
|
||||
|
||||
public var showError: Bool = false
|
||||
public var errorText: String?
|
||||
|
||||
public var inputId: String?
|
||||
public var value: AnyHashable?
|
||||
|
||||
public var surface: Surface = .light
|
||||
public var disabled: Bool = false
|
||||
|
||||
public var dataAnalyticsTrack: String?
|
||||
public var dataClickStream: String?
|
||||
public var dataTrack: String?
|
||||
public var accessibilityHintEnabled: String?
|
||||
public var accessibilityHintDisabled: String?
|
||||
public var accessibilityValueEnabled: String?
|
||||
public var accessibilityValueDisabled: String?
|
||||
public var accessibilityLabelEnabled: String?
|
||||
public var accessibilityLabelDisabled: String?
|
||||
|
||||
public init() {}
|
||||
|
||||
public static func == (lhs: DefaultRadioButtonModel, rhs: DefaultRadioButtonModel) -> Bool {
|
||||
lhs.isEqual(rhs)
|
||||
}
|
||||
|
||||
public func isEqual(_ equatable: DefaultRadioButtonModel) -> Bool {
|
||||
return id == equatable.id
|
||||
&& selected == equatable.selected
|
||||
|
||||
&& labelText == equatable.labelText
|
||||
&& labelTextAttributes == equatable.labelTextAttributes
|
||||
&& childText == equatable.childText
|
||||
&& childTextAttributes == equatable.childTextAttributes
|
||||
|
||||
&& showError == equatable.showError
|
||||
&& errorText == equatable.errorText
|
||||
|
||||
&& inputId == equatable.inputId
|
||||
&& value == equatable.value
|
||||
|
||||
&& surface == equatable.surface
|
||||
&& disabled == equatable.disabled
|
||||
|
||||
&& dataAnalyticsTrack == equatable.dataAnalyticsTrack
|
||||
&& dataClickStream == equatable.dataClickStream
|
||||
&& dataTrack == equatable.dataTrack
|
||||
&& accessibilityHintEnabled == equatable.accessibilityHintEnabled
|
||||
&& accessibilityHintDisabled == equatable.accessibilityHintDisabled
|
||||
&& accessibilityValueEnabled == equatable.accessibilityValueEnabled
|
||||
&& accessibilityValueDisabled == equatable.accessibilityValueDisabled
|
||||
&& accessibilityLabelEnabled == equatable.accessibilityLabelEnabled
|
||||
&& accessibilityLabelDisabled == equatable.accessibilityLabelDisabled
|
||||
}
|
||||
}
|
||||
@ -11,7 +11,8 @@ import VDSColorTokens
|
||||
import VDSFormControlsTokens
|
||||
import Combine
|
||||
|
||||
public class RadioSwatch: RadioSwatchBase<DefaultRadioSwatchModel>{
|
||||
@objc(VDSRadioSwatch)
|
||||
public class RadioSwatch: RadioSwatchBase{
|
||||
public override func initialSetup() {
|
||||
super.initialSetup()
|
||||
publisher(for: .touchUpInside)
|
||||
@ -21,8 +22,24 @@ public class RadioSwatch: RadioSwatchBase<DefaultRadioSwatchModel>{
|
||||
}
|
||||
}
|
||||
|
||||
open class RadioSwatchBase<ModelType: RadioSwatchModel>: Control<ModelType> {
|
||||
|
||||
@objc(VDSRadioSwatchBase)
|
||||
open class RadioSwatchBase: Control, Accessable, DataTrackable, BinaryColorable {
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializers
|
||||
//--------------------------------------------------
|
||||
required public init() {
|
||||
super.init(frame: .zero)
|
||||
}
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
super.init(frame: .zero)
|
||||
}
|
||||
|
||||
public required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Public Properties
|
||||
//--------------------------------------------------
|
||||
@ -38,55 +55,38 @@ open class RadioSwatchBase<ModelType: RadioSwatchModel>: Control<ModelType> {
|
||||
$0.contentMode = .scaleAspectFit
|
||||
}
|
||||
}()
|
||||
|
||||
open var fillImage: UIImage? { didSet { didChange() }}
|
||||
|
||||
open var text: String = "" { didSet { didChange() }}
|
||||
|
||||
open var primaryColor: UIColor? { didSet { didChange() }}
|
||||
|
||||
open var secondaryColor: UIColor? { didSet { didChange() }}
|
||||
|
||||
open var strikethrough: Bool = false { didSet { didChange() }}
|
||||
|
||||
//can't bind to @Proxy
|
||||
open override var isSelected: Bool {
|
||||
get { model.selected }
|
||||
set {
|
||||
if model.selected != newValue {
|
||||
model.selected = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Proxy(\.model.text)
|
||||
open var text: String
|
||||
|
||||
@Proxy(\.model.strikethrough)
|
||||
open var strikethrough: Bool
|
||||
open var inputId: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.inputId)
|
||||
open var inputId: String?
|
||||
open var value: AnyHashable? { didSet { didChange() }}
|
||||
|
||||
open var dataAnalyticsTrack: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.value)
|
||||
open var value: AnyHashable?
|
||||
|
||||
@Proxy(\.model.dataAnalyticsTrack)
|
||||
open var dataAnalyticsTrack: String?
|
||||
open var dataClickStream: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.dataClickStream)
|
||||
open var dataClickStream: String?
|
||||
open var dataTrack: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.dataTrack)
|
||||
open var dataTrack: String?
|
||||
|
||||
@Proxy(\.model.accessibilityHintEnabled)
|
||||
open var accessibilityHintEnabled: String?
|
||||
open var accessibilityHintEnabled: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityHintDisabled)
|
||||
open var accessibilityHintDisabled: String?
|
||||
open var accessibilityHintDisabled: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityValueEnabled)
|
||||
open var accessibilityValueEnabled: String?
|
||||
open var accessibilityValueEnabled: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityValueDisabled)
|
||||
open var accessibilityValueDisabled: String?
|
||||
open var accessibilityValueDisabled: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityLabelEnabled)
|
||||
open var accessibilityLabelEnabled: String?
|
||||
open var accessibilityLabelEnabled: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityLabelDisabled)
|
||||
open var accessibilityLabelDisabled: String?
|
||||
open var accessibilityLabelDisabled: String? { didSet { didChange() }}
|
||||
|
||||
//functions
|
||||
//--------------------------------------------------
|
||||
@ -106,9 +106,7 @@ open class RadioSwatchBase<ModelType: RadioSwatchModel>: Control<ModelType> {
|
||||
addSubview(selectorView)
|
||||
|
||||
selectorView.addSubview(fillView)
|
||||
|
||||
updateSelector(model)
|
||||
|
||||
|
||||
selectorView.topAnchor.constraint(equalTo: topAnchor).isActive = true
|
||||
selectorView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
|
||||
selectorView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
|
||||
@ -128,7 +126,7 @@ open class RadioSwatchBase<ModelType: RadioSwatchModel>: Control<ModelType> {
|
||||
|
||||
public override func reset() {
|
||||
super.reset()
|
||||
updateSelector(model)
|
||||
setNeedsDisplay()
|
||||
setAccessibilityLabel()
|
||||
}
|
||||
|
||||
@ -140,15 +138,11 @@ open class RadioSwatchBase<ModelType: RadioSwatchModel>: Control<ModelType> {
|
||||
//--------------------------------------------------
|
||||
// MARK: - State
|
||||
//--------------------------------------------------
|
||||
open override func updateView(viewModel: ModelType) {
|
||||
let enabled = !viewModel.disabled
|
||||
|
||||
updateSelector(viewModel)
|
||||
open override func updateView() {
|
||||
setAccessibilityHint()
|
||||
setAccessibilityValue(viewModel.selected)
|
||||
setAccessibilityLabel(viewModel.selected)
|
||||
setNeedsLayout()
|
||||
layoutIfNeeded()
|
||||
setAccessibilityValue(isSelected)
|
||||
setAccessibilityLabel(isSelected)
|
||||
setNeedsDisplay()
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
@ -201,49 +195,6 @@ open class RadioSwatchBase<ModelType: RadioSwatchModel>: Control<ModelType> {
|
||||
return swatchSize
|
||||
}
|
||||
|
||||
open func updateSelector(_ viewModel: ModelType) {
|
||||
//get the colors
|
||||
let backgroundColor = radioSwatchBackgroundColorConfiguration.getColor(viewModel)
|
||||
let borderColor = viewModel.selected ? radioSwatchBorderColorConfiguration.getColor(viewModel) : .clear
|
||||
let fillBorderColor = radioSwatchFillBorderColorConfiguration.getColor(viewModel)
|
||||
selectorView.backgroundColor = backgroundColor
|
||||
selectorView.layer.borderColor = borderColor.cgColor
|
||||
selectorView.layer.cornerRadius = selectorView.bounds.width * 0.5
|
||||
selectorView.layer.borderWidth = viewModel.selected ? selectorBorderWidth : 0
|
||||
selectorView.layer.masksToBounds = true
|
||||
|
||||
gradientLayer?.removeFromSuperlayer()
|
||||
gradientLayer = nil
|
||||
|
||||
var fillColorBackground: UIColor = .clear
|
||||
|
||||
if let fillImage = viewModel.fillImage {
|
||||
fillView.image = viewModel.disabled ? fillImage.image(alpha: disabledAlpha) : fillImage
|
||||
|
||||
} else {
|
||||
fillView.image = nil
|
||||
if let primary = viewModel.primaryColor, let secondary = viewModel.secondaryColor {
|
||||
let firstColor = viewModel.disabled ? primary.withAlphaComponent(disabledAlpha) : primary
|
||||
let secondColor = viewModel.disabled ? secondary.withAlphaComponent(disabledAlpha) : secondary
|
||||
let gradient = CAGradientLayer()
|
||||
gradientLayer = gradient
|
||||
gradient.frame = fillView.bounds
|
||||
gradient.colors = [secondColor.cgColor, secondColor.cgColor, firstColor.cgColor, firstColor.cgColor]
|
||||
gradient.locations = [NSNumber(value: 0.0), NSNumber(value: 0.5), NSNumber(value: 0.5), NSNumber(value: 1.0)]
|
||||
gradient.transform = CATransform3DMakeRotation(135.0 / 180.0 * .pi, 0.0, 0.0, 1.0)
|
||||
fillView.layer.addSublayer(gradient)
|
||||
} else {
|
||||
fillColorBackground = viewModel.primaryColor ?? .white
|
||||
}
|
||||
}
|
||||
fillView.backgroundColor = viewModel.disabled ? fillColorBackground.withAlphaComponent(disabledAlpha) : fillColorBackground
|
||||
fillView.layer.borderColor = fillBorderColor.cgColor
|
||||
fillView.layer.cornerRadius = fillView.bounds.width * 0.5
|
||||
fillView.layer.borderWidth = selectorBorderWidth
|
||||
fillView.layer.masksToBounds = true
|
||||
setNeedsDisplay()
|
||||
}
|
||||
|
||||
open override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
// Accounts for any size changes
|
||||
@ -252,12 +203,51 @@ open class RadioSwatchBase<ModelType: RadioSwatchModel>: Control<ModelType> {
|
||||
|
||||
open override func draw(_ layer: CALayer, in ctx: CGContext) {
|
||||
|
||||
let borderColor = radioSwatchBorderColorConfiguration.getColor(model)
|
||||
let backgroundColor = radioSwatchBackgroundColorConfiguration.getColor(self)
|
||||
let borderColor = isSelected ? radioSwatchBorderColorConfiguration.getColor(self) : .clear
|
||||
let fillBorderColor = radioSwatchFillBorderColorConfiguration.getColor(self)
|
||||
selectorView.backgroundColor = backgroundColor
|
||||
selectorView.layer.borderColor = borderColor.cgColor
|
||||
selectorView.layer.cornerRadius = selectorView.bounds.width * 0.5
|
||||
selectorView.layer.borderWidth = isSelected ? selectorBorderWidth : 0
|
||||
selectorView.layer.masksToBounds = true
|
||||
|
||||
gradientLayer?.removeFromSuperlayer()
|
||||
gradientLayer = nil
|
||||
|
||||
var fillColorBackground: UIColor = .clear
|
||||
|
||||
if let fillImage {
|
||||
fillView.image = disabled ? fillImage.image(alpha: disabledAlpha) : fillImage
|
||||
|
||||
} else {
|
||||
fillView.image = nil
|
||||
if let primary = primaryColor, let secondary = secondaryColor {
|
||||
let firstColor = disabled ? primary.withAlphaComponent(disabledAlpha) : primary
|
||||
let secondColor = disabled ? secondary.withAlphaComponent(disabledAlpha) : secondary
|
||||
let gradient = CAGradientLayer()
|
||||
gradientLayer = gradient
|
||||
gradient.frame = fillView.bounds
|
||||
gradient.colors = [secondColor.cgColor, secondColor.cgColor, firstColor.cgColor, firstColor.cgColor]
|
||||
gradient.locations = [NSNumber(value: 0.0), NSNumber(value: 0.5), NSNumber(value: 0.5), NSNumber(value: 1.0)]
|
||||
gradient.transform = CATransform3DMakeRotation(135.0 / 180.0 * .pi, 0.0, 0.0, 1.0)
|
||||
fillView.layer.addSublayer(gradient)
|
||||
} else {
|
||||
fillColorBackground = primaryColor ?? .white
|
||||
}
|
||||
}
|
||||
|
||||
fillView.backgroundColor = disabled ? fillColorBackground.withAlphaComponent(disabledAlpha) : fillColorBackground
|
||||
fillView.layer.borderColor = fillBorderColor.cgColor
|
||||
fillView.layer.cornerRadius = fillView.bounds.width * 0.5
|
||||
fillView.layer.borderWidth = selectorBorderWidth
|
||||
fillView.layer.masksToBounds = true
|
||||
|
||||
shapeLayer?.removeFromSuperlayer()
|
||||
shapeLayer = nil
|
||||
|
||||
if model.strikethrough {
|
||||
if strikethrough {
|
||||
let strikeThroughBorderColor = radioSwatchBorderColorConfiguration.getColor(self)
|
||||
let bounds = selectorView.bounds
|
||||
let length = max(bounds.size.height, bounds.size.width)
|
||||
guard length > 0.0, shapeLayer == nil else { return }
|
||||
@ -267,7 +257,7 @@ open class RadioSwatchBase<ModelType: RadioSwatchModel>: Control<ModelType> {
|
||||
strikeThrough.fillColor = nil
|
||||
strikeThrough.opacity = 1.0
|
||||
strikeThrough.lineWidth = strikeThroughLineThickness
|
||||
strikeThrough.strokeColor = borderColor.cgColor
|
||||
strikeThrough.strokeColor = strikeThroughBorderColor.cgColor
|
||||
|
||||
let linePath = UIBezierPath()
|
||||
linePath.move(to: CGPoint(x: 0, y: bounds.height))
|
||||
|
||||
@ -9,29 +9,29 @@ import Foundation
|
||||
import UIKit
|
||||
import Combine
|
||||
|
||||
public class RadioSwatchGroup: RadioSwatchGroupBase<DefaultRadioSwatchGroupModel, RadioSwatch> {
|
||||
@objc(VDSRadioSwatchGroup)
|
||||
public class RadioSwatchGroup: RadioSwatchGroupBase<RadioSwatch> {
|
||||
|
||||
public override func didSelect(selector: RadioSwatch) {
|
||||
if let index = model.selectors.firstIndex(where: {$0.selected == true }),
|
||||
let cell = collectionView.cellForItem(at: IndexPath(item: index, section: 0)) as? CollectionViewCell<RadioSwatch> {
|
||||
cell.modelHandler.toggle()
|
||||
}
|
||||
selectedHandler?.toggle()
|
||||
selector.toggle()
|
||||
label.text = selector.model.text
|
||||
label.text = selector.text
|
||||
valueChanged()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class RadioSwatchGroupBase<GroupModelType: RadioSwatchGroupModel, ModelHandlerType: RadioSwatchBase<GroupModelType.SelectorModelType>>: Control<GroupModelType>, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UICollectionViewDelegate {
|
||||
public class RadioSwatchGroupBase<HandlerType: RadioSwatchBase>: SelectorGroupSelectedHandlerBase<HandlerType>, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UICollectionViewDelegate {
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Public Properties
|
||||
//--------------------------------------------------
|
||||
public var selectedModel: ModelHandlerType.ModelType? {
|
||||
return model.selectedModel
|
||||
public override var selectorViews: [HandlerType] {
|
||||
didSet {
|
||||
collectionView.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Private Properties
|
||||
//--------------------------------------------------
|
||||
@ -55,7 +55,7 @@ public class RadioSwatchGroupBase<GroupModelType: RadioSwatchGroupModel, ModelHa
|
||||
$0.showsVerticalScrollIndicator = false
|
||||
$0.isScrollEnabled = false
|
||||
$0.translatesAutoresizingMaskIntoConstraints = false
|
||||
$0.register(CollectionViewCell<ModelHandlerType>.self, forCellWithReuseIdentifier: "collectionViewCell")
|
||||
$0.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "collectionViewCell")
|
||||
}
|
||||
}()
|
||||
|
||||
@ -64,13 +64,19 @@ public class RadioSwatchGroupBase<GroupModelType: RadioSwatchGroupModel, ModelHa
|
||||
//--------------------------------------------------
|
||||
override public var disabled: Bool {
|
||||
didSet {
|
||||
updateSelectors()
|
||||
for selector in selectorViews {
|
||||
selector.disabled = disabled
|
||||
}
|
||||
collectionView.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
override public var surface: Surface {
|
||||
didSet {
|
||||
updateSelectors()
|
||||
for selector in selectorViews {
|
||||
selector.surface = surface
|
||||
}
|
||||
collectionView.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,16 +115,14 @@ public class RadioSwatchGroupBase<GroupModelType: RadioSwatchGroupModel, ModelHa
|
||||
}
|
||||
|
||||
open func setHeight() {
|
||||
let swatches = model.selectors
|
||||
|
||||
guard swatches.count > 0 else {
|
||||
guard selectorViews.count > 0 else {
|
||||
collectionViewHeight?.constant = 0
|
||||
return
|
||||
}
|
||||
|
||||
// Calculate the height
|
||||
let swatchesInRow = floor(CGFloat(collectionView.bounds.width/(cellSize + itemSpacing)))
|
||||
let numberOfRows = ceil(CGFloat(swatches.count)/swatchesInRow)
|
||||
let numberOfRows = ceil(CGFloat(selectorViews.count)/swatchesInRow)
|
||||
let height = (numberOfRows * cellSize) + (itemSpacing * (numberOfRows-1))
|
||||
|
||||
collectionViewHeight?.constant = CGFloat(height)
|
||||
@ -130,29 +134,13 @@ public class RadioSwatchGroupBase<GroupModelType: RadioSwatchGroupModel, ModelHa
|
||||
collectionView.dataSource = self
|
||||
}
|
||||
|
||||
open override func updateView(viewModel: ModelType) {
|
||||
label.set(with: viewModel.labelModel)
|
||||
open override func updateView() {
|
||||
label.textPosition = .left
|
||||
label.typograpicalStyle = .BodySmall
|
||||
label.text = selectedHandler?.text ?? " "
|
||||
label.surface = surface
|
||||
label.disabled = disabled
|
||||
collectionView.reloadData()
|
||||
setNeedsLayout()
|
||||
}
|
||||
|
||||
//Refactor into new CollectionView Selector protocol
|
||||
public func updateSelectors(){
|
||||
let selectors = model.selectors.compactMap { existing in
|
||||
return existing.copyWith {
|
||||
$0.disabled = disabled
|
||||
$0.surface = surface
|
||||
}
|
||||
}
|
||||
model.selectors = selectors
|
||||
}
|
||||
|
||||
public func replace(viewModel: ModelHandlerType.ModelType){
|
||||
if let index = model.selectors.firstIndex(where: { element in
|
||||
return element.id == viewModel.id
|
||||
}) {
|
||||
model.selectors[index] = viewModel
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
@ -166,12 +154,11 @@ public class RadioSwatchGroupBase<GroupModelType: RadioSwatchGroupModel, ModelHa
|
||||
// MARK: - UICollectionViewDelegate
|
||||
//--------------------------------------------------
|
||||
open func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
|
||||
return !model.selectors[indexPath.row].disabled
|
||||
return !selectorViews[indexPath.row].disabled
|
||||
}
|
||||
|
||||
open func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||
guard let cell = collectionView.cellForItem(at: indexPath) as? CollectionViewCell<ModelHandlerType> else { return }
|
||||
didSelect(selector: cell.modelHandler)
|
||||
didSelect(selector: selectorViews[indexPath.row])
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
@ -182,53 +169,25 @@ public class RadioSwatchGroupBase<GroupModelType: RadioSwatchGroupModel, ModelHa
|
||||
}
|
||||
|
||||
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
return model.selectors.count
|
||||
return selectorViews.count
|
||||
}
|
||||
|
||||
var cellsubs: [Int: AnyCancellable] = [:]
|
||||
|
||||
|
||||
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath) as? CollectionViewCell<ModelHandlerType> else { return UICollectionViewCell() }
|
||||
|
||||
let model = model.selectors[indexPath.row]
|
||||
cell.modelHandler.isUserInteractionEnabled = false
|
||||
|
||||
//cancel if sub exists
|
||||
if let sub = cellsubs[indexPath.row] {
|
||||
sub.cancel()
|
||||
cellsubs[indexPath.row] = nil
|
||||
}
|
||||
|
||||
let sub = cell.modelHandler
|
||||
.handlerPublisher()
|
||||
.sink { [weak self] changed in
|
||||
if cell.modelHandler.shouldUpdateView(viewModel: model) {
|
||||
print("Model Change: \(changed)")
|
||||
self?.replace(viewModel: changed)
|
||||
}
|
||||
}
|
||||
cellsubs[indexPath.row] = sub
|
||||
|
||||
cell.set(with: model)
|
||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath)
|
||||
let handler = selectorViews[indexPath.row]
|
||||
handler.isUserInteractionEnabled = false
|
||||
cell.subviews.forEach { $0.removeFromSuperview() }
|
||||
cell.addSubview(handler)
|
||||
handler.topAnchor.constraint(equalTo: cell.topAnchor).isActive = true
|
||||
handler.leadingAnchor.constraint(equalTo: cell.leadingAnchor).isActive = true
|
||||
handler.trailingAnchor.constraint(equalTo: cell.trailingAnchor).isActive = true
|
||||
handler.bottomAnchor.constraint(equalTo: cell.bottomAnchor).isActive = true
|
||||
return cell
|
||||
|
||||
}
|
||||
|
||||
open func didSelect(selector: ModelHandlerType) {
|
||||
open func didSelect(selector: HandlerType) {
|
||||
fatalError("Must override didSelect")
|
||||
}
|
||||
|
||||
public func valueChanged() {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + Constants.ModelStateDebounce) { [weak self] in
|
||||
self?.sendActions(for: .valueChanged)
|
||||
}
|
||||
}
|
||||
|
||||
public var selectedModelHandler: ModelHandlerType? {
|
||||
guard let index = model.selectors.firstIndex(where: {$0.selected == true }),
|
||||
let cell = collectionView.cellForItem(at: IndexPath(item: index, section: 0)) as? CollectionViewCell<ModelHandlerType> else {
|
||||
return nil
|
||||
}
|
||||
return cell.modelHandler
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,36 +0,0 @@
|
||||
//
|
||||
// RadioSwatchGroupModel.swift
|
||||
// VDS
|
||||
//
|
||||
// Created by Matt Bruce on 8/25/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol RadioSwatchGroupModel: SelectorGroupSelectedModelable, Equatable where SelectorModelType: RadioSwatchModel { }
|
||||
|
||||
extension RadioSwatchGroupModel {
|
||||
public var labelModel: DefaultLabelModel {
|
||||
var model = DefaultLabelModel()
|
||||
model.textPosition = .left
|
||||
model.typograpicalStyle = .BodySmall
|
||||
model.text = selectedModel?.text ?? " "
|
||||
model.surface = surface
|
||||
model.disabled = disabled
|
||||
return model
|
||||
}
|
||||
}
|
||||
|
||||
public struct DefaultRadioSwatchGroupModel: RadioSwatchGroupModel {
|
||||
public typealias SelectorModelType = DefaultRadioSwatchModel
|
||||
public var id = UUID()
|
||||
public var inputId: String?
|
||||
public var value: AnyHashable?
|
||||
public var surface: Surface = .light
|
||||
public var disabled: Bool = false
|
||||
public var selectors: [SelectorModelType]
|
||||
public init() { selectors = [] }
|
||||
public init(selectors: [SelectorModelType]){
|
||||
self.selectors = selectors
|
||||
}
|
||||
}
|
||||
@ -1,47 +0,0 @@
|
||||
//
|
||||
// RadioSwatchModel.swift
|
||||
// VDS
|
||||
//
|
||||
// Created by Matt Bruce on 8/25/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
public protocol RadioSwatchModel: Modelable, FormFieldable, DataTrackable, Accessable, Selectable, BinaryColorable, Equatable {
|
||||
var fillImage: UIImage? { get set }
|
||||
var primaryColor: UIColor? { get set }
|
||||
var secondaryColor: UIColor? { get set }
|
||||
var text: String { get set }
|
||||
var strikethrough: Bool { get set }
|
||||
}
|
||||
|
||||
public struct DefaultRadioSwatchModel: RadioSwatchModel {
|
||||
public var id = UUID()
|
||||
public var selected: Bool = false
|
||||
|
||||
public var fillImage: UIImage?
|
||||
public var primaryColor: UIColor?
|
||||
public var secondaryColor: UIColor?
|
||||
public var text: String = ""
|
||||
public var strikethrough: Bool = false
|
||||
|
||||
public var inputId: String?
|
||||
public var value: AnyHashable?
|
||||
|
||||
public var surface: Surface = .light
|
||||
public var disabled: Bool = false
|
||||
|
||||
public var dataAnalyticsTrack: String?
|
||||
public var dataClickStream: String?
|
||||
public var dataTrack: String?
|
||||
public var accessibilityHintEnabled: String?
|
||||
public var accessibilityHintDisabled: String?
|
||||
public var accessibilityValueEnabled: String?
|
||||
public var accessibilityValueDisabled: String?
|
||||
public var accessibilityLabelEnabled: String?
|
||||
public var accessibilityLabelDisabled: String?
|
||||
|
||||
public init() {}
|
||||
|
||||
}
|
||||
@ -11,7 +11,25 @@ import VDSColorTokens
|
||||
import VDSFormControlsTokens
|
||||
import Combine
|
||||
|
||||
open class EntryField<ModelType: EntryFieldModel>: Control<ModelType> {
|
||||
public enum HelperTextPlacement: String, CaseIterable {
|
||||
case bottom, right
|
||||
}
|
||||
|
||||
open class EntryField: Control {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializers
|
||||
//--------------------------------------------------
|
||||
required public init() {
|
||||
super.init(frame: .zero)
|
||||
}
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
super.init(frame: .zero)
|
||||
}
|
||||
|
||||
public required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Private Properties
|
||||
@ -75,46 +93,32 @@ open class EntryField<ModelType: EntryFieldModel>: Control<ModelType> {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Public Properties
|
||||
//--------------------------------------------------
|
||||
@Proxy(\.model.labelText)
|
||||
open var labelText: String?
|
||||
|
||||
@Proxy(\.model.helperText)
|
||||
open var helperText: String?
|
||||
|
||||
@Proxy(\.model.showError)
|
||||
open var showError: Bool
|
||||
|
||||
@Proxy(\.model.errorText)
|
||||
open var errorText: String?
|
||||
|
||||
@Proxy(\.model.tooltipTitle)
|
||||
open var tooltipTitle: String?
|
||||
|
||||
@Proxy(\.model.tooltipContent)
|
||||
open var tooltipContent: String?
|
||||
|
||||
@Proxy(\.model.transparentBackground)
|
||||
open var transparentBackground: Bool
|
||||
|
||||
@Proxy(\.model.width)
|
||||
open var width: CGFloat?
|
||||
|
||||
@Proxy(\.model.maxLength)
|
||||
open var maxLength: Int?
|
||||
|
||||
@Proxy(\.model.inputId)
|
||||
open var inputId: String?
|
||||
|
||||
@Proxy(\.model.value)
|
||||
open var value: AnyHashable?
|
||||
|
||||
@Proxy(\.model.defaultVaue)
|
||||
open var defaultValue: AnyHashable?
|
||||
|
||||
@Proxy(\.model.required)
|
||||
open var required: Bool
|
||||
|
||||
@Proxy(\.model.readOnly)
|
||||
open var readOnly: Bool
|
||||
|
||||
|
||||
|
||||
@ -1,84 +1,84 @@
|
||||
////
|
||||
//// EntryFieldModel.swift
|
||||
//// VDS
|
||||
////
|
||||
//// Created by Matt Bruce on 10/3/22.
|
||||
////
|
||||
//
|
||||
// EntryFieldModel.swift
|
||||
// VDS
|
||||
//import Foundation
|
||||
//
|
||||
// Created by Matt Bruce on 10/3/22.
|
||||
//public enum HelperTextPlacement: String, CaseIterable {
|
||||
// case bottom, right
|
||||
//}
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum HelperTextPlacement: String, CaseIterable {
|
||||
case bottom, right
|
||||
}
|
||||
|
||||
public protocol EntryFieldModel: Modelable, FormFieldable, Errorable {
|
||||
var defaultVaue: AnyHashable? { get set }
|
||||
var required: Bool { get set }
|
||||
var readOnly: Bool { get set }
|
||||
var labelText: String? { get set }
|
||||
var helperText: String? { get set }
|
||||
var helperTextPlacement: HelperTextPlacement { get set }
|
||||
var transparentBackground: Bool { get set }
|
||||
var width: CGFloat? { get set }
|
||||
var maxLength: Int? { get set }
|
||||
var tooltipTitle: String? { get set }
|
||||
var tooltipContent: String? { get set }
|
||||
}
|
||||
|
||||
extension EntryFieldModel {
|
||||
|
||||
public var labelModel: DefaultLabelModel {
|
||||
var model = DefaultLabelModel()
|
||||
model.textPosition = .left
|
||||
model.typograpicalStyle = .BodySmall
|
||||
model.text = labelText
|
||||
model.surface = surface
|
||||
model.disabled = disabled
|
||||
return model
|
||||
}
|
||||
|
||||
public var helperLabelModel: DefaultLabelModel? {
|
||||
guard let helperText else { return nil }
|
||||
var model = DefaultLabelModel()
|
||||
model.textPosition = .left
|
||||
model.typograpicalStyle = .BodySmall
|
||||
model.text = helperText
|
||||
model.surface = surface
|
||||
model.disabled = disabled
|
||||
return model
|
||||
}
|
||||
|
||||
public var errorLabelModel: DefaultLabelModel? {
|
||||
guard let errorText else { return nil }
|
||||
var model = DefaultLabelModel()
|
||||
model.textPosition = .left
|
||||
model.typograpicalStyle = .BodySmall
|
||||
model.text = errorText
|
||||
model.surface = surface
|
||||
model.disabled = disabled
|
||||
return model
|
||||
}
|
||||
|
||||
public var tooltipTitleModel: DefaultLabelModel? {
|
||||
guard let tooltipTitle else { return nil }
|
||||
var model = DefaultLabelModel()
|
||||
model.textPosition = .left
|
||||
model.typograpicalStyle = .BodySmall
|
||||
model.text = tooltipTitle
|
||||
model.surface = surface
|
||||
model.disabled = disabled
|
||||
return model
|
||||
}
|
||||
|
||||
public var tooltipContentModel: DefaultLabelModel? {
|
||||
guard let tooltipContent else { return nil }
|
||||
var model = DefaultLabelModel()
|
||||
model.textPosition = .left
|
||||
model.typograpicalStyle = .BodySmall
|
||||
model.text = tooltipContent
|
||||
model.surface = surface
|
||||
model.disabled = disabled
|
||||
return model
|
||||
}
|
||||
|
||||
}
|
||||
//public protocol EntryFieldModel: Modelable, FormFieldable, Errorable {
|
||||
// var defaultVaue: AnyHashable? { get set }
|
||||
// var required: Bool { get set }
|
||||
// var readOnly: Bool { get set }
|
||||
// var labelText: String? { get set }
|
||||
// var helperText: String? { get set }
|
||||
// var helperTextPlacement: HelperTextPlacement { get set }
|
||||
// var transparentBackground: Bool { get set }
|
||||
// var width: CGFloat? { get set }
|
||||
// var maxLength: Int? { get set }
|
||||
// var tooltipTitle: String? { get set }
|
||||
// var tooltipContent: String? { get set }
|
||||
//}
|
||||
//
|
||||
//extension EntryFieldModel {
|
||||
//
|
||||
// public var labelModel: DefaultLabelModel {
|
||||
// var model = DefaultLabelModel()
|
||||
// model.textPosition = .left
|
||||
// model.typograpicalStyle = .BodySmall
|
||||
// model.text = labelText
|
||||
// model.surface = surface
|
||||
// model.disabled = disabled
|
||||
// return model
|
||||
// }
|
||||
//
|
||||
// public var helperLabelModel: DefaultLabelModel? {
|
||||
// guard let helperText else { return nil }
|
||||
// var model = DefaultLabelModel()
|
||||
// model.textPosition = .left
|
||||
// model.typograpicalStyle = .BodySmall
|
||||
// model.text = helperText
|
||||
// model.surface = surface
|
||||
// model.disabled = disabled
|
||||
// return model
|
||||
// }
|
||||
//
|
||||
// public var errorLabelModel: DefaultLabelModel? {
|
||||
// guard let errorText else { return nil }
|
||||
// var model = DefaultLabelModel()
|
||||
// model.textPosition = .left
|
||||
// model.typograpicalStyle = .BodySmall
|
||||
// model.text = errorText
|
||||
// model.surface = surface
|
||||
// model.disabled = disabled
|
||||
// return model
|
||||
// }
|
||||
//
|
||||
// public var tooltipTitleModel: DefaultLabelModel? {
|
||||
// guard let tooltipTitle else { return nil }
|
||||
// var model = DefaultLabelModel()
|
||||
// model.textPosition = .left
|
||||
// model.typograpicalStyle = .BodySmall
|
||||
// model.text = tooltipTitle
|
||||
// model.surface = surface
|
||||
// model.disabled = disabled
|
||||
// return model
|
||||
// }
|
||||
//
|
||||
// public var tooltipContentModel: DefaultLabelModel? {
|
||||
// guard let tooltipContent else { return nil }
|
||||
// var model = DefaultLabelModel()
|
||||
// model.textPosition = .left
|
||||
// model.typograpicalStyle = .BodySmall
|
||||
// model.text = tooltipContent
|
||||
// model.surface = surface
|
||||
// model.disabled = disabled
|
||||
// return model
|
||||
// }
|
||||
//
|
||||
//}
|
||||
|
||||
@ -11,10 +11,28 @@ import VDSColorTokens
|
||||
import VDSFormControlsTokens
|
||||
import Combine
|
||||
|
||||
public class TextEntryField: TextEntryFieldBase<DefaultTextEntryField>{}
|
||||
public enum TextEntryFieldType: String, CaseIterable {
|
||||
case text, number, calendar, inlineAction, password, creditCard, tel, date, securityCode
|
||||
}
|
||||
|
||||
open class TextEntryFieldBase<ModelType:TextEntryFieldModel>: EntryField<ModelType> {
|
||||
public class TextEntryField: TextEntryFieldBase{}
|
||||
|
||||
open class TextEntryFieldBase: EntryField {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializers
|
||||
//--------------------------------------------------
|
||||
required public init() {
|
||||
super.init(frame: .zero)
|
||||
}
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
super.init(frame: .zero)
|
||||
}
|
||||
|
||||
public required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Private Properties
|
||||
//--------------------------------------------------
|
||||
@ -31,16 +49,12 @@ open class TextEntryFieldBase<ModelType:TextEntryFieldModel>: EntryField<ModelTy
|
||||
// MARK: - Public Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
@Proxy(\.model.type)
|
||||
public var type: TextEntryFieldType
|
||||
|
||||
@Proxy(\.model.showSuccess)
|
||||
open var showSuccess: Bool
|
||||
|
||||
@Proxy(\.model.successText)
|
||||
open var successText: String?
|
||||
|
||||
@Proxy(\.model.helperTextPlacement)
|
||||
open var helperTextPlacement: HelperTextPlacement
|
||||
|
||||
private var successLabel = Label().with {
|
||||
|
||||
@ -1,67 +1,64 @@
|
||||
////
|
||||
//// TextEntryFieldModel.swift
|
||||
//// VDS
|
||||
////
|
||||
//// Created by Matt Bruce on 10/3/22.
|
||||
////
|
||||
//
|
||||
// TextEntryFieldModel.swift
|
||||
// VDS
|
||||
//import Foundation
|
||||
//
|
||||
// Created by Matt Bruce on 10/3/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum TextEntryFieldType: String, CaseIterable {
|
||||
case text, number, calendar, inlineAction, password, creditCard, tel, date, securityCode
|
||||
}
|
||||
|
||||
public protocol TextEntryFieldModel: EntryFieldModel {
|
||||
var type: TextEntryFieldType { get set }
|
||||
var showSuccess: Bool { get set }
|
||||
var successText: String? { get set }
|
||||
}
|
||||
|
||||
extension TextEntryFieldModel {
|
||||
|
||||
public var successLabelModel: DefaultLabelModel? {
|
||||
var model = DefaultLabelModel()
|
||||
model.textPosition = .left
|
||||
model.typograpicalStyle = .BodySmall
|
||||
model.text = successText
|
||||
model.surface = surface
|
||||
model.disabled = disabled
|
||||
return model
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public struct DefaultTextEntryField: TextEntryFieldModel {
|
||||
public var id = UUID()
|
||||
public var inputId: String?
|
||||
|
||||
public var type: TextEntryFieldType = .text
|
||||
public var value: AnyHashable?
|
||||
public var defaultVaue: AnyHashable?
|
||||
public var required: Bool = false
|
||||
public var readOnly: Bool = false
|
||||
|
||||
public var labelText: String?
|
||||
|
||||
public var helperText: String?
|
||||
public var helperTextPlacement: HelperTextPlacement = .bottom
|
||||
|
||||
public var showError: Bool = false
|
||||
public var errorText: String?
|
||||
|
||||
public var showSuccess: Bool = false
|
||||
public var successText: String?
|
||||
|
||||
public var transparentBackground: Bool = false
|
||||
public var width: CGFloat?
|
||||
public var maxLength: Int?
|
||||
|
||||
public var tooltipTitle: String?
|
||||
public var tooltipContent: String?
|
||||
|
||||
public var surface: Surface = .light
|
||||
public var disabled: Bool = false
|
||||
|
||||
public init() { }
|
||||
}
|
||||
//public protocol TextEntryFieldModel: EntryFieldModel {
|
||||
// var type: TextEntryFieldType { get set }
|
||||
// var showSuccess: Bool { get set }
|
||||
// var successText: String? { get set }
|
||||
//}
|
||||
//
|
||||
//extension TextEntryFieldModel {
|
||||
//
|
||||
// public var successLabelModel: DefaultLabelModel? {
|
||||
// var model = DefaultLabelModel()
|
||||
// model.textPosition = .left
|
||||
// model.typograpicalStyle = .BodySmall
|
||||
// model.text = successText
|
||||
// model.surface = surface
|
||||
// model.disabled = disabled
|
||||
// return model
|
||||
// }
|
||||
//
|
||||
//}
|
||||
//
|
||||
//
|
||||
//public struct DefaultTextEntryField: TextEntryFieldModel {
|
||||
// public var id = UUID()
|
||||
// public var inputId: String?
|
||||
//
|
||||
// public var type: TextEntryFieldType = .text
|
||||
// public var value: AnyHashable?
|
||||
// public var defaultVaue: AnyHashable?
|
||||
// public var required: Bool = false
|
||||
// public var readOnly: Bool = false
|
||||
//
|
||||
// public var labelText: String?
|
||||
//
|
||||
// public var helperText: String?
|
||||
// public var helperTextPlacement: HelperTextPlacement = .bottom
|
||||
//
|
||||
// public var showError: Bool = false
|
||||
// public var errorText: String?
|
||||
//
|
||||
// public var showSuccess: Bool = false
|
||||
// public var successText: String?
|
||||
//
|
||||
// public var transparentBackground: Bool = false
|
||||
// public var width: CGFloat?
|
||||
// public var maxLength: Int?
|
||||
//
|
||||
// public var tooltipTitle: String?
|
||||
// public var tooltipContent: String?
|
||||
//
|
||||
// public var surface: Surface = .light
|
||||
// public var disabled: Bool = false
|
||||
//
|
||||
// public init() { }
|
||||
//}
|
||||
|
||||
@ -9,6 +9,19 @@ import Foundation
|
||||
import UIKit
|
||||
import VDSColorTokens
|
||||
import Combine
|
||||
|
||||
public enum ToggleTextSize: String, CaseIterable {
|
||||
case small, large
|
||||
}
|
||||
|
||||
public enum ToggleTextWeight: String, CaseIterable {
|
||||
case regular, bold
|
||||
}
|
||||
|
||||
public enum ToggleTextPosition: String, CaseIterable {
|
||||
case left, right
|
||||
}
|
||||
|
||||
/**
|
||||
A custom implementation of Apple's UISwitch.
|
||||
|
||||
@ -17,8 +30,8 @@ import Combine
|
||||
Container: The background of the toggle control.
|
||||
Knob: The circular indicator that slides on the container.
|
||||
*/
|
||||
|
||||
public class Toggle: ToggleBase<DefaultToggleModel>{
|
||||
@objc(VDSToggle)
|
||||
public class Toggle: ToggleBase{
|
||||
public override func initialSetup() {
|
||||
super.initialSetup()
|
||||
publisher(for: .touchUpInside)
|
||||
@ -28,7 +41,23 @@ public class Toggle: ToggleBase<DefaultToggleModel>{
|
||||
}
|
||||
}
|
||||
|
||||
open class ToggleBase<ModelType: ToggleModel>: Control<ModelType> {
|
||||
@objc(VDSToggleBase)
|
||||
open class ToggleBase: Control, Accessable, DataTrackable, BinaryColorable {
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializers
|
||||
//--------------------------------------------------
|
||||
required public init() {
|
||||
super.init(frame: .zero)
|
||||
}
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
super.init(frame: .zero)
|
||||
}
|
||||
|
||||
public required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Private Properties
|
||||
@ -86,136 +115,93 @@ open class ToggleBase<ModelType: ToggleModel>: Control<ModelType> {
|
||||
$0.forFalse.disabled.darkColor = VDSColor.paletteGray44
|
||||
}
|
||||
|
||||
private var typograpicalStyle: TypographicalStyle {
|
||||
if textSize == .small {
|
||||
if textWeight == .bold {
|
||||
return .BoldBodySmall
|
||||
} else {
|
||||
return .BodySmall
|
||||
}
|
||||
} else {
|
||||
if textWeight == .bold {
|
||||
return .BoldBodyLarge
|
||||
} else {
|
||||
return .BodyLarge
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Public Properties
|
||||
//--------------------------------------------------
|
||||
@Proxy(\.model.on)
|
||||
open var isOn: Bool
|
||||
open var isOn: Bool = false { didSet { didChange() }}
|
||||
|
||||
open var isAnimated: Bool = true { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.showText)
|
||||
public var showText: Bool {
|
||||
didSet {
|
||||
if oldValue != showText {
|
||||
updateLabel(model)
|
||||
}
|
||||
}
|
||||
}
|
||||
open var showText: Bool = false { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.onText)
|
||||
public var onText: String
|
||||
open var onText: String = "On" { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.offText)
|
||||
public var offText: String
|
||||
open var offText: String = "Off" { didSet { didChange() }}
|
||||
|
||||
public var fontSize: TypographicalStyle.FontSize = .small{
|
||||
didSet {
|
||||
updateTypography()
|
||||
}
|
||||
}
|
||||
open var textSize: ToggleTextSize = .small { didSet { didChange() }}
|
||||
|
||||
public var isBold: Bool = false {
|
||||
didSet {
|
||||
updateTypography()
|
||||
}
|
||||
}
|
||||
open var textWeight: ToggleTextWeight = .regular { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.textPosition)
|
||||
public var textPosition: TextPosition {
|
||||
didSet {
|
||||
if oldValue != textPosition {
|
||||
updateLabel(model)
|
||||
}
|
||||
}
|
||||
}
|
||||
open var textPosition: ToggleTextPosition = .left { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.inputId)
|
||||
open var inputId: String?
|
||||
open var inputId: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.value)
|
||||
open var value: AnyHashable?
|
||||
open var value: AnyHashable? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.dataAnalyticsTrack)
|
||||
open var dataAnalyticsTrack: String?
|
||||
open var dataAnalyticsTrack: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.dataClickStream)
|
||||
open var dataClickStream: String?
|
||||
open var dataClickStream: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.dataTrack)
|
||||
open var dataTrack: String?
|
||||
open var dataTrack: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityHintEnabled)
|
||||
open var accessibilityHintEnabled: String?
|
||||
open var accessibilityHintEnabled: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityHintDisabled)
|
||||
open var accessibilityHintDisabled: String?
|
||||
open var accessibilityHintDisabled: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityValueEnabled)
|
||||
open var accessibilityValueEnabled: String?
|
||||
open var accessibilityValueEnabled: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityValueDisabled)
|
||||
open var accessibilityValueDisabled: String?
|
||||
open var accessibilityValueDisabled: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityLabelEnabled)
|
||||
open var accessibilityLabelEnabled: String?
|
||||
open var accessibilityLabelEnabled: String? { didSet { didChange() }}
|
||||
|
||||
@Proxy(\.model.accessibilityLabelDisabled)
|
||||
open var accessibilityLabelDisabled: String?
|
||||
open var accessibilityLabelDisabled: String? { didSet { didChange() }}
|
||||
|
||||
//only allows
|
||||
//fontSize: small, large
|
||||
//fontWeight: regular, bold
|
||||
private func updateTypography() {
|
||||
if fontSize == .small {
|
||||
if isBold {
|
||||
model.typograpicalStyle = .BoldBodySmall
|
||||
} else {
|
||||
model.typograpicalStyle = .BodySmall
|
||||
}
|
||||
} else {
|
||||
if isBold {
|
||||
model.typograpicalStyle = .BoldBodyLarge
|
||||
} else {
|
||||
model.typograpicalStyle = .BodyLarge
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Constraints
|
||||
//--------------------------------------------------
|
||||
private var knobLeadingConstraint: NSLayoutConstraint?
|
||||
private var knobTrailingConstraint: NSLayoutConstraint?
|
||||
private var knobHeightConstraint: NSLayoutConstraint?
|
||||
private var knobWidthConstraint: NSLayoutConstraint?
|
||||
private var toggleHeightConstraint: NSLayoutConstraint?
|
||||
private var toggleWidthConstraint: NSLayoutConstraint?
|
||||
|
||||
//functions
|
||||
//--------------------------------------------------
|
||||
// MARK: - Toggle
|
||||
//--------------------------------------------------
|
||||
private func updateToggle(_ viewModel: ModelType) {
|
||||
//private func
|
||||
func constrainKnob(){
|
||||
self.knobLeadingConstraint?.isActive = false
|
||||
self.knobTrailingConstraint?.isActive = false
|
||||
if viewModel.on {
|
||||
self.knobTrailingConstraint = self.toggleView.trailingAnchor.constraint(equalTo: self.knobView.trailingAnchor, constant: 2)
|
||||
self.knobLeadingConstraint = self.knobView.leadingAnchor.constraint(greaterThanOrEqualTo: self.toggleView.leadingAnchor)
|
||||
} else {
|
||||
self.knobTrailingConstraint = self.toggleView.trailingAnchor.constraint(greaterThanOrEqualTo: self.knobView.trailingAnchor)
|
||||
self.knobLeadingConstraint = self.knobView.leadingAnchor.constraint(equalTo: self.toggleView.leadingAnchor, constant: 2)
|
||||
}
|
||||
self.knobTrailingConstraint?.isActive = true
|
||||
self.knobLeadingConstraint?.isActive = true
|
||||
self.knobWidthConstraint?.constant = self.knobSize.width
|
||||
self.layoutIfNeeded()
|
||||
private func constrainKnob(){
|
||||
self.knobLeadingConstraint?.isActive = false
|
||||
self.knobTrailingConstraint?.isActive = false
|
||||
if isOn {
|
||||
knobTrailingConstraint = toggleView.trailingAnchor.constraint(equalTo: knobView.trailingAnchor, constant: 2)
|
||||
knobLeadingConstraint = knobView.leadingAnchor.constraint(greaterThanOrEqualTo: toggleView.leadingAnchor)
|
||||
} else {
|
||||
knobTrailingConstraint = toggleView.trailingAnchor.constraint(greaterThanOrEqualTo: knobView.trailingAnchor)
|
||||
knobLeadingConstraint = knobView.leadingAnchor.constraint(equalTo: toggleView.leadingAnchor, constant: 2)
|
||||
}
|
||||
knobTrailingConstraint?.isActive = true
|
||||
knobLeadingConstraint?.isActive = true
|
||||
self.layoutIfNeeded()
|
||||
}
|
||||
|
||||
private func updateToggle() {
|
||||
let toggleColor = toggleColorConfiguration.getColor(self)
|
||||
let knobColor = knobColorConfiguration.getColor(self)
|
||||
|
||||
let toggleColor = toggleColorConfiguration.getColor(viewModel)
|
||||
let knobColor = knobColorConfiguration.getColor(viewModel)
|
||||
|
||||
if viewModel.disabled {
|
||||
if disabled || !isAnimated {
|
||||
toggleView.backgroundColor = toggleColor
|
||||
knobView.backgroundColor = knobColor
|
||||
constrainKnob()
|
||||
@ -225,8 +211,8 @@ open class ToggleBase<ModelType: ToggleModel>: Control<ModelType> {
|
||||
self.knobView.backgroundColor = knobColor
|
||||
}, completion: nil)
|
||||
|
||||
UIView.animate(withDuration: 0.33, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.5, options: [], animations: {
|
||||
constrainKnob()
|
||||
UIView.animate(withDuration: 0.33, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.5, options: [], animations: { [weak self] in
|
||||
self?.constrainKnob()
|
||||
}, completion: nil)
|
||||
}
|
||||
}
|
||||
@ -234,8 +220,8 @@ open class ToggleBase<ModelType: ToggleModel>: Control<ModelType> {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Labels
|
||||
//--------------------------------------------------
|
||||
private func updateLabel(_ viewModel: ModelType) {
|
||||
let showText = viewModel.showText
|
||||
private func updateLabel() {
|
||||
|
||||
stackView.spacing = showText ? 12 : 0
|
||||
|
||||
if stackView.subviews.contains(label) {
|
||||
@ -243,6 +229,12 @@ open class ToggleBase<ModelType: ToggleModel>: Control<ModelType> {
|
||||
}
|
||||
|
||||
if showText {
|
||||
label.textPosition = textPosition == .left ? .left : .right
|
||||
label.typograpicalStyle = typograpicalStyle
|
||||
label.text = isOn ? onText : offText
|
||||
label.surface = surface
|
||||
label.disabled = disabled
|
||||
|
||||
if textPosition == .left {
|
||||
stackView.insertArrangedSubview(label, at: 0)
|
||||
} else {
|
||||
@ -265,75 +257,68 @@ open class ToggleBase<ModelType: ToggleModel>: Control<ModelType> {
|
||||
isAccessibilityElement = true
|
||||
accessibilityTraits = .button
|
||||
addSubview(stackView)
|
||||
|
||||
//create the wrapping view
|
||||
|
||||
//set the h/w to container size, since the width "can" grow if text is there
|
||||
//allow this to be greaterThanEqualTo
|
||||
heightAnchor.constraint(equalToConstant: toggleContainerSize.height).isActive = true
|
||||
widthAnchor.constraint(greaterThanOrEqualToConstant: toggleContainerSize.width).isActive = true
|
||||
|
||||
//create the container for the toggle/knob
|
||||
let toggleContainerView = UIView()
|
||||
toggleContainerView.translatesAutoresizingMaskIntoConstraints = false
|
||||
toggleContainerView.backgroundColor = .clear
|
||||
toggleContainerView.widthAnchor.constraint(equalToConstant: toggleContainerSize.width).isActive = true
|
||||
toggleContainerView.heightAnchor.constraint(equalToConstant: toggleContainerSize.height).isActive = true
|
||||
|
||||
toggleHeightConstraint = toggleView.heightAnchor.constraint(equalToConstant: toggleSize.height)
|
||||
toggleHeightConstraint?.isActive = true
|
||||
|
||||
toggleWidthConstraint = toggleView.widthAnchor.constraint(equalToConstant: toggleSize.width)
|
||||
toggleWidthConstraint?.isActive = true
|
||||
|
||||
toggleView.layer.cornerRadius = toggleSize.height / 2.0
|
||||
knobView.layer.cornerRadius = knobSize.height / 2.0
|
||||
|
||||
toggleView.backgroundColor = toggleColorConfiguration.getColor(model)
|
||||
|
||||
//adding views
|
||||
toggleContainerView.addSubview(toggleView)
|
||||
toggleView.addSubview(knobView)
|
||||
|
||||
|
||||
knobHeightConstraint = knobView.heightAnchor.constraint(equalToConstant: knobSize.height)
|
||||
knobHeightConstraint?.isActive = true
|
||||
knobWidthConstraint = knobView.widthAnchor.constraint(equalToConstant: knobSize.width)
|
||||
knobWidthConstraint?.isActive = true
|
||||
stackView.addArrangedSubview(toggleContainerView)
|
||||
|
||||
//adding constraints
|
||||
toggleView.heightAnchor.constraint(equalToConstant: toggleSize.height).isActive = true
|
||||
toggleView.widthAnchor.constraint(equalToConstant: toggleSize.width).isActive = true
|
||||
toggleView.layer.cornerRadius = toggleSize.height / 2.0
|
||||
toggleView.bottomAnchor.constraint(greaterThanOrEqualTo: knobView.bottomAnchor).isActive = true
|
||||
toggleView.centerXAnchor.constraint(equalTo: toggleContainerView.centerXAnchor).isActive = true
|
||||
toggleView.centerYAnchor.constraint(equalTo: toggleContainerView.centerYAnchor).isActive = true
|
||||
|
||||
knobView.layer.cornerRadius = knobSize.height / 2.0
|
||||
knobView.heightAnchor.constraint(equalToConstant: knobSize.height).isActive = true
|
||||
knobView.widthAnchor.constraint(equalToConstant: knobSize.width).isActive = true
|
||||
knobView.centerYAnchor.constraint(equalTo: toggleView.centerYAnchor).isActive = true
|
||||
knobView.topAnchor.constraint(greaterThanOrEqualTo: toggleView.topAnchor).isActive = true
|
||||
|
||||
toggleView.bottomAnchor.constraint(greaterThanOrEqualTo: knobView.bottomAnchor).isActive = true
|
||||
|
||||
updateLabel(model)
|
||||
stackView.addArrangedSubview(toggleContainerView)
|
||||
//pin stackview to edges
|
||||
stackView.topAnchor.constraint(equalTo: topAnchor).isActive = true
|
||||
stackView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
|
||||
stackView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
|
||||
stackView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
|
||||
|
||||
toggleView.centerXAnchor.constraint(equalTo: toggleContainerView.centerXAnchor).isActive = true
|
||||
toggleView.centerYAnchor.constraint(equalTo: toggleContainerView.centerYAnchor).isActive = true
|
||||
|
||||
}
|
||||
|
||||
public override func reset() {
|
||||
super.reset()
|
||||
toggleView.backgroundColor = toggleColorConfiguration.getColor(model)
|
||||
knobView.backgroundColor = knobColorConfiguration.getColor(model)
|
||||
toggleView.backgroundColor = toggleColorConfiguration.getColor(self)
|
||||
knobView.backgroundColor = knobColorConfiguration.getColor(self)
|
||||
setAccessibilityLabel()
|
||||
}
|
||||
|
||||
/// This will toggle the state of the Toggle and execute the actionBlock if provided.
|
||||
open func toggle() {
|
||||
isOn.toggle()
|
||||
isSelected = isOn
|
||||
sendActions(for: .valueChanged)
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - State
|
||||
//--------------------------------------------------
|
||||
open override func updateView(viewModel: ModelType) {
|
||||
label.set(with: viewModel.label)
|
||||
updateLabel(viewModel)
|
||||
updateToggle(viewModel)
|
||||
open override func updateView() {
|
||||
updateLabel()
|
||||
updateToggle()
|
||||
setAccessibilityHint()
|
||||
setAccessibilityValue(viewModel.on)
|
||||
setAccessibilityLabel(viewModel.on)
|
||||
backgroundColor = viewModel.surface.color
|
||||
setNeedsLayout()
|
||||
layoutIfNeeded()
|
||||
setAccessibilityValue(isOn)
|
||||
setAccessibilityLabel(isOn)
|
||||
backgroundColor = surface.color
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,59 +0,0 @@
|
||||
//
|
||||
// ToggleModel.swift
|
||||
// VDS
|
||||
//
|
||||
// Created by Matt Bruce on 7/22/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
public protocol ToggleModel: Modelable, FormFieldable, DataTrackable, Accessable, Labelable, BinaryColorable {
|
||||
var showText: Bool { get set }
|
||||
var on: Bool { get set }
|
||||
var offText: String { get set }
|
||||
var onText: String { get set }
|
||||
}
|
||||
|
||||
extension ToggleModel {
|
||||
public var userTrueColor: Bool { return on }
|
||||
|
||||
public var label: DefaultLabelModel {
|
||||
var model = DefaultLabelModel()
|
||||
model.textPosition = textPosition
|
||||
model.typograpicalStyle = typograpicalStyle
|
||||
model.text = on ? onText : offText
|
||||
model.surface = surface
|
||||
model.disabled = disabled
|
||||
return model
|
||||
}
|
||||
}
|
||||
|
||||
public struct DefaultToggleModel: ToggleModel {
|
||||
public var id = UUID()
|
||||
public var on: Bool = false
|
||||
public var showText: Bool = false
|
||||
public var offText: String = "Off"
|
||||
public var onText: String = "On"
|
||||
|
||||
public var typograpicalStyle: TypographicalStyle = .BodySmall
|
||||
public var textPosition: TextPosition = .left
|
||||
|
||||
public var inputId: String?
|
||||
public var value: AnyHashable? = true
|
||||
|
||||
public var surface: Surface = .light
|
||||
public var disabled: Bool = false
|
||||
|
||||
public var dataAnalyticsTrack: String?
|
||||
public var dataClickStream: String?
|
||||
public var dataTrack: String?
|
||||
public var accessibilityHintEnabled: String?
|
||||
public var accessibilityHintDisabled: String?
|
||||
public var accessibilityValueEnabled: String?
|
||||
public var accessibilityValueDisabled: String?
|
||||
public var accessibilityLabelEnabled: String?
|
||||
public var accessibilityLabelDisabled: String?
|
||||
|
||||
public init() { }
|
||||
}
|
||||
@ -1,45 +0,0 @@
|
||||
//
|
||||
// ProxPropertyWrapper.swift
|
||||
// VDS
|
||||
//
|
||||
// Created by Matt Bruce on 7/29/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
//https://www.swiftbysundell.com/articles/accessing-a-swift-property-wrappers-enclosing-instance/
|
||||
@propertyWrapper
|
||||
public struct AnyProxy<EnclosingSelf, Value> {
|
||||
private let keyPath: ReferenceWritableKeyPath<EnclosingSelf, Value>
|
||||
|
||||
public init(_ keyPath: ReferenceWritableKeyPath<EnclosingSelf, Value>) {
|
||||
self.keyPath = keyPath
|
||||
}
|
||||
|
||||
@available(*, unavailable, message: "The wrapped value must be accessed from the enclosing instance property.")
|
||||
public var wrappedValue: Value {
|
||||
get { fatalError() }
|
||||
set { fatalError() }
|
||||
}
|
||||
|
||||
public static subscript(
|
||||
_enclosingInstance observed: EnclosingSelf,
|
||||
wrapped wrappedKeyPath: ReferenceWritableKeyPath<EnclosingSelf, Value>,
|
||||
storage storageKeyPath: ReferenceWritableKeyPath<EnclosingSelf, Self>
|
||||
) -> Value {
|
||||
get {
|
||||
let storageValue = observed[keyPath: storageKeyPath]
|
||||
let value = observed[keyPath: storageValue.keyPath]
|
||||
return value
|
||||
}
|
||||
set {
|
||||
let storageValue = observed[keyPath: storageKeyPath]
|
||||
observed[keyPath: storageValue.keyPath] = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension NSObject: ProxyContainer {}
|
||||
public protocol ProxyContainer {
|
||||
typealias Proxy<T> = AnyProxy<Self, T>
|
||||
}
|
||||
@ -1,57 +0,0 @@
|
||||
//
|
||||
// CodableColor.swift
|
||||
// VDS
|
||||
//
|
||||
// Created by Matt Bruce on 8/26/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
@propertyWrapper
|
||||
public struct CodableColor {
|
||||
public var wrappedValue: UIColor
|
||||
|
||||
public init(wrappedValue: UIColor) {
|
||||
self.wrappedValue = wrappedValue
|
||||
}
|
||||
}
|
||||
|
||||
extension CodableColor: Codable {
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.singleValueContainer()
|
||||
let colorString = try container.decode(String.self)
|
||||
wrappedValue = UIColor(hexString: colorString)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.singleValueContainer()
|
||||
try container.encode(wrappedValue.hexString)
|
||||
}
|
||||
}
|
||||
|
||||
@propertyWrapper
|
||||
public struct OptionalCodableColor {
|
||||
public var wrappedValue: UIColor?
|
||||
|
||||
public init(wrappedValue: UIColor?) {
|
||||
self.wrappedValue = wrappedValue
|
||||
}
|
||||
}
|
||||
|
||||
extension OptionalCodableColor: Codable {
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.singleValueContainer()
|
||||
if container.decodeNil() {
|
||||
wrappedValue = nil
|
||||
} else {
|
||||
let colorString = try container.decode(String.self)
|
||||
wrappedValue = UIColor(hexString: colorString)
|
||||
}
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.singleValueContainer()
|
||||
try container.encode(wrappedValue?.hexString)
|
||||
}
|
||||
}
|
||||
@ -1,31 +0,0 @@
|
||||
//
|
||||
// File.swift
|
||||
// VDS
|
||||
//
|
||||
// Created by Matt Bruce on 8/11/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@propertyWrapper
|
||||
public struct Debuggable<Value> {
|
||||
private var value: Value
|
||||
private let description: String
|
||||
|
||||
public init(wrappedValue: Value, description: String = "") {
|
||||
print("Initialized '\(description)' with value \(wrappedValue)")
|
||||
self.value = wrappedValue
|
||||
self.description = description
|
||||
}
|
||||
|
||||
public var wrappedValue: Value {
|
||||
get {
|
||||
print("Accessing '\(description)', returning: \(value)")
|
||||
return value
|
||||
}
|
||||
set {
|
||||
print("Updating '\(description)', newValue: \(newValue)")
|
||||
value = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,23 +0,0 @@
|
||||
//
|
||||
// UseAutoLayout.swift
|
||||
// VDS
|
||||
//
|
||||
// Created by Matt Bruce on 8/11/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
@propertyWrapper
|
||||
public struct UsesAutoLayout<T: UIView> {
|
||||
public var wrappedValue: T {
|
||||
didSet {
|
||||
wrappedValue.translatesAutoresizingMaskIntoConstraints = false
|
||||
}
|
||||
}
|
||||
|
||||
public init(wrappedValue: T) {
|
||||
self.wrappedValue = wrappedValue
|
||||
wrappedValue.translatesAutoresizingMaskIntoConstraints = false
|
||||
}
|
||||
}
|
||||
@ -18,37 +18,31 @@ public protocol Accessable {
|
||||
}
|
||||
|
||||
//Configurations to set within the UIControl
|
||||
extension ModelHandlerable where Self: UIView {
|
||||
private var accessableModel: Accessable? {
|
||||
guard let model = self.model as? Accessable else {
|
||||
return nil
|
||||
}
|
||||
return model
|
||||
}
|
||||
|
||||
extension Handlerable where Self: UIView, Self: Accessable {
|
||||
|
||||
public func setAccessibilityHint(_ override: Bool? = nil) {
|
||||
let check = override ?? !model.disabled
|
||||
if let value = accessableModel?.accessibilityHintEnabled, check {
|
||||
let check = override ?? !disabled
|
||||
if let value = accessibilityHintEnabled, check {
|
||||
accessibilityHint = value
|
||||
} else if let value = accessableModel?.accessibilityHintDisabled, !check {
|
||||
} else if let value = accessibilityHintDisabled, !check {
|
||||
accessibilityHint = value
|
||||
}
|
||||
}
|
||||
|
||||
public func setAccessibilityValue(_ override: Bool? = nil) {
|
||||
let check = override ?? !model.disabled
|
||||
if let value = accessableModel?.accessibilityValueEnabled, check {
|
||||
let check = override ?? !disabled
|
||||
if let value = accessibilityValueEnabled, check {
|
||||
accessibilityValue = value
|
||||
} else if let value = accessableModel?.accessibilityValueDisabled, !check {
|
||||
} else if let value = accessibilityValueDisabled, !check {
|
||||
accessibilityValue = value
|
||||
}
|
||||
}
|
||||
|
||||
public func setAccessibilityLabel(_ override: Bool? = nil) {
|
||||
let check = override ?? !model.disabled
|
||||
if let value = accessableModel?.accessibilityLabelEnabled, check {
|
||||
let check = override ?? !disabled
|
||||
if let value = accessibilityLabelEnabled, check {
|
||||
accessibilityLabel = value
|
||||
} else if let value = accessableModel?.accessibilityLabelDisabled, !check {
|
||||
} else if let value = accessibilityLabelDisabled, !check {
|
||||
accessibilityLabel = value
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,15 +8,15 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
public protocol Colorable<ModelType> {
|
||||
associatedtype ModelType
|
||||
func getColor(_ viewModel: ModelType) -> UIColor
|
||||
public protocol Colorable<ObjectType> {
|
||||
associatedtype ObjectType
|
||||
func getColor(_ object: ObjectType) -> UIColor
|
||||
}
|
||||
|
||||
extension Colorable {
|
||||
fileprivate func getColor(_ viewModel: Any) -> UIColor {
|
||||
guard let model = viewModel as? ModelType else {
|
||||
assertionFailure("Invalid ModelType, Expecting type \(ModelType.self), received \(viewModel) ")
|
||||
fileprivate func getColor(_ object: Any) -> UIColor {
|
||||
guard let model = object as? ObjectType else {
|
||||
assertionFailure("Invalid ObjectType, Expecting type \(ObjectType.self), received \(object) ")
|
||||
return .black
|
||||
}
|
||||
return getColor(model)
|
||||
@ -25,15 +25,15 @@ extension Colorable {
|
||||
public func eraseToAnyColorable() -> AnyColorable { AnyColorable(self) }
|
||||
}
|
||||
|
||||
public struct GenericColorable<ModelType>: Colorable, Withable {
|
||||
private var wrapped: any Colorable<ModelType>
|
||||
public struct GenericColorable<ObjectType>: Colorable, Withable {
|
||||
private var wrapped: any Colorable<ObjectType>
|
||||
|
||||
public init<C: Colorable>(colorable: C) where C.ModelType == ModelType {
|
||||
public init<C: Colorable>(colorable: C) where C.ObjectType == ObjectType {
|
||||
wrapped = colorable
|
||||
}
|
||||
|
||||
public func getColor(_ viewModel: ModelType) -> UIColor {
|
||||
wrapped.getColor(viewModel)
|
||||
public func getColor(_ object: ObjectType) -> UIColor {
|
||||
wrapped.getColor(object)
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ public struct AnyColorable: Colorable, Withable {
|
||||
wrapped = colorable
|
||||
}
|
||||
|
||||
public func getColor(_ viewModel: Any) -> UIColor {
|
||||
wrapped.getColor(viewModel)
|
||||
public func getColor(_ object: Any) -> UIColor {
|
||||
wrapped.getColor(object)
|
||||
}
|
||||
}
|
||||
|
||||
33
VDS/Protocols/Handlerable.swift
Normal file
33
VDS/Protocols/Handlerable.swift
Normal file
@ -0,0 +1,33 @@
|
||||
//
|
||||
// Handlerable.swift
|
||||
// VDS
|
||||
//
|
||||
// Created by Matt Bruce on 7/22/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
import UIKit
|
||||
|
||||
public protocol Handlerable: AnyObject, Initable, Disabling, Surfaceable {
|
||||
var subject: PassthroughSubject<Void, Never> { get set }
|
||||
var subscribers: Set<AnyCancellable> { get set }
|
||||
func updateView()
|
||||
}
|
||||
|
||||
extension Handlerable {
|
||||
|
||||
public func handlerPublisher() -> AnyPublisher<Void, Never> {
|
||||
subject
|
||||
.eraseToAnyPublisher()
|
||||
.debounce(for: .seconds(Constants.StateDebounce), scheduler: RunLoop.main)
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
}
|
||||
|
||||
extension Handlerable where Self: UIView {
|
||||
public func didChange() {
|
||||
subject.send()
|
||||
setNeedsLayout()
|
||||
}
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
//
|
||||
// LabelModelable.swift
|
||||
// VDS
|
||||
//
|
||||
// Created by Matt Bruce on 7/28/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
public protocol Labelable {
|
||||
var typograpicalStyle: TypographicalStyle { get set }
|
||||
var textPosition: TextPosition { get set }
|
||||
}
|
||||
|
||||
extension Labelable {
|
||||
public var font: UIFont? {
|
||||
return typograpicalStyle.font
|
||||
}
|
||||
}
|
||||
@ -1,53 +0,0 @@
|
||||
//
|
||||
// Modelable.swift
|
||||
// VDS
|
||||
//
|
||||
// Created by Matt Bruce on 7/22/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
import UIKit
|
||||
|
||||
public protocol ModelHandlerable: AnyObject, Initable {
|
||||
associatedtype ModelType: Modelable
|
||||
var model: ModelType { get set }
|
||||
var modelPublisher: Published<ModelType>.Publisher { get }
|
||||
var subscribers: Set<AnyCancellable> { get set }
|
||||
init(with model: ModelType)
|
||||
func set(with model: ModelType)
|
||||
func shouldUpdateView(viewModel: ModelType) -> Bool
|
||||
func updateView(viewModel: ModelType)
|
||||
}
|
||||
|
||||
extension ModelHandlerable {
|
||||
public init() {
|
||||
self.init(with: ModelType())
|
||||
}
|
||||
|
||||
public func set(with model: ModelType) {
|
||||
if shouldUpdateView(viewModel: model){
|
||||
updateView(viewModel: model)
|
||||
self.model = model
|
||||
}
|
||||
}
|
||||
|
||||
public func shouldUpdateView(viewModel: ModelType) -> Bool {
|
||||
model != viewModel
|
||||
}
|
||||
|
||||
public func setupUpdateView() {
|
||||
handlerPublisher()
|
||||
.subscribe(on: RunLoop.main)
|
||||
.sink { [weak self] viewModel in
|
||||
self?.updateView(viewModel: viewModel)
|
||||
}
|
||||
.store(in: &subscribers)
|
||||
}
|
||||
|
||||
public func handlerPublisher() -> AnyPublisher<ModelType, Never> {
|
||||
modelPublisher
|
||||
.debounce(for: .seconds(Constants.ModelStateDebounce), scheduler: RunLoop.main)
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
//
|
||||
// Modelable.swift
|
||||
// VDS
|
||||
//
|
||||
// Created by Matt Bruce on 8/5/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol Modelable: Surfaceable, Disabling, Initable, Withable, Identifiable, Equatable, CustomDebugStringConvertible where ID == UUID {
|
||||
|
||||
}
|
||||
|
||||
extension Modelable {
|
||||
public var debugDescription: String {
|
||||
return "\(id.uuidString)"
|
||||
}
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
//
|
||||
// Selectable.swift
|
||||
// VDS
|
||||
//
|
||||
// Created by Matt Bruce on 8/23/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol Selectable {
|
||||
var selected: Bool { get set }
|
||||
}
|
||||
@ -1,37 +0,0 @@
|
||||
//
|
||||
// SelectorGroupModel.swift
|
||||
// VDS
|
||||
//
|
||||
// Created by Matt Bruce on 8/11/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
///MARK: Groups that allow anything selected
|
||||
public protocol SelectorGroupModelable: Modelable, FormFieldable {
|
||||
associatedtype SelectorModelType: Modelable where SelectorModelType: FormFieldable & Selectable
|
||||
var selectors: [SelectorModelType] { get set }
|
||||
}
|
||||
|
||||
extension SelectorGroupModelable {
|
||||
public var debugDescription: String {
|
||||
"group id: \(id.uuidString)\r\(selectors.compactMap{"id: \($0.debugDescription) \($0.selected ? "*" : "")"}.joined(separator: "\r"))"
|
||||
}
|
||||
}
|
||||
|
||||
///MARK: Groups that allow single selections
|
||||
public protocol SelectorGroupSelectedModelable: SelectorGroupModelable {
|
||||
var selectedModel: SelectorModelType? { get }
|
||||
}
|
||||
|
||||
extension SelectorGroupSelectedModelable {
|
||||
public var selectedModel: SelectorModelType? {
|
||||
if let index = selectors.firstIndex(where: { element in
|
||||
return element.selected == true
|
||||
}) {
|
||||
return selectors[index]
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user