BootBrander - Bootstrap .less 生成器 UI(第 3 部分 / UI 优化)





0/5 (0投票)
创建一个 MVC 站点,用户可以通过输入更改 bootstrap 变量,并生成自定义品牌的 bootstrap.css。
引言
这是本系列的第三部分。如果您还没有阅读第 1 部分和第 2 部分,您应该先阅读它们。
在上一篇文章中,我们让我们的 Bootstrap 颜色选择器 UI 处于工作状态。但是,仍然缺少一些东西。并非所有代表颜色的变量都暴露给了用户。
这是因为在解析 variables.less
时,我们得出结论,所有值以“#
”开头的变量都是颜色。这是正确的,但这并不意味着它们是所有的颜色。
如果您愿意,请加入我们。下载第二篇文章是一个很好的起点。
文章索引
其他变量类型
如果我们查看 variables.less
,我们可以找到更多类型的变量。有些不代表颜色。有些是其他变量的引用,有些包含颜色函数。让我们看几个例子。
只是一种颜色
@brand-primary: #428bca;
非颜色
@font-family-sans-serif: "Helvetica Neue", Helvetica, Arial, sans-serif;
@font-size-base: 14px;
@font-size-large: ceil(@font-size-base * 1.25); // ~18px
对另一个变量的引用。它可能是一种颜色
@link-color: @brand-primary;
一个颜色函数
@gray: lighten(#000, 33.5%); // #555
@link-hover-color: darken(@link-color, 15%);
最后一个显然是最糟糕的。它既是函数又是引用。
让我们暂时忽略如何实际处理颜色函数。但我们需要创建一个函数,可以告诉我们某个东西最终是否会是颜色。
如果一个变量是对另一个变量的引用,我们将不得不向上遍历树到终点。最初的值是颜色。它可能需要一些步骤才能达到。
@top-level-color: #ff0000;
@second-color: @top-level-color;
@third-color: @second-color;
当我们找到顶层变量时,它可能根本不是颜色。那么我们应该丢弃它。
isColor 函数
让我们创建一个函数,它将告诉我们一个变量是否是颜色。为了解决我们的引用变量,这将是一个递归函数。我们很幸运,因为我们知道,如果我们找到一个引用,目标变量应该已经在我们的模型中。这是因为 less 编译器不允许引用尚未声明的变量。
/**
* Finds out if a value is color
* @function isColor
* @returns {boolean}
*/
function isColor(value) {
if(value.substr(0,1)==="#") {
return true;
}
else {
if(value.substr(0,1)==="@") {
if(viewModel.variables.hasOwnProperty(value)) {
console.log(viewModel.variables[value]);
return isColor(viewModel.variables[value].value());
}
}
}
return false;
}
所以它所做的是;首先查看实际值是否以“#
”开头,如果是,我们就完成了。否则,我们需要查看该值是否是引用。如果是,我们将在我们的变量对象中查找它,看看它之前是否已添加到我们的 viewModel
中,以及它是否是颜色。
更改解析器
在解析器中,我们应该更改这一行...
if(value.subtr(0,1)==="#") {
...变成...
if(isColor(value) {
但这并不是我们唯一会做的事情。对于这个例子,我们只关注颜色。但也许我们会在某个时候允许用户更改所有变量。所以我们还会将所有非颜色的内容添加到 viewModel
中。
这不会造成任何伤害。然后我们可以在以后轻松添加功能。所以,在我们的解析器循环中,我们将首先在 isColor if
的末尾声明继续,并在其下方添加其他类型为隐藏的变量。
整个 parseVariablesLess
函数应该看起来像这样
/**
* Parses the variables.less file
* @function parseVariablesLess
*/
function parseVariablesLess(responseText) {
var lines = responseText.split('\n'),
line,
i,
nameValue,
name,
value,
category
;
for (i = 0, j = lines.length; i < j; i++) {
line = lines[i];
if (line.substr(0, 4) === "//==") {
category = line.substr(5, line.length).trim();
viewModel.categories[category] = {
variables: ko.observableArray()
};
continue;
}
if (line.substr(0, 1) === "@") {
//this is a variable
nameValue = line.split(":");
name = nameValue[0].trim();
value = nameValue[1].trim();
//This line has change to! In the previous version we just replaced the ;
//but there could be comments behind them
value = value.split(";")[0];
if (storedViewData.hasOwnProperty(name)) {
value = storedViewData[name];
}
if (isColor(value)) {
//this is color
if (value.length === 4) {
value += value.substr(1, 3);
}
//add the name to the categories
viewModel.categories[category].variables.push(name);
//add the variable to the variables object
viewModel.variables[name] = {
type: ko.observable("color"),
value: ko.observable(value)
}
continue;
}
//add the variable to the variables object
//storing them for later potential use
viewModel.variables[name] = {
type: ko.observable("hidden"),
value: ko.observable(value)
}
}
}
}
处理引用颜色
如果我现在启动界面,我们将看到:
如果用户点击 @component-active-bg
的色轮并更改它,它将破坏与原始颜色 @brand-primary
的链接。尽管我觉得这应该是可能的,但我也希望有一种方法可以“跳转”到另一种颜色。
为此,我们需要引入一种新的变量类型“color-reference
”,并创建一个新的 knockout 模板。一个对话框和我们 viewModel
中的一个函数。
我们将从检测这种新类型开始。让我们更改 isColor
周围的 if
;
if (isColor(value)) {
//this is color
if (value.length === 4) {
value += value.substr(1, 3);
}
//add the name to the categories
viewModel.categories[category].variables.push(name);
if (value.substr(0, 1) === "@") {
//add the variable to the variables object
viewModel.variables[name] = {
type: ko.observable("color-reference"),
value: ko.observable(value)
}
}
else {
//add the variable to the variables object
viewModel.variables[name] = {
type: ko.observable("color"),
value: ko.observable(value)
}
}
continue;
}
测试
运行网站,我们看到并非所有类别都显示。控制台中有一个错误。这来自 knockout; 消息:找不到 ID 为 color-reference 的模板
我们需要提供一个模板才能开始。只需复制颜色模板并添加一些更改
<script type="text/html" id="color-reference">
<div class="row">
<div class="col-xs-9" style="padding-right: 0;">
<a class="form-control" data-bind="text: value" ></a>
</div>
<div class="col-xs-3" style="padding-left: 0;">
<input type="color"
class="form-control" data-bind="value: value" />
</div>
</div>
</script>
我将输入框更改为一个链接。如果您将鼠标悬停在它上面,您会看到它带有下划线装饰。如果用户点击此链接,我希望显示一个对话框,其中包含此颜色所继承的颜色的 UI。
但首先,我们需要一个函数来绑定到链接。我们将把它添加到 viewModel
中,以便我们可以用 knockout 绑定它。
var viewModel = window.viewModel = {
categories: {},
variables: {},
gotoReference: function (data) {
alert(data.value());
}
},
然后将它绑定到链接。
<a class="form-control" data-bind="text: value,
click: $root.gotoReference"></a>
那实际上是什么意思?它表示转到 viewModel
的根目录,找到函数 gotoReference
并将其绑定到 click 事件处理程序。
测试
如果我们运行这个并在脚手架中找到例如 @link-color
并点击表单控件,它应该弹出一个警告,显示“@brand-primary
”。
创建参考对话框
如果用户点击这样的链接,我想启动一个带有颜色模板的对话框。如果引用颜色本身是对另一个颜色的引用,它应该启动另一个屏幕。
要创建对话框,我将使用 bootbox
。您可以通过 NuGet 包管理器找到它。安装后,我倾向于直接将其添加到 BundleConfig.cs 中的 bootstrap bundle 中。
运行...
bootbox.alert("Hello world!");
...从控制台应该会给您一个漂亮的对话框。
为了填充对话框,我们将使用我们之前创建的 knockout 模板。然后,我们将创建与对话框的新绑定。让我们开始吧!
var viewModel = {
categories: {},
variables: {},
gotoReference: function (data) {
var $dialog,
//This will select the parent variable
variableModel = viewModel.variables[data.value()]
;
//Create a reference to the main viewModel
variableModel.gotoReference = viewModel.gotoReference;
$dialog = bootbox.dialog({
title: data.value(),
//By sending in the type, this would create a new reference modal if needed
message: $("#" + variableModel.type).html(),
buttons: {
ok: {
label: "Ok"
}
}
});
//Apply binding with our variableModel, but just for the dialog!
ko.applyBindings(variableModel, $dialog.get(0));
}
},
测试
- 启动网站
- 找到“下拉菜单”部分
- 点击
@dropdown-link-active-bg
的值- 应该会打开一个对话框,其中包含名称
@component-active-bg
和值@brand-primary
- 应该会打开一个对话框,其中包含名称
- 点击值
@brand-primary
- 应该会打开一个对话框,其中包含名称
@brand-primary
,并且它应该是一种实际的颜色
- 应该会打开一个对话框,其中包含名称
- 更改颜色并关闭对话框
- 更改应该会在整个网站中反映出来。
修复颜色微调器
您可能已经看到,这些引用变量的颜色微调器只是黑色。我们需要做几件事。首先,我们需要能够找到父颜色的真实值。我们将通过函数 getColor
来实现。
/**
* Finds out if a value is color
* @function getColor
* @param {string} name The name of this variable
* @returns {string} The color
*/
function getColor(name) {
var value = viewModel.variables[name].value();
if (value.substr(0, 1) === "@") {
return getColor(value);
}
else {
return value;
}
}
除此之外,我们还需要一个 ko.computed
变量来返回此颜色并设置微调器的值。但首先,我们需要修复一些东西。我将展示我们将在 color-reference
可观察对象周围得到的代码。
viewModel.variables[name] = {
type: ko.observable("color-reference"),
value: ko.observable(value)
};
viewModel.variables[name].colorSpinnerValue=ko.computed(function () {
return getColor(viewModel.variables[name].value());
});
我们需要在变量实际创建之后设置 colorSpinnerValue
。因为否则,当我们尝试获取它时,它还不存在。但这并不是问题。这段代码将更改所有引用字段为最后一个颜色。最初,它会起作用。但我们是在循环中执行此操作。因此,如果计算函数被再次调用,变量“name
”将不再具有相同的值。
我们可以通过两种方式修复
- 创建一个名为
parseLine
的函数,并传入单行。然后name
变量将在此闭包中。 - 在循环内部创建一个闭包。(这会导致
jshint/jslint
抱怨。)
尽管这不是正确的方法,但我为了简洁起见选择了第二种。
(function (name) {
viewModel.variables[name] = {
type: ko.observable("color-reference"),
value: ko.observable(value)
};
viewModel.variables[name].colorSpinnerValue = ko.computed(function () {
return getColor(viewModel.variables[name].value());
});
})(name);
现在,我们仍然需要更改 knockout 绑定
<div class="col-xs-3" style="padding-left: 0;">
<input type="color" class="form-control"
data-bind="value: colorSpinnerValue" />
</div>
重新执行之前的测试现在应该会在所有色轮中反映出更改。
总结
我认为它需要更多的功能。用户应该能够“打破”与引用变量的链接。您可以通过更改变量的 type()
来实现这一点,knockout 将立即替换为正常的颜色对话框。
我们仍然需要处理 darken 和 lighten 函数。
我还觉得应该抑制没有选项的类别。但我将把这个问题留给您。
对我来说,下一步是让用户能够下载反映其选择的 .css 文件。