init
This commit is contained in:
commit
412875b25b
|
@ -0,0 +1,2 @@
|
|||
.discourse-site
|
||||
HELP
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2019 joffreyjaffeux
|
||||
|
||||
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.
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"name": "discourse-placeholder-theme-component",
|
||||
"about_url": "https://github.com/discourse/discourse-placeholder-theme-component",
|
||||
"license_url": "https://github.com/discourse/discourse-placeholder-theme-component/blob/master/LICENSE"
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
.d-wrap[data-wrap=placeholder] {
|
||||
padding: 0.5em;
|
||||
border: 1px solid $primary-medium;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.discourse-placeholder-name {
|
||||
width: 200px;
|
||||
font-weight: 700;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
.discourse-placeholder-value,
|
||||
.discourse-placeholder-select {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.discourse-placeholder-value {
|
||||
width: 100%;
|
||||
padding: 0.5em;
|
||||
line-height: $line-height-small;
|
||||
color: $primary;
|
||||
background-color: $secondary;
|
||||
border: 1px solid $primary-medium;
|
||||
}
|
||||
|
||||
.discourse-placeholder-select {
|
||||
width: 100%;
|
||||
margin: 0.5em 0;
|
||||
line-height: $line-height-small;
|
||||
color: $primary;
|
||||
background-color: $secondary;
|
||||
border: 1px solid $primary-medium;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,207 @@
|
|||
<script type="text/discourse-plugin" version="0.8.30">
|
||||
api.decorateCooked($cooked => {
|
||||
const VALID_TAGS = "h1, h2, h3, h4, h5, h6, p, code, blockquote, .md-table, li";
|
||||
const DELIMITER = "=";
|
||||
|
||||
// http://davidwalsh.name/javascript-debounce-function
|
||||
function debounce(func, wait, immediate) {
|
||||
let timeout;
|
||||
|
||||
return function() {
|
||||
const context = this,
|
||||
args = arguments;
|
||||
|
||||
const later = function() {
|
||||
timeout = null;
|
||||
if (!immediate) func.apply(context, args);
|
||||
};
|
||||
|
||||
const callNow = immediate && !timeout;
|
||||
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
|
||||
if (callNow) func.apply(context, args);
|
||||
};
|
||||
}
|
||||
|
||||
function processChange($cooked, inputEvent, mappings) {
|
||||
const value = inputEvent.target.value;
|
||||
const key = inputEvent.target.dataset.key;
|
||||
|
||||
let newValue;
|
||||
if (value && value.length && value !== "none") {
|
||||
newValue = value;
|
||||
} else {
|
||||
newValue = `${DELIMITER}${key}${DELIMITER}`;
|
||||
}
|
||||
|
||||
$cooked.find(VALID_TAGS).each((index, elem) => {
|
||||
let replaced = false;
|
||||
const mapping = mappings[index];
|
||||
let newInnnerHTML = elem.innerHTML;
|
||||
let diff = 0;
|
||||
|
||||
if (!mapping) {
|
||||
return;
|
||||
}
|
||||
|
||||
mapping.forEach(m => {
|
||||
if (m.pattern !== `${DELIMITER}${key}${DELIMITER}`) {
|
||||
m.position = m.position + diff;
|
||||
return;
|
||||
}
|
||||
|
||||
replaced = true;
|
||||
|
||||
const previousLength = m.length;
|
||||
const prefix = newInnnerHTML.slice(0, m.position + diff);
|
||||
const suffix = newInnnerHTML.slice(
|
||||
m.position + diff + m.length,
|
||||
newInnnerHTML.length
|
||||
);
|
||||
newInnnerHTML = `${prefix}${newValue}${suffix}`;
|
||||
|
||||
m.length = newValue.length;
|
||||
m.position = m.position + diff;
|
||||
diff = diff + newValue.length - previousLength;
|
||||
});
|
||||
|
||||
if (replaced) {
|
||||
elem.innerHTML = newInnnerHTML;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function addSelectOption(select, options = {}) {
|
||||
const option = document.createElement("option");
|
||||
option.classList.add("discourse-placeholder-option");
|
||||
option.value = options.value;
|
||||
option.text = options.description || options.value;
|
||||
|
||||
if (options.selected) {
|
||||
option.setAttribute("selected", true);
|
||||
}
|
||||
|
||||
select.appendChild(option);
|
||||
}
|
||||
|
||||
function processPlaceholders(placeholders, $cooked, mappings) {
|
||||
const keys = Object.keys(placeholders);
|
||||
const pattern = `(${DELIMITER}(?:${keys.join("|")})${DELIMITER})`;
|
||||
const regex = new RegExp(pattern, "g");
|
||||
|
||||
$cooked.find(VALID_TAGS).each((index, elem) => {
|
||||
const innerHTML = elem.innerHTML;
|
||||
let match;
|
||||
|
||||
mappings[index] = mappings[index] || [];
|
||||
|
||||
while ((match = regex.exec(innerHTML)) != null) {
|
||||
mappings[index].push({
|
||||
pattern: match[1],
|
||||
position: match.index,
|
||||
length: match[1].length
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const mappings = [];
|
||||
const placeholders = {};
|
||||
|
||||
$cooked.find(".d-wrap[data-wrap=placeholder]:not(.placeholdered)").each((index, elem) => {
|
||||
const dataKey = elem.dataset.key;
|
||||
if (!dataKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
const defaultValue = elem.dataset.default;
|
||||
const defaultValues = (elem.dataset.defaults || "").split(",").filter(x => x);
|
||||
const description = elem.dataset.description;
|
||||
|
||||
placeholders[dataKey] = {
|
||||
default: defaultValue,
|
||||
defaults: defaultValues,
|
||||
description
|
||||
}
|
||||
|
||||
elem.classList.add("placeholdered")
|
||||
|
||||
const span = document.createElement("span");
|
||||
span.classList.add("discourse-placeholder-name")
|
||||
span.innerText = dataKey;
|
||||
elem.appendChild(span)
|
||||
|
||||
if (defaultValues && defaultValues.length) {
|
||||
const select = document.createElement("select");
|
||||
select.classList.add("discourse-placeholder-select")
|
||||
select.dataset.key = dataKey;
|
||||
|
||||
if (description) {
|
||||
addSelectOption(select, { value: "none", description });
|
||||
}
|
||||
|
||||
defaultValues.forEach(value =>
|
||||
addSelectOption(select, {
|
||||
value,
|
||||
selected: defaultValue === value
|
||||
})
|
||||
);
|
||||
|
||||
elem.appendChild(select);
|
||||
} else {
|
||||
const input = document.createElement("input");
|
||||
input.classList.add("discourse-placeholder-value")
|
||||
input.dataset.key = dataKey;
|
||||
|
||||
if (description) {
|
||||
input.setAttribute("placeholder", description);
|
||||
}
|
||||
|
||||
if (defaultValue) {
|
||||
input.value = defaultValue;
|
||||
}
|
||||
|
||||
elem.appendChild(input)
|
||||
}
|
||||
})
|
||||
|
||||
if (Object.keys(placeholders).length > 0) {
|
||||
processPlaceholders(placeholders, $cooked, mappings);
|
||||
}
|
||||
|
||||
$cooked
|
||||
.on(
|
||||
"input",
|
||||
".discourse-placeholder-value",
|
||||
debounce(inputEvent => {
|
||||
processChange($cooked, inputEvent, mappings);
|
||||
}, 250)
|
||||
)
|
||||
.on(
|
||||
"change",
|
||||
".discourse-placeholder-select",
|
||||
debounce(inputEvent => {
|
||||
processChange($cooked, inputEvent, mappings);
|
||||
}, 250)
|
||||
);
|
||||
|
||||
// trigger fake event to setup initial state
|
||||
Object.keys(placeholders).forEach(placeholderKey => {
|
||||
const placeholder = placeholders[placeholderKey];
|
||||
|
||||
const value =
|
||||
placeholder.default ||
|
||||
(placeholder.defaults.length && !placeholder.description
|
||||
? placeholder.defaults[0]
|
||||
: null);
|
||||
|
||||
processChange(
|
||||
$cooked,
|
||||
{ target: { value, dataset: { key: placeholderKey } } },
|
||||
mappings
|
||||
);
|
||||
});
|
||||
});
|
||||
</script>
|
Loading…
Reference in New Issue