Upgrade `param-input` to Octane (#196)
* Convert `param-input` to glimmer components
This commit is contained in:
parent
bd602b02a0
commit
8028b9f16a
|
@ -0,0 +1,55 @@
|
|||
<div class="param {{if this.valid 'valid' 'invalid'}}">
|
||||
{{#if (eq this.type "boolean")}}
|
||||
{{#if @info.nullable}}
|
||||
<ComboBox
|
||||
@valueAttribute="id"
|
||||
@value={{this.nullableBoolValue}}
|
||||
@nameProperty="name"
|
||||
@content={{this.boolTypes}}
|
||||
@onChange={{this.updateNullableBoolValue}}
|
||||
/>
|
||||
{{else}}
|
||||
<Input
|
||||
@type="checkbox"
|
||||
@checked={{this.boolvalue}}
|
||||
{{on "change" this.updateBoolValue}}
|
||||
/>
|
||||
{{/if}}
|
||||
<span class="param-name">{{@info.identifier}}</span>
|
||||
|
||||
{{else if (eq this.type "int")}}
|
||||
<Input
|
||||
@type="number"
|
||||
@value={{this.value}}
|
||||
{{on "change" this.updateValue}}
|
||||
/>
|
||||
<span class="param-name">{{@info.identifier}}</span>
|
||||
|
||||
{{else if (eq this.type "string")}}
|
||||
<TextField
|
||||
@value={{this.value}}
|
||||
@type="text"
|
||||
@onChange={{this.updateValue}}
|
||||
/>
|
||||
<span class="param-name">{{@info.identifier}}</span>
|
||||
|
||||
{{else if (eq this.type "user_id")}}
|
||||
<EmailGroupUserChooser
|
||||
@value={{this.value}}
|
||||
@options={{(hash maximum=1)}}
|
||||
@onChange={{this.updateValue}}
|
||||
/>
|
||||
<span class="param-name">{{@info.identifier}}</span>
|
||||
|
||||
{{else if (eq this.type "user_list")}}
|
||||
<EmailGroupUserChooser
|
||||
@value={{this.value}}
|
||||
@onChange={{this.updateValue}}
|
||||
/>
|
||||
<span class="param-name">{{@info.identifier}}</span>
|
||||
|
||||
{{else}}
|
||||
<TextField @value={{this.value}} @onChange={{this.updateValue}} />
|
||||
<span class="param-name">{{@info.identifier}}</span>
|
||||
{{/if}}
|
||||
</div>
|
|
@ -1,10 +1,11 @@
|
|||
import Component from "@ember/component";
|
||||
import Component from "@glimmer/component";
|
||||
import I18n from "I18n";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import Category from "discourse/models/category";
|
||||
import { dasherize } from "@ember/string";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import { computed } from "@ember/object";
|
||||
import { action } from "@ember/object";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { inject as service } from "@ember/service";
|
||||
|
||||
const layoutMap = {
|
||||
int: "int",
|
||||
|
@ -26,58 +27,73 @@ const layoutMap = {
|
|||
user_list: "user_list",
|
||||
};
|
||||
|
||||
function allowsInputTypeTime() {
|
||||
try {
|
||||
const inp = document.createElement("input");
|
||||
inp.attributes.type = "time";
|
||||
inp.attributes.type = "date";
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export default class ParamInput extends Component {
|
||||
@service site;
|
||||
|
||||
export default Component.extend({
|
||||
classNameBindings: ["valid:valid:invalid", ":param"],
|
||||
@tracked value;
|
||||
@tracked boolValue;
|
||||
@tracked nullableBoolValue;
|
||||
|
||||
boolTypes: [
|
||||
boolTypes = [
|
||||
{ name: I18n.t("explorer.types.bool.true"), id: "Y" },
|
||||
{ name: I18n.t("explorer.types.bool.false"), id: "N" },
|
||||
{ name: I18n.t("explorer.types.bool.null_"), id: "#null" },
|
||||
],
|
||||
initialValues: null,
|
||||
];
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
|
||||
if (this.initialValues && this.info.identifier in this.initialValues) {
|
||||
this.set("value", this.initialValues[this.info.identifier]);
|
||||
const identifier = this.args.info.identifier;
|
||||
const initialValues = this.args.initialValues;
|
||||
|
||||
// access parsed params if present to update values to previously ran values
|
||||
if (initialValues && identifier in initialValues) {
|
||||
const initialValue = initialValues[identifier];
|
||||
if (this.type === "boolean") {
|
||||
if (this.args.info.nullable) {
|
||||
this.nullableBoolValue = initialValue;
|
||||
} else {
|
||||
this.boolValue = initialValue !== "false";
|
||||
}
|
||||
} else {
|
||||
this.value =
|
||||
this.args.info.type === "category_id"
|
||||
? this.dasherizeCategoryId(initialValue)
|
||||
: initialValue;
|
||||
}
|
||||
} else {
|
||||
// if no parsed params then get and set default values
|
||||
const params = this.args.params;
|
||||
this.value =
|
||||
this.args.info.type === "category_id"
|
||||
? this.dasherizeCategoryId(params[identifier])
|
||||
: params[identifier];
|
||||
this.boolValue = params[identifier] !== "false";
|
||||
this.nullableBoolValue = params[identifier];
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
value: computed("params", "info.identifier", {
|
||||
get() {
|
||||
return this.params[this.get("info.identifier")];
|
||||
},
|
||||
set(key, value) {
|
||||
this.params[this.get("info.identifier")] = value?.toString();
|
||||
return value;
|
||||
},
|
||||
}),
|
||||
get type() {
|
||||
const type = this.args.info.type;
|
||||
if ((type === "time" || type === "date") && !allowsInputTypeTime()) {
|
||||
return "string";
|
||||
}
|
||||
return layoutMap[type] || "generic";
|
||||
}
|
||||
|
||||
valueBool: computed("params", "info.identifier", {
|
||||
get() {
|
||||
return this.params[this.get("info.identifier")] !== "false";
|
||||
},
|
||||
set(key, value) {
|
||||
value = !!value;
|
||||
this.params[this.get("info.identifier")] = value.toString();
|
||||
return value;
|
||||
},
|
||||
}),
|
||||
get valid() {
|
||||
const nullable = this.args.info.nullable;
|
||||
// intentionally use 'this.args' here instead of 'this.type'
|
||||
// to get the original key instead of the translated value from the layoutMap
|
||||
const type = this.args.info.type;
|
||||
let value;
|
||||
|
||||
if (type === "boolean") {
|
||||
value = nullable ? this.nullableBoolValue : this.boolValue;
|
||||
} else {
|
||||
value = this.value;
|
||||
}
|
||||
|
||||
@discourseComputed("value", "info.type", "info.nullable")
|
||||
valid(value, type, nullable) {
|
||||
if (isEmpty(value)) {
|
||||
return nullable;
|
||||
}
|
||||
|
@ -104,10 +120,6 @@ export default Component.extend({
|
|||
case "post_id":
|
||||
return isPositiveInt || /\d+\/\d+(\?u=.*)?$/.test(value);
|
||||
case "category_id":
|
||||
if (!isPositiveInt && value !== dasherize(value)) {
|
||||
this.set("value", dasherize(value));
|
||||
}
|
||||
|
||||
if (isPositiveInt) {
|
||||
return !!this.site.categories.find((c) => c.id === intVal);
|
||||
} else if (/\//.test(value)) {
|
||||
|
@ -132,21 +144,55 @@ export default Component.extend({
|
|||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("info.type")
|
||||
layoutType(type) {
|
||||
if ((type === "time" || type === "date") && !allowsInputTypeTime()) {
|
||||
return "string";
|
||||
dasherizeCategoryId(value) {
|
||||
const isPositiveInt = /^\d+$/.test(value);
|
||||
if (!isPositiveInt && value !== dasherize(value)) {
|
||||
return dasherize(value);
|
||||
}
|
||||
if (layoutMap[type]) {
|
||||
return layoutMap[type];
|
||||
}
|
||||
return "generic";
|
||||
},
|
||||
return value;
|
||||
}
|
||||
|
||||
@discourseComputed("layoutType")
|
||||
layoutName(layoutType) {
|
||||
return `admin/components/q-params/${layoutType}`;
|
||||
},
|
||||
});
|
||||
@action
|
||||
updateValue(input) {
|
||||
// handle selectKit inputs as well as traditional inputs
|
||||
const value = input.target ? input.target.value : input;
|
||||
if (value.length) {
|
||||
this.value =
|
||||
this.args.info.type === "category_id"
|
||||
? this.dasherizeCategoryId(value.toString())
|
||||
: value.toString();
|
||||
} else {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
this.args.updateParams(this.args.info.identifier, this.value);
|
||||
}
|
||||
|
||||
@action
|
||||
updateBoolValue(input) {
|
||||
this.boolValue = input.target.checked;
|
||||
this.args.updateParams(
|
||||
this.args.info.identifier,
|
||||
this.boolValue.toString()
|
||||
);
|
||||
}
|
||||
|
||||
@action
|
||||
updateNullableBoolValue(input) {
|
||||
this.nullableBoolValue = input;
|
||||
this.args.updateParams(this.args.info.identifier, this.nullableBoolValue);
|
||||
}
|
||||
}
|
||||
|
||||
function allowsInputTypeTime() {
|
||||
try {
|
||||
const input = document.createElement("input");
|
||||
input.attributes.type = "time";
|
||||
input.attributes.type = "date";
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
{{#if @hasParams}}
|
||||
<div class="query-params">
|
||||
{{#each @paramInfo as |pinfo|}}
|
||||
<ParamInput
|
||||
@params={{@params}}
|
||||
@initialValues={{@initialValues}}
|
||||
@info={{pinfo}}
|
||||
@updateParams={{@updateParams}}
|
||||
/>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
|
@ -312,6 +312,12 @@ export default Controller.extend({
|
|||
});
|
||||
},
|
||||
|
||||
// This is necessary with glimmer's one way data stream to get the child's
|
||||
// changes of 'params' to bubble up.
|
||||
updateParams(identifier, value) {
|
||||
this.selectedItem.set(`params.${identifier}`, value);
|
||||
},
|
||||
|
||||
run() {
|
||||
if (this.get("selectedItem.dirty")) {
|
||||
return;
|
||||
|
@ -325,6 +331,7 @@ export default Controller.extend({
|
|||
showResults: false,
|
||||
params: JSON.stringify(this.selectedItem.params),
|
||||
});
|
||||
|
||||
ajax(
|
||||
"/admin/plugins/explorer/queries/" +
|
||||
this.get("selectedItem.id") +
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
{{#if info.nullable}}
|
||||
{{combo-box
|
||||
valueAttribute="id"
|
||||
value=value
|
||||
nameProperty="name"
|
||||
content=boolTypes
|
||||
}}
|
||||
{{else}}
|
||||
{{input type="checkbox" checked=valueBool}}
|
||||
{{/if}}
|
||||
|
||||
<span class="param-name">{{info.identifier}}</span>
|
|
@ -1,2 +0,0 @@
|
|||
{{text-field value=value}}
|
||||
<span class="param-name">{{info.identifier}}</span>
|
|
@ -1,2 +0,0 @@
|
|||
{{input type="number" value=value}}
|
||||
<span class="param-name">{{info.identifier}}</span>
|
|
@ -1,2 +0,0 @@
|
|||
{{text-field value=value type="text"}}
|
||||
<span class="param-name">{{info.identifier}}</span>
|
|
@ -1,7 +0,0 @@
|
|||
{{email-group-user-chooser
|
||||
value=value
|
||||
options=(hash maximum=1)
|
||||
onChange=(action (mut value))
|
||||
}}
|
||||
|
||||
<span class="param-name">{{info.identifier}}</span>
|
|
@ -1,2 +0,0 @@
|
|||
{{email-group-user-chooser value=value onChange=(action (mut value))}}
|
||||
<span class="param-name">{{info.identifier}}</span>
|
|
@ -212,17 +212,13 @@
|
|||
</div>
|
||||
|
||||
<form class="query-run" {{action "run" on="submit"}}>
|
||||
{{#if selectedItem.hasParams}}
|
||||
<div class="query-params">
|
||||
{{#each selectedItem.param_info as |pinfo|}}
|
||||
{{param-input
|
||||
params=selectedItem.params
|
||||
initialValues=parsedParams
|
||||
info=pinfo
|
||||
}}
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
<ParamInputsWrapper
|
||||
@hasParams={{selectedItem.hasParams}}
|
||||
@params={{selectedItem.params}}
|
||||
@initialValues={{parsedParams}}
|
||||
@paramInfo={{selectedItem.param_info}}
|
||||
@updateParams={{action "updateParams"}}
|
||||
/>
|
||||
|
||||
{{#if runDisabled}}
|
||||
{{#if saveDisabled}}
|
||||
|
|
|
@ -3,13 +3,12 @@
|
|||
<p>{{model.description}}</p>
|
||||
|
||||
<form class="query-run" {{action "run" on="submit"}}>
|
||||
{{#if hasParams}}
|
||||
<div class="query-params">
|
||||
{{#each model.param_info as |pinfo|}}
|
||||
{{param-input params=model.params info=pinfo}}
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
<ParamInputsWrapper
|
||||
@hasParams={{hasParams}}
|
||||
@params={{model.params}}
|
||||
@paramInfo={{model.param_info}}
|
||||
@updateParams={{action "updateParams"}}
|
||||
/>
|
||||
|
||||
{{d-button
|
||||
action=(action "run")
|
||||
|
|
Loading…
Reference in New Issue