Compare commits

...

13 Commits

18 changed files with 458 additions and 116 deletions

17
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,17 @@
on: [push]
name: Build
jobs:
build-ubuntu:
name: Commit release
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Setup JDK
uses: actions/setup-java@v1
with:
java-version: 11
- name: Build
run: ./gradlew build packageUberJarForCurrentOS

View File

@@ -1,4 +1,7 @@
on: [push]
on:
push:
branches:
- master
name: Commit release
@@ -16,7 +19,13 @@ jobs:
- name: Set version from gradle.properties
run: echo "version=` cat gradle.properties | grep ^version= | grep -o [\\.0-9]* `" >> $GITHUB_ENV
- name: Build
run: ./gradlew packageUberJarForCurrentOS
run: ./gradlew build packageUberJarForCurrentOS
- name: Publish Web
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./web/build/distributions
publish_branch: site
- name: Create Release
id: create_release
uses: actions/create-release@v1

View File

@@ -2,6 +2,9 @@ package dev.inmo.kmppscriptbuilder.core.models
import kotlinx.serialization.Serializable
const val defaultProjectName = "\${project.name}"
const val defaultProjectDescription = "\${project.name}"
@Serializable
data class MavenConfig(
val name: String,
@@ -22,14 +25,20 @@ data class MavenPublishingRepository(
name.toUpperCase()
}
fun build(indent: String) = """maven {
name = "$name"
url = uri("$url")
credentials {
username = project.hasProperty('${nameCapitalized}_USER') ? project.property('${nameCapitalized}_USER') : System.getenv('${nameCapitalized}_USER')
password = project.hasProperty('${nameCapitalized}_PASSWORD') ? project.property('${nameCapitalized}_PASSWORD') : System.getenv('${nameCapitalized}_PASSWORD')
fun build(indent: String): String {
val usernameProperty = "${nameCapitalized}_USER"
val passwordProperty = "${nameCapitalized}_PASSWORD"
return """if ((project.hasProperty('${usernameProperty}') || System.getenv('${usernameProperty}') != null) && (project.hasProperty('${passwordProperty}') || System.getenv('${passwordProperty}') != null)) {
maven {
name = "$name"
url = uri("$url")
credentials {
username = project.hasProperty('${usernameProperty}') ? project.property('${usernameProperty}') : System.getenv('${usernameProperty}')
password = project.hasProperty('${passwordProperty}') ? project.property('${passwordProperty}') : System.getenv('${passwordProperty}')
}
}
}""".replace("\n", "\n$indent")
}
}
val SonatypeRepository = MavenPublishingRepository("sonatype", "https://oss.sonatype.org/service/local/staging/deploy/maven2/")

View File

@@ -0,0 +1,18 @@
package dev.inmo.kmppscriptbuilder.desktop.utils
import java.awt.Desktop
import java.lang.Exception
import java.net.URI
fun openLink(link: String): Boolean {
val desktop = if (Desktop.isDesktopSupported()) Desktop.getDesktop() else null
if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE)) {
try {
desktop.browse(URI(link))
return true
} catch (e: Exception) {
e.printStackTrace()
}
}
return false
}

View File

@@ -6,10 +6,18 @@ import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.platform.DesktopPlatform
import androidx.compose.ui.res.loadSvgResource
import androidx.compose.ui.res.svgResource
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.dp
import dev.inmo.kmppscriptbuilder.core.models.Config
import dev.inmo.kmppscriptbuilder.desktop.utils.*
import dev.inmo.micro_utils.coroutines.safelyWithoutExceptions
import java.awt.Desktop
import java.lang.Exception
import java.net.URL
class BuilderView : View() {
private val projectTypeView = ProjectTypeView()

View File

@@ -2,8 +2,7 @@ package dev.inmo.kmppscriptbuilder.desktop.views
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.runtime.*
import dev.inmo.kmppscriptbuilder.core.models.MavenConfig
import dev.inmo.kmppscriptbuilder.core.models.SonatypeRepository
import dev.inmo.kmppscriptbuilder.core.models.*
import dev.inmo.kmppscriptbuilder.desktop.utils.*
class MavenInfoView : VerticalView("Project information") {
@@ -18,8 +17,8 @@ class MavenInfoView : VerticalView("Project information") {
var mavenConfig: MavenConfig
get() = MavenConfig(
projectNameProperty,
projectDescriptionProperty,
projectNameProperty.ifBlank { defaultProjectName },
projectDescriptionProperty.ifBlank { defaultProjectDescription },
projectUrlProperty,
projectVcsUrlProperty,
includeGpgSignProperty,

View File

@@ -6,13 +6,13 @@ kotlin.incremental.js=true
android.useAndroidX=true
android.enableJetifier=true
kotlin_version=1.4.30
kotlin_coroutines_version=1.4.2
kotlin_version=1.4.31
kotlin_coroutines_version=1.4.3
kotlin_serialisation_core_version=1.1.0
ktor_version=1.5.1
micro_utils_version=0.4.27
ktor_version=1.5.2
micro_utils_version=0.4.36
compose_version=0.3.0
compose_version=0.3.2
# ANDROID

View File

@@ -1,15 +1,82 @@
package dev.inmo.kmppscriptbuilder.web
import dev.inmo.kmppscriptbuilder.web.views.MavenProjectInfoView
import dev.inmo.kmppscriptbuilder.web.views.ProjectTypeView
import dev.inmo.kmppscriptbuilder.core.models.Config
import dev.inmo.kmppscriptbuilder.core.utils.serialFormat
import dev.inmo.kmppscriptbuilder.web.views.*
import kotlinx.browser.document
import kotlinx.dom.appendElement
import org.w3c.dom.*
import org.w3c.dom.url.URL
import org.w3c.files.*
fun saveFile(content: String, filename: String) {
val a = document.body!!.appendElement("a") {
setAttribute("style", "visibility:hidden; display: none")
} as HTMLAnchorElement
val blob = Blob(arrayOf(content), BlobPropertyBag(
"application/*;charset=utf-8"
))
val url = URL.createObjectURL(blob)
a.href = url
a.download = filename
a.click()
URL.revokeObjectURL(url)
a.remove()
}
fun main() {
document.addEventListener(
"DOMContentLoaded",
{
val projectTypeView = ProjectTypeView()
val mavenInfoTypeView = MavenProjectInfoView()
val builderView = BuilderView()
(document.getElementById("openConfig") as HTMLElement).onclick = {
val targetInput = document.body!!.appendElement("input") {
setAttribute("style", "visibility:hidden; display: none")
} as HTMLInputElement
targetInput.type = "file"
targetInput.onchange = {
targetInput.files ?.also { files ->
for (i in (0 until files.length) ) {
files[i] ?.also { file ->
val reader = FileReader()
reader.onload = {
val content = it.target.asDynamic().result as String
builderView.config = serialFormat.decodeFromString(Config.serializer(), content)
false
}
reader.readAsText(file)
}
}
}
}
targetInput.click()
targetInput.remove()
false
}
(document.getElementById("saveConfig") as HTMLElement).onclick = {
val filename = "publish.kpsb"
val content = serialFormat.encodeToString(Config.serializer(), builderView.config)
saveFile(content, filename)
false
}
(document.getElementById("exportScript") as HTMLElement).onclick = {
val filename = "publish.gradle"
val content = builderView.config.run {
type.buildMavenGradleConfig(
mavenConfig,
licenses
)
}
saveFile(content, filename)
false
}
}
)
}

View File

@@ -0,0 +1,10 @@
package dev.inmo.kmppscriptbuilder.web.utils
import kotlinx.browser.document
inline fun <R> keepScrolling(crossinline block: () -> R): R = document.body ?.let {
val (x, y) = (it.scrollLeft to it.scrollTop)
return block().also { _ ->
it.scrollTo(x, y)
}
} ?: block()

View File

@@ -0,0 +1,23 @@
package dev.inmo.kmppscriptbuilder.web.views
import dev.inmo.kmppscriptbuilder.core.models.Config
import kotlinx.browser.document
import org.w3c.dom.HTMLElement
class BuilderView : View {
private val projectTypeView = ProjectTypeView()
private val licensesView = LicensesView(document.getElementById("licensesListDiv") as HTMLElement)
private val mavenInfoTypeView = MavenProjectInfoView()
var config: Config
get() = Config(
licensesView.licenses,
mavenInfoTypeView.mavenConfig,
projectTypeView.projectType
)
set(value) {
licensesView.licenses = value.licenses
mavenInfoTypeView.mavenConfig = value.mavenConfig
projectTypeView.projectType = value.type
}
}

View File

@@ -3,16 +3,16 @@ package dev.inmo.kmppscriptbuilder.web.views
import dev.inmo.kmppscriptbuilder.core.models.Developer
import org.w3c.dom.*
class DevelopersView(rootElement: HTMLElement) : ListView<Developer>(rootElement, "Add developer", "Remove developer") {
class DevelopersView(rootElement: HTMLElement) : MutableListView<Developer>(rootElement, "Add developer", "Remove developer") {
private val HTMLElement.usernameElement: HTMLInputElement
get() = children[0] as HTMLInputElement
get() = getElementsByTagName("input")[0] as HTMLInputElement
private val HTMLElement.nameElement: HTMLInputElement
get() = children[1] as HTMLInputElement
get() = getElementsByTagName("input")[1] as HTMLInputElement
private val HTMLElement.emailElement: HTMLInputElement
get() = children[2] as HTMLInputElement
get() = getElementsByTagName("input")[2] as HTMLInputElement
var developers: List<Developer>
get() = elements.values.map {
get() = elements.map {
Developer(it.usernameElement.value, it.nameElement.value, it.emailElement.value)
}
set(value) {
@@ -21,7 +21,7 @@ class DevelopersView(rootElement: HTMLElement) : ListView<Developer>(rootElement
override fun createPlainObject(): Developer = Developer("", "", "")
override fun HTMLElement.placeElement(value: Developer) {
override fun HTMLElement.addContentBeforeRemoveButton(value: Developer) {
createTextField("Developer ID", "Developer username").value = value.id
createTextField("Developer name", "").value = value.name
createTextField("Developer E-Mail", "").value = value.eMail

View File

@@ -0,0 +1,113 @@
package dev.inmo.kmppscriptbuilder.web.views
import dev.inmo.kmppscriptbuilder.core.models.License
import dev.inmo.kmppscriptbuilder.core.models.getLicenses
import dev.inmo.micro_utils.coroutines.safeActor
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import io.ktor.client.HttpClient
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.SendChannel
import kotlinx.coroutines.flow.consumeAsFlow
import kotlinx.coroutines.flow.debounce
import kotlinx.dom.appendElement
import org.w3c.dom.*
class LicensesView(
rootElement: HTMLElement,
client: HttpClient = HttpClient(),
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) : MutableListView<License>(rootElement, "Add empty license", "Remove license") {
private val HTMLElement.idElement: HTMLInputElement
get() = getElementsByTagName("input")[0] as HTMLInputElement
private val HTMLElement.titleElement: HTMLInputElement
get() = getElementsByTagName("input")[1] as HTMLInputElement
private val HTMLElement.urlElement: HTMLInputElement
get() = getElementsByTagName("input")[2] as HTMLInputElement
private class LicenseOfferList(
rootElement: HTMLElement,
private val licensesView: LicensesView,
client: HttpClient,
scope: CoroutineScope
) : ListView<License>(rootElement, useSimpleDiffStrategy = true) {
private var licensesTemplates: List<License> = emptyList()
init {
scope.launch {
licensesTemplates = client.getLicenses().values.toList()
changeActor.send(Unit) // update list of searches
}
}
private val changeActor: SendChannel<Unit> = scope.run {
val onChangeActor = Channel<Unit>(Channel.CONFLATED)
onChangeActor.consumeAsFlow().subscribeSafelyWithoutExceptions(scope) {
val lowercased = searchString
data = if (lowercased.isEmpty()) {
emptyList()
} else {
licensesTemplates.filter {
val lowercasedTitle = it.title.toLowerCase()
lowercased.all { it in lowercasedTitle }
}
}
}
onChangeActor
}
private val searchElement = rootElement.createTextField("Quick add", "Type some license name part to find it").apply {
oninput = {
changeActor.offer(Unit)
false
}
}
private var searchString: String
get() = searchElement.value.toLowerCase()
set(value) {
searchElement.value = value
}
override fun HTMLElement.placeElement(value: License) {
createCommonButton(value.title).onclick = {
searchString = ""
licensesView.licenses += value
changeActor.offer(Unit)
false
}
}
override fun HTMLElement.updateElement(from: License, to: License) {
getElementsByTagName("button")[0] ?.remove()
placeElement(to)
}
}
private val licensesOffersList = LicenseOfferList(
rootElement.appendElement("div") { classList.add("uk-padding-small") } as HTMLElement,
this,
client,
scope
)
var licenses: List<License>
get() = elements.map {
License(it.idElement.value, it.titleElement.value, it.urlElement.value)
}
set(value) {
data = value
}
override fun createPlainObject(): License = License("", "", "")
override fun HTMLElement.addContentBeforeRemoveButton(value: License) {
createTextField("License Id", "Short name like \"Apache-2.0\"").value = value.id
createTextField("License Title", "Official title of license (like \"Apache Software License 2.0\")").value = value.title
createTextField("License URL", "Link to your LICENSE file OR official license file (like \"https://opensource.org/licenses/Apache-2.0\")").value = value.url ?: ""
}
override fun HTMLElement.updateElement(from: License, to: License) {
idElement.value = to.id
titleElement.value = to.title
urlElement.value = to.url ?: ""
}
}

View File

@@ -1,61 +1,57 @@
package dev.inmo.kmppscriptbuilder.web.views
import dev.inmo.micro_utils.common.calculateDiff
import kotlinx.browser.document
import dev.inmo.micro_utils.common.calculateStrictDiff
import kotlinx.dom.appendElement
import org.w3c.dom.HTMLElement
abstract class ListView<T>(
private val rootElement: HTMLElement,
addButtonText: String = "Add",
private val removeButtonText: String = "Remove"
protected val rootElement: HTMLElement,
useSimpleDiffStrategy: Boolean = false
) : View {
protected val elements = mutableMapOf<T, HTMLElement>()
protected val elements = mutableListOf<HTMLElement>()
private val diffHandling: (old: List<T>, new: List<T>) -> Unit = if (useSimpleDiffStrategy) {
{ _, new ->
elements.forEach { it.remove() }
elements.clear()
new.forEach {
val element = instantiateElement()
elements.add(element)
element.placeElement(it)
}
}
} else {
{ old, new ->
val diff = old.calculateStrictDiff(new)
diff.removed.forEach {
elements[it.index].remove()
elements.removeAt(it.index)
println(it.value)
}
diff.added.forEach {
val element = instantiateElement()
elements.add(element)
element.placeElement(it.value)
}
diff.replaced.forEach { (old, new) ->
val element = elements.getOrNull(old.index) ?.also { it.updateElement(old.value, new.value) }
if (element == null) {
val newElement = instantiateElement()
newElement.placeElement(new.value)
elements[new.index] = newElement
}
}
}
}
protected var data: List<T> = emptyList()
set(value) {
val old = field
field = value
val diff = old.calculateDiff(value)
diff.removed.forEach {
rootElement.removeChild(elements[it.value] ?: return@forEach)
}
diff.added.forEach {
val element = instantiateElement()
element.placeElement(it.value)
elements[it.value] = element
element.addRemoveButton(it.value)
}
diff.replaced.forEach { (old, new) ->
val element = elements[old.value] ?.also { it.updateElement(old.value, new.value) }
if (element == null) {
val newElement = instantiateElement()
newElement.placeElement(new.value)
elements[new.value] = newElement
}
}
diffHandling(old, value)
}
init {
rootElement.createButton(addButtonText).apply {
onclick = {
data += createPlainObject()
Unit
}
}
}
protected abstract fun createPlainObject(): T
protected abstract fun HTMLElement.placeElement(value: T)
protected abstract fun HTMLElement.updateElement(from: T, to: T)
private fun HTMLElement.addRemoveButton(value: T) {
createButton(removeButtonText).onclick = {
data = data.filter {
it != value
}
Unit
}
}
private fun instantiateElement() = rootElement.appendElement("div") {
classList.add("uk-padding-small")
} as HTMLElement

View File

@@ -1,7 +1,6 @@
package dev.inmo.kmppscriptbuilder.web.views
import dev.inmo.kmppscriptbuilder.core.models.MavenConfig
import dev.inmo.kmppscriptbuilder.core.models.SonatypeRepository
import dev.inmo.kmppscriptbuilder.core.models.*
import kotlinx.browser.document
import org.w3c.dom.HTMLElement
import org.w3c.dom.HTMLInputElement
@@ -14,17 +13,17 @@ class MavenProjectInfoView : View {
private val includeGpgElement = document.getElementById("includeGpgSignToggle") as HTMLInputElement
private val includeMavenCentralElement = document.getElementById("includeMavenCentralTargetRepoToggle") as HTMLInputElement
private val developersView = DevelopersView(document.getElementById("developersListDiv") as HTMLElement)
private val repositoriesView = RepositoriesView(document.getElementById("repositoriesListDiv") as HTMLElement)
var mavenConfig: MavenConfig
get() = MavenConfig(
nameElement.value,
descriptionElement.value,
nameElement.value.ifBlank { defaultProjectName },
descriptionElement.value.ifBlank { defaultProjectDescription },
urlElement.value,
vcsUrlElement.value,
includeGpgElement.checked,
developersView.developers,// TODO:: Add developers
// TODO:: Add repositories
if (includeMavenCentralElement.checked) {
developersView.developers,
repositoriesView.repositories + if (includeMavenCentralElement.checked) {
listOf(SonatypeRepository)
} else {
emptyList()
@@ -37,8 +36,8 @@ class MavenProjectInfoView : View {
vcsUrlElement.value = value.vcsUrl
includeGpgElement.checked = value.includeGpgSigning
developersView.developers = value.developers
// TODO:: Add repositories
val reposWithoutSonatype = value.repositories.filter { it != SonatypeRepository }
includeMavenCentralElement.checked = value.repositories.size != reposWithoutSonatype.size
repositoriesView.repositories = reposWithoutSonatype
}
}

View File

@@ -0,0 +1,41 @@
package dev.inmo.kmppscriptbuilder.web.views
import dev.inmo.kmppscriptbuilder.web.utils.keepScrolling
import org.w3c.dom.HTMLElement
abstract class MutableListView<T>(
rootElement: HTMLElement,
addButtonText: String = "Add",
private val removeButtonText: String = "Remove"
) : ListView<T>(rootElement) {
init {
rootElement.createPrimaryButton(addButtonText).apply {
onclick = {
keepScrolling {
val newObject = createPlainObject()
data += newObject
}
false
}
}
}
protected abstract fun createPlainObject(): T
protected open fun HTMLElement.addContentBeforeRemoveButton(value: T) {}
protected open fun HTMLElement.addContentAfterRemoveButton(value: T) {}
final override fun HTMLElement.placeElement(value: T) {
addContentBeforeRemoveButton(value)
addRemoveButton()
addContentAfterRemoveButton(value)
}
private fun HTMLElement.addRemoveButton() {
val button = createPrimaryButton(removeButtonText)
button.onclick = {
elements.indexOf(button.parentElement).takeIf { it > -1 } ?.also {
data -= data[it]
} ?: rootElement.removeChild(this@addRemoveButton)
false
}
}
}

View File

@@ -0,0 +1,31 @@
package dev.inmo.kmppscriptbuilder.web.views
import dev.inmo.kmppscriptbuilder.core.models.MavenPublishingRepository
import org.w3c.dom.*
class RepositoriesView(rootElement: HTMLElement) : MutableListView<MavenPublishingRepository>(rootElement, "Add repository", "Remove repository") {
private val HTMLElement.nameElement: HTMLInputElement
get() = getElementsByTagName("input")[0] as HTMLInputElement
private val HTMLElement.urlElement: HTMLInputElement
get() = getElementsByTagName("input")[1] as HTMLInputElement
var repositories: List<MavenPublishingRepository>
get() = elements.map {
MavenPublishingRepository(it.nameElement.value, it.urlElement.value)
}
set(value) {
data = value
}
override fun createPlainObject(): MavenPublishingRepository = MavenPublishingRepository("", "")
override fun HTMLElement.addContentBeforeRemoveButton(value: MavenPublishingRepository) {
createTextField("Repository name", "This name will be used to identify repository in grade").value = value.name
createTextField("Repository URL", "For example: https://repo.maven.apache.org/maven2/").value = value.url
}
override fun HTMLElement.updateElement(from: MavenPublishingRepository, to: MavenPublishingRepository) {
nameElement.value = to.name
urlElement.value = to.url
}
}

View File

@@ -2,29 +2,34 @@ package dev.inmo.kmppscriptbuilder.web.views
import kotlinx.dom.appendElement
import org.w3c.dom.*
import kotlin.random.Random
fun HTMLElement.createTextField(
label: String,
placeholder: String
): HTMLInputElement {
val uuid = "r" + Random.nextLong()
return appendElement("div") {
classList.add("uk-margin", "uk-width-1-1")
appendElement("label") {
classList.add("uk-form-label")
setAttribute("for", uuid)
innerText = label
}
}.appendElement("input") {
id = uuid
classList.add("uk-input", "uk-width-expand")
setAttribute("type", "text")
setAttribute("placeholder", placeholder)
} as HTMLInputElement
}.appendElement("label") {
classList.add("uk-form-label")
innerHTML = label
}.run {
val input = appendElement("input") {
classList.add("uk-input", "uk-width-expand")
setAttribute("type", "text")
setAttribute("placeholder", placeholder)
} as HTMLInputElement
input
}
}
fun HTMLElement.createButton(text: String): HTMLButtonElement = appendElement("button") {
fun HTMLElement.createPrimaryButton(text: String): HTMLButtonElement = (appendElement("button") {
classList.add("uk-button", "uk-button-primary")
innerHTML = text
} as HTMLButtonElement
} as HTMLButtonElement).apply {
innerText = text
}
fun HTMLElement.createCommonButton(text: String): HTMLButtonElement = (appendElement("button") {
classList.add("uk-button", "uk-button-default")
} as HTMLButtonElement).apply {
innerText = text
}

View File

@@ -7,19 +7,19 @@
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/uikit@3.6.17/dist/css/uikit.min.css" />
</head>
<body>
<div uk-sticky="sel-target: .uk-navbar-container; cls-active: uk-navbar-sticky; bottom: #transparent-sticky-navbar">
<nav class="uk-navbar-container" uk-navbar>
<div class="uk-navbar-left">
<div class="uk-padding-small uk-text-lead">Kotlin Publication Scripts Builder</div>
</div>
<div class="uk-navbar-right">
<ul class="uk-navbar-nav">
<li uk-tooltip="title: Open file"><a href="#"><span uk-icon="icon: pull"></span></a></li><!--Open file-->
<li uk-tooltip="title: Save file"><a href="#"><span uk-icon="icon: push"></span></a></li><!--Save file-->
</ul>
</div>
</nav>
</div>
<nav class="uk-navbar-container" uk-navbar>
<div class="uk-navbar-left">
<div class="uk-padding-small uk-text-lead">Kotlin Publication Scripts Builder</div>
<div class="uk-padding-small"><a href="https://github.com/InsanusMokrassar/KotlinPublicationScriptsBuilder"><img src="https://img.shields.io/github/stars/InsanusMokrassar/KotlinPublicationScriptsBuilder?label=Github&style=plastic"/></a></div>
</div>
<div class="uk-navbar-right">
<ul class="uk-navbar-nav">
<li uk-tooltip="title: Open config" id="openConfig"><a href="#"><span uk-icon="icon: pull"></span></a></li><!--Open file-->
<li uk-tooltip="title: Save config" id="saveConfig"><a href="#"><span uk-icon="icon: push"></span></a></li><!--Save file-->
<li uk-tooltip="title: Export script" id="exportScript"><a href="#"><span uk-icon="icon: upload"></span></a></li><!--Save file-->
</ul>
</div>
</nav>
<form class="uk-padding-small">
<fieldset class="uk-fieldset">
<legend class="uk-legend">Project type</legend>
@@ -30,11 +30,11 @@
</ul>
</div>
<legend class="uk-legend">Licenses</legend>
<div class="uk-padding-small">
<div class="uk-margin uk-width-1-1">
<input id="searchFilterInput" class="uk-input uk-width-expand" type="text" placeholder="License search filter">
</div>
<button class="uk-button uk-button-primary">Add empty license</button>
<div id="licensesListDiv" class="uk-padding-small">
<!-- <div class="uk-margin uk-width-1-1">-->
<!-- <input id="searchFilterInput" class="uk-input uk-width-expand" type="text" placeholder="License search filter">-->
<!-- </div>-->
<!-- <button class="uk-button uk-button-primary">Add empty license</button>-->
</div>
<legend class="uk-legend">Project information</legend>
@@ -66,13 +66,10 @@
</div>
<legend class="uk-legend">Developers info</legend>
<div id="developersListDiv" class="uk-padding-small">
</div>
<div id="developersListDiv" class="uk-padding-small"></div>
<legend class="uk-legend">Repositories info</legend>
<div class="uk-padding-small">
<button class="uk-button uk-button-primary">Add repository</button>
</div>
<div id="repositoriesListDiv" class="uk-padding-small"></div>
</fieldset>
</form>
<!-- UIkit JS -->