diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml new file mode 100644 index 0000000..4d0eb3e --- /dev/null +++ b/.github/workflows/documentation.yml @@ -0,0 +1,30 @@ +name: documentation +on: + pull_request: + branches: + - main + paths: + - 'docs/**' +permissions: + contents: write +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Configure Git Credentials + run: | + git config user.name github-actions[bot] + git config user.email 41898282+github-actions[bot]@users.noreply.github.com + - uses: actions/setup-python@v5 + with: + python-version: 3.x + - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV + - uses: actions/cache@v4 + with: + key: mkdocs-material-${{ env.cache_id }} + path: .cache + restore-keys: | + mkdocs-material- + - run: pip install mkdocs-material + - run: mkdocs gh-deploy --force diff --git a/docs/assets/img/logo.png b/docs/assets/img/logo.png new file mode 100644 index 0000000..d766e7c Binary files /dev/null and b/docs/assets/img/logo.png differ diff --git a/docs/assets/video/compose-android.gif b/docs/assets/video/compose-android.gif new file mode 100644 index 0000000..26c60ec Binary files /dev/null and b/docs/assets/video/compose-android.gif differ diff --git a/docs/assets/video/flutter.gif b/docs/assets/video/flutter.gif new file mode 100644 index 0000000..4b4ad24 Binary files /dev/null and b/docs/assets/video/flutter.gif differ diff --git a/docs/assets/video/swift-ui.gif b/docs/assets/video/swift-ui.gif new file mode 100644 index 0000000..a943242 Binary files /dev/null and b/docs/assets/video/swift-ui.gif differ diff --git a/docs/assets/video/viewsystem-android.gif b/docs/assets/video/viewsystem-android.gif new file mode 100644 index 0000000..26c60ec Binary files /dev/null and b/docs/assets/video/viewsystem-android.gif differ diff --git a/docs/contributors.md b/docs/contributors.md new file mode 100644 index 0000000..9ba1e1a --- /dev/null +++ b/docs/contributors.md @@ -0,0 +1,9 @@ +# Contributors + +> We would like to thank everyone who made this possible and helped > the CodandoTV community grow stronger. This project exists thanks > to all the people who contribute + +_Signed by Rods_ + +This project exists thanks to all the people who contribute. + + \ No newline at end of file diff --git a/docs/how-to-use/compose.md b/docs/how-to-use/compose.md new file mode 100644 index 0000000..ecaec7d --- /dev/null +++ b/docs/how-to-use/compose.md @@ -0,0 +1,99 @@ +# Jetpack Compose + +- Create your ComponentPropertyClass with properties that you need. In this example, I used checkbox component: + +!!! warning "Immutable and Stable annotations" + Here we have some points to consider To avoid unnecessary recompositions at your component. We recommend use the `@Immutable` and `@Stable` annotations in your properties. + +```kotlin +@JsonIgnoreProperties(ignoreUnknown = true) +@Immutable +@Stable +data class CheckBoxProperties( + @JsonProperty("text") val text: String? = null, + ... define your properties here +) +``` + +- Add your Component json object in `Dymanic.json`: + +```json +{ + "key": "CraftDCheckBox", + "value": { + ... define your properties here + } + } +``` + +- Create your Component + +!!! tip "Your component must have three properties" + + - componentProperties: The mapped properties from json + - modifier: Default for composable componets + - behaviour: This make reference to the component's behaviour, for example: onclick -> for buttons, onchange -> for checkbox etc... + +```kotlin +class CraftDCheckBoxBuilder( + override val key: String = CraftDComponentKey.CHECK_BOX_COMPONENT.key +) : + CraftDBuilder { + @Composable + override fun craft(model: SimpleProperties, listener: CraftDViewListener) { + val checkBoxProperties = model.value.convertToVO() + CraftDCheckBox(checkBoxProperties) { + checkBoxProperties.actionProperties?.let { listener.invoke(it) } + } + } +} +``` + +- Create your Component Builder: + +!!! tip "Note" + + This Builder must extend CraftBuilder Class and override craft method. + +```kotlin +class CraftDCheckBoxBuilder( + override val key: String = CraftDComponentKey.CHECK_BOX_COMPONENT.key +) : + CraftDBuilder { + @Composable + override fun craft(model: SimpleProperties, listener: CraftDViewListener) { + val checkBoxProperties = model.value.convertToVO() + CraftDCheckBox(checkBoxProperties) { + checkBoxProperties.actionProperties?.let { listener.invoke(it) } + } + } +} +``` + +- In your screen you can add the builder inside of `CraftBuilderManager` + +```kotlin +@Composable +fun InitialScreen( + vm: SampleCraftDComposeViewModel +) { + val properties by vm.properties.collectAsStateWithLifecycle() + val dynamicBuilder = remember { + CraftDBuilderManager().add( + CraftDCheckBoxBuilder() + ) + } + LaunchedEffect(Unit) { + vm.loadProperties() + } + + CraftDynamic( + properties = properties, + dynamicBuilder = dynamicBuilder + ) { + //Component click return to do something + } +} +``` + +So now enjoy your component! diff --git a/docs/how-to-use/futter.md b/docs/how-to-use/futter.md new file mode 100644 index 0000000..0feae91 --- /dev/null +++ b/docs/how-to-use/futter.md @@ -0,0 +1,85 @@ +# Flutter + +- Create your ComponentPropertyClass with properties that you need + +```dart +class ButtonProperties { + const ButtonProperties({ + required this.text, + ... place your construtor properties + }); + + final String text; + ... place your properties +} +``` + +- Add your Component json object in `Dymanic.json` + +```json +{ + "key": "CraftDBbutton", + "value": { + "text": "Knife", + ... place your properties + } + } +``` + +- Create your Component + +!!! tip "Your component must have three properties" + - ButtonProperties: The mapped properties from json + - callback: This make reference to the component's behaviour, for example: onclick -> for buttons, onchange -> for checkbox etc... + + ```dart + class CraftDButton extends StatelessWidget { + const CraftDButton( + {super.key, required this.buttonProperties, required this.callback} + ); + + //... place your code + } + ``` + +- Create your Component Builder + +!!! tip "This Builder must extend `CraftBuilder` Class and override craft and fromJson methods." + ```dart + class CraftDButtonBuilder extends CraftDBuilder { + CraftDButtonBuilder() : super(key: key); + + @override + Widget craft(ButtonProperties model, CraftDViewListener listener) { + //... place your code + } + + @override + ButtonProperties fromJson(properties) { + return ButtonProperties( + text: properties["text"], + //... rest of tour code + ) + } + + static String key = "CraftDButton"; + } + ``` + +- In your Page, create your `CraftDBuilder` declaration and put it into `CraftDynamic` Widget + +```dart + // You can put it in your dependency injection + final craftdBuilderManager = CraftDBuilderManager(); + + return CraftDynamic( + simplePropertiesList: simplePropertiesList, + craftDBuilderManager : craftdBuilderManager, + onAction: (actionProperties) { + print( + "categoria ${actionProperties.analyticsProperties?.category} " + "label ${actionProperties.analyticsProperties?.label} - " + "track ${actionProperties.analyticsProperties?.track}"); + }); + } +``` \ No newline at end of file diff --git a/docs/how-to-use/swift-ui.md b/docs/how-to-use/swift-ui.md new file mode 100644 index 0000000..53232bb --- /dev/null +++ b/docs/how-to-use/swift-ui.md @@ -0,0 +1,69 @@ +# Swift UI + +- Create your ComponentPropertyClass with properties that you need + +```swift +public struct TextProperties: Decodable { + public let text: String? + public let textColorHex: String? + public let textSize: String? + public let backgroundHex: String? + public let textAllCaps: Bool? + public let textHtml: String? +} +``` + +- Add your Component json object in `Dymanic.json`` + +```json +[ + { + "key": "MyCraftDText", + "value": { + "text": "Knife", + "backgroundHex": "#9A71F6", + "textSize": "30", + "textColorHex": "#000000" + } + } +] +``` + +- Create your Component + +!!! tip "This Builder must extend `CraftBuilder` Class and override craft method." + ```swift + public class MyCraftDTextBuilder: CraftDBuilder { + public let key = "MyCraftDText" + + let decoder = JSONDecoder() + + public func craft( + model: SimpleProperties, + listener: CraftDViewListener + ) -> any View { + do { + let properties = try model.decodeValue(TextProperties.self, using: decoder) + return Text(properties.text ?? "") + } catch { + return EmptyView() + } + } + } + ``` + +- In your add your builder inside of `CraftBuilderManager` + +```swift +@main +struct CraftDSampleApp: App { + var body: some Scene { + WindowGroup { + //Or another View + let craftDBuilderManager = CraftDBuilderManager() + .add(builder: <#T##any CraftDBuilder#> like MyCraftDTextBuilder) + CraftDynamic(craftDBuilderManager: CraftDBuilderManager) + } + } +} +``` \ No newline at end of file diff --git a/docs/how-to-use/view-system.md b/docs/how-to-use/view-system.md new file mode 100644 index 0000000..3101455 --- /dev/null +++ b/docs/how-to-use/view-system.md @@ -0,0 +1,146 @@ +# View System + +- Add in a xml that you want to use + +```xml + + + + + + +``` + +- Add in your ViewModel the interface from the Dynamic + +```kotlin +class YourViewModel( + val craft: CraftDView, + val repository: DynamicRepository +) : ViewModel() { + //Stuffs +} +``` + +- In your `Activity`/`Fragment` connect the Dynamic to the adapter in xml + +```kotlin + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityDynamicComponentBinding.inflate(layoutInflater) + setContentView(binding.root) + + binding.recycler.adapter = vm.craft as CraftDViewAdapter + //--- Stuffs +} +``` + +- Create object Properties of according with your components for example: + +```kotlin +@JsonIgnoreProperties(ignoreUnknown = true) +data class TextProperties( + @JsonProperty("xxx") val myProperties1: String, + @JsonProperty("xxx") val myProperties2: String, +) +``` + +!!! tip "View Renderds" + + Create object ViewRenders of according with your components for example: + + ```kotlin + class MyComponentRender(override var onClickListener: CraftDViewListener?) + : ViewRenderer("Your Key", "Your Identifier") { + + inner class MyHolder(val anyView: AnyView) : RecyclerView.ViewHolder(anyView) + + override fun bindView(model: SimpleProperties, holder: MyHolder, position: Int) { + val anyProperties = model.value.convertToVO() + + holder.any.text = anyProperties.text + anyProperties.textColorHex?.let { textColorHex -> + //Stuff + } + anyProperties.actionProperties?.let { actionProperties -> + //Stuff + } + } + + override fun createViewHolder(parent: ViewGroup): MyHolder { + return MyHolder(AnyView(parent.context)) + } + } + ``` + +- Configure your ViewModel to accept for example + +```kotlin +class DynamicViewModel( + val craft: CraftD, + val repository: SampleCraftDRepository +) : ViewModel() { + + fun onResume() { + viewModelScope.launch { + repository.getDynamic() + .catch { + it.printStackTrace() + } + .collect { + setupDynamicRender(it) + craft.setViewObjectDiff(it) + } + } + } + + private fun setupDynamicRender(list: List) { + craft.registerRenderers( + CraftDBuilderManager.getBuilderRenders( + simpleProperties = list, + customDynamicBuilderList = customListViewRender // Can you pass your custom list from ViewRender + ) { action -> + listener.invoke(action) + }) + } + + private val listener = object : CraftDViewListener { + override fun invoke(actionProperties: ActionProperties) { + actionProperties.analytics?.let { + //Stuff + } + actionProperties.deeplink?.let { + //Stuff + } + } + } +} +``` + +- Enjoy and Have fun to create a json that you need + +!!! tip "Your component must have three properties" + Your json must to have at least two parameters key and value that are respective of your object for example: + + ```json + { + "data": [ + { + "key": "MyDynamicView", + "value": { + "text": "Any", + "textColor": "Any" + } + } + ] + } + ``` + +So now enjoy your component!!! \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index a85b738..fff5d75 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,5 +1,90 @@ -# CraftD +# Welcome to CraftD -- [Why](why.md) +## Features + +### 🔗 Compatibility + +| Tech | Support | +|----------------------------|------------------| +| View System - Android | ✅ Supported | +| Jetpack Compose - Android | ✅ Supported | +| Widgets Flutter | ✅ Supported | +| SwiftUI - iOS | ✅ Supported | +| Compose Multiplatform | ⚒️ In Progress | + +### Components that already exist in the library + +| Component | Android Compose | Android View | Flutter | SwiftUI | +|-------------------|-----------------|--------------|---------|---------| +| Button | X | X | X | - | +| Text | X | X | X | X | +| Checkbox | X | - | - | - | + +### How to create a custom component? + +- Create a data structure to represent your component: + +```kotlin +@JsonIgnoreProperties(ignoreUnknown = true) +@Immutable +@Stable +data class CheckBoxProperties( + @JsonProperty("text") val text: String? = null, + ... rest of your properties +) +``` + +- Add your Component json object in _Dymanic.json_ + +```json +{ + "key": "CraftDCheckBox", + "value": { + ... rest of your properties + } +} +``` + +- Create your component: + +```kotlin +@Composable +fun CraftDCheckBox( + checkboxProperties: CheckBoxProperties, + modifier: Modifier = Modifier, + onChecked: (Boolean) -> Unit +) { + ... Rest of your code +} +``` + +### Screen recordings + +
+
+ ![Compose Android](./assets/video/compose-android.gif){ width="300" } +
Compose Android
+
+ +
+ ![View System Android](./assets/video/viewsystem-android.gif){ width="300" } +
View System Android
+
+ +
+ ![Flutter](./assets/video/flutter.gif){ width="300" } +
Flutter
+
+ +
+ ![Swift UI](./assets/video/swift-ui.gif){ width="300" } +
Swift UI
+
+
+ + +!!! info "Credits" + + A Server Driven UI library for Android. + Inspired by the [_DynamicView_](https://github.com/rviannaoliveira/DynamicView). -- [Setup](setup.md) \ No newline at end of file diff --git a/docs/notes/developers.md b/docs/notes/developers.md new file mode 100644 index 0000000..f0b6085 --- /dev/null +++ b/docs/notes/developers.md @@ -0,0 +1,12 @@ +# For Developers + +!!! info "Note" + This library was created by the [CodandoTV YouTube channel](https://www.youtube.com/codandotv) community. + +If you want to participate in the project just get in touch or fork the project on github. + +- [:fontawesome-brands-github: CraftD repository](https://github.com/CodandoTV/CraftD/) + +And if you are not yet subscribed to the channel, give us a hand and subscribe. + +- [:fontawesome-brands-youtube: CodandoTV](https://www.youtube.com/codandotv) \ No newline at end of file diff --git a/docs/notes/license.md b/docs/notes/license.md new file mode 100644 index 0000000..254cb5f --- /dev/null +++ b/docs/notes/license.md @@ -0,0 +1,11 @@ +# License + +MIT License + +Copyright (c) 2024 CodandoTV and Rodrigo Vianna Calixto de Oliveira + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/docs/setup.md b/docs/setup.md index 1a29b9f..4440c4a 100644 --- a/docs/setup.md +++ b/docs/setup.md @@ -2,20 +2,58 @@ ## Android -First of all, you should add the library in your `build.gradle.kts`. The latest android version (add latest version badge), so you can do: - -``` -implementation(....) +- Add in your settings.gradle mavenCetral: + +```kotlin +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + //Stuff + mavenCentral() + } +} ``` +- There are three versions for Android + +!!! example "Jetpack Compose" + + ```kotlin + implementation("io.github.codandotv:craftd-compose:${last_version}") + ``` + +!!! example "XML View System" + + ```kotlin + implementation("io.github.codandotv:craftd-xml:${last_version}") + ``` + +!!! example "Core" + Core - Is meant to be used for you to customize even the craftD mechanism + + ```kotlin + implementation("io.github.codandotv:craftd-core:${last_version}") + ``` + +## iOS + +- Add your pod ‘CraftDSwiftUI’ + +!!! warning "In Progress" + This section is in progress... + ## Flutter -First of all, you should add the library in your `pubspec.yaml`. The latest flutter version (add latest version badge), so you can do: +- Run this command with Flutter: -``` -implementation(....) +```shell +flutter pub add craftd_widget ``` -## iOS +This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get): -TODO \ No newline at end of file +!!! example "pubspec.yml" + ```yaml + dependencies: + craftd_widget: $last_version + ``` \ No newline at end of file diff --git a/docs/why.md b/docs/why.md deleted file mode 100644 index 95ba5a9..0000000 --- a/docs/why.md +++ /dev/null @@ -1,9 +0,0 @@ -# Why - -CraftD provides an easy, and efficient way to implement Server Driven UI paradigm. - -The goals of CraftD are: - -- Provides an abstraction layer to build you widgets that comes from a server; - -- TODO \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..ab4c498 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,55 @@ +site_name: CraftD +theme: + name: material + logo: assets/img/logo.png + font: + text: Merriweather Sans + code: Red Hat Mono + features: + - navigation.footer + - content.code.annotations # (1)! + - content.code.copy + palette: + # Dark Mode + - scheme: slate + toggle: + icon: material/weather-sunny + name: Dark mode + primary: deep purple + accent: light blue + + # Light Mode + - scheme: default + toggle: + icon: material/weather-night + name: Light mode + primary: deep purple + accent: blue + +markdown_extensions: + - smarty + - codehilite: + guess_lang: false + - footnotes + - meta + - toc: + permalink: true + - pymdownx.betterem: + smart_enable: all + - pymdownx.caret + - pymdownx.inlinehilite + - pymdownx.magiclink + - pymdownx.smartsymbols + - pymdownx.superfences + - pymdownx.emoji + - pymdownx.details + - pymdownx.tabbed: + alternate_style: true + - tables + - admonition + - attr_list + - md_in_html + - pymdownx.blocks.caption + - pymdownx.emoji: + emoji_index: !!python/name:material.extensions.emoji.twemoji + emoji_generator: !!python/name:material.extensions.emoji.to_svg