I want to create a popup that display example of possible usable tag in a textarea, I need it to be displayed on every content edition page on the admin.
What do I need to do to have that kind of block in the admin content edition page ?
Thanks.
I would create a module with:
A twig file containing the contents of the dialog.
A small JS file to show the dialog, using the jquery.dialog library included in Drupal 8.
Implement a hook_from_alter to attach the changes based on the form_ids.
Like the following, which works on node edit forms, but is not thoroughly tested:
file: templates/popuptag.html.twig
<div style="display: none;">
<div id="popuptag-dialog" title="Tag Usage">
<p>The tag can be used at vero eos et accusamus et iusto odio
dignissimos ducimus qui blanditiis praesentium voluptatum
deleniti atque corrupti quos dolores et quas molestias
excepturi sint occaecati cupiditate non provident, similique sunt.</p>
</div>
</div>
file: js/popuptag.dialog.js
(function ($) {
'use strict';
Drupal.behaviors.popuptag_dialog = {
attach: function (context) {
$( "#popuptag-dialog" ).dialog();
}
};
})(jQuery);
file: popuptag.module
/**
* Implements hook_theme().
*/
function popuptag_theme() {
return [
'popuptag' => [
'template' => 'popuptag',
'render element' => 'dialog',
],
];
}
/**
* Implements hook_form_alter().
*/
function popuptag_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
// Add here other form_ids if needed
if (in_array($form_id, ['node_page_edit_form'])) {
$form['popuptag'] = [
'#theme' => 'popuptag',
'#form_id' => $form_id,
];
$form["#attached"]["library"][] = 'popuptag/dialog';
}
}
file: popuptag.libraries.yml
dialog:
js:
js/popuptag.dialog.js: { minified: true }
dependencies:
- core/drupal.dialog
file: popuptag.info.yml
name: 'popuptag'
type: module
description: 'popuptag'
core: 8.x
package: 'Custom'
Related
I want to pass slot's content into a wrap components which one of it's prop will accept html string and render it, here's the code
Parent.vue
<Ellipsis style="max-width: 240px"
>Genes that helped people survive during the time of the Black Death are
more likely to be found in people with autoimmune diseases alive today.
Does this tell scientists anything about what surviving the COVID-19
pandemic might mean for the world’s population?
<template #tooltip>
<div style="text-align: center">
<i>Lorem Ipsum</i><br />
Sed ut perspiciatis unde omnis<br />
iste natus error sit voluptatem accusantium doloremque
laudantium,<br />
totam rem aperiam
</div>
</template>
</Ellipsis>
Ellipsis.vue
<template>
<Tooltip
ref="tooltipRef"
:content="tooltipContent"
>
<span
ref="content"
:class="ellipsisClassRef.valueOf()"
:style="ellipsisStyleRef"
#click="handleClickRef"
>
<slot></slot>
</span>
</Tooltip>
</template>
setup(props, { slots }) {
onMounted(() => {
if (slots.default !== undefined) {
tooltipContent.value = slots.default()[0].children;
}
if (slots.tooltip !== undefined) {
// have to get the html content like below
tooltipContent.value = `<div style="color: blue">hello world</div>`;
}
});
}
So the main problem is how to convert the slot's content( html content) I got from parent, and turn it into the string and pass it to the wrap content. When I console.log(slots.tooltip), I know I can get the html content like a nested object like following,
0
:
{__v_isVNode: true, __v_skip: true, type: 'div', props: {…}, key: null, …}
length
:
1
[[Prototype]]
:
Array(0)
at
:
ƒ at()
length
:
1
name
:
"at"
arguments
:
(...)
caller
:
(...)
[[Prototype]]
:
ƒ ()
concat
:
ƒ concat()
constructor
:
ƒ Array()
copyWithin
:
ƒ copyWithin(
but there a lot of things to cover if I want to concat them into a string, so I wonder if there's a way to just get the html content as a string or like a helper function to call to get format like <div style="color:blue">Hello world</div>.
You can use v-html directive to return Raw HTML.
<span v-html="rawHtml"></span>
The other option could be Fallback Content in the slot, if the fallback content is static.
<button type="submit">
<slot>
Submit <!-- fallback content -->
</slot>
</button>
But if the slot content is dynamic, then you should better use the Render Functions
h('div', { style: { color: 'blue' }, innerHTML: 'hello world' })
Check my other answer here for details:
Adding custom HTML element inside template using functions Vue.js
Example
const { createApp, h } = Vue;
const myComponent = {
setup(props) {
return () => h('div', { style: { color: 'blue' }, innerHTML: 'hello world' })
}
}
const App = {
components: {
myComponent
}
}
const app = createApp(App)
app.mount('#app')
<div id="app">
<my-component></my-component><br/>
</div>
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
I am working on a webs site which uses framer motion and next.js, the problem is that I need to remove a class that allow animations to happen before the path changes.
At first i tried
import {useRouter} from 'next/router'
export default function Projects(){
const [animated,setAnimated] = useState(true)
const {asPath} = useRouter()
useEffect(() => {
if(asPath!== '/projects'){
setAnimated(false)
}else{
setAnimated(true)
}
},[asPath])
return(
<div className={styles.activeLayer}>
<Image layout="fill" height='100%'src={allProjects[currentDisplay].backgroundImg} alt=""/>
<div className={`${styles.descriptionBox} + ${(**animated** ? styles.animatedOne:'')}`}>
<h2>
SomeTitle
</h2>
<p>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Modi excepturi esse ex odit saepe ipsam facere beatae similique doloremque quo quidem tempore quas molestias, commodi corporis natus possimus atque voluptatem?</p>
<div className={`${styles.iconComponent} + ${(animated ? styles.animatedFive:'')}`}>
<Tool imgArray={allProjects[currentDisplay].tools} />
</div>
this doesn't work cause animations run for a millisecond before it changes, and it actually needs to listen to the push request from a <Link>, but I don't know how to do it.
,
You can swap it. Set the state to default as false.
import {useRouter} from 'next/router'
const [animated,setAnimated] = useState(false)
const {asPath} = useRouter()
useEffect(() => {
if(asPath !== '/projects'){
setAnimated(false)
}else{
setAnimated(true)
}
},[asPath])
Instead of messing with some possibly incorrect initial state and using the useEffect hook to update it after the initial render, just set the correct initial state.
Example:
import { useRouter } from 'next/router';
export default function Projects() {
const {asPath} = useRouter();
const animated = asPath === '/projects';
return (
<div className={styles.activeLayer}>
<Image
layout="fill"
height='100%'
src={allProjects[currentDisplay].backgroundImg}
alt=""
/>
<div
className={`${styles.descriptionBox} + ${animated
? styles.animatedOne : ''}`
}
>
<h2>SomeTitle</h2>
<p>....</p>
<div
className={`${styles.iconComponent} + ${animated
? styles.animatedFive : ''}`
}
>
<Tool imgArray={allProjects[currentDisplay].tools} />
</div>
</div>
</div>
);
}
animated is what could be considered derived state and it's often considered a React anti-pattern to store derived state in React state. Just use the asPath === '/projects' value directly.
Example:
import { useRouter } from 'next/router';
export default function Projects() {
const {asPath} = useRouter();
return (
<div className={styles.activeLayer}>
<Image
layout="fill"
height='100%'
src={allProjects[currentDisplay].backgroundImg}
alt=""
/>
<div
className={`${styles.descriptionBox} + ${animated
? styles.animatedOne : ''}`
}
>
<h2>SomeTitle</h2>
<p>.....</p>
<div
className={`${styles.iconComponent} + ${animated
? styles.animatedFive : ''}`
}
>
<Tool imgArray={allProjects[currentDisplay].tools} />
</div>
</div>
</div>
);
}
Xquery newbie here again. I have the following xml:
<div type="section" n="1">
<p>Lorem ipsum dolor sit amet, <rs type="xyz">consectetur</rs> adipiscing <placeName ref="#PLACE1">elit</placeName>, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.<p>
<p>Duis aute irure <rs type="xyz">dolor</rs> in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt <rs type="place" ref="#PLACE2">mollit anim</rs> id est <rs type="xyz">laborum</rs>.<p>
</div>
I would like to create a unique ID for each "place" (rs type=place and placeName) based on its position within the text. To do this, I'd like to retrieve the following information for each place:
paragraph number within the "div type=section" node
character count from the beginning of the paragraph to the start of the child node (rs type=place or placeName).
Taking the above example, I'd expect these results:
<placeName ref="#PLACE1">elit</placeName>
paragraph: 1
character count: 51 ("Lorem ipsum dolor sit amet, consectetur adipiscing ")
<rs type="place" ref="#PLACE2">mollit anim</rs>
paragraph: 2
character count: 186 ("Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt ")
I must be missing something very simple, but I just can't figure out how to do this particular character count in xquery. I know preceding/following-sibling::text() will allow me to count until the previous/following node. Is there something like this to get to the beginning of the ancestor from a given node? Any help or direction would really be appreciated.
If you know the place and rs elements will be children of the p element then you just can select the preceding-sibling::node(), string-join them and then compute the string-length; so in XQuery 3.1 with the arrow operator (I hope exist supports that):
(//placeName | //rs[#type = 'place'])
!
(ancestor::p[1]/(., preceding-sibling::p) => count() ||
' : '
||
preceding-sibling::node() => string-join() => string-length()
)
https://xqueryfiddle.liberty-development.net/bFDb2BK/1
For the more complex case that your elements are descendants of the p I think the expression
(preceding::text() intersect ancestor::p[1]//text()) => string-join() => string-length()
gives the value you want (https://xqueryfiddle.liberty-development.net/bFDb2BK/4), I am not sure how well that performs.
If the arrow and the map operator are not supported or you prefer a FLOWR expression then
for $place in (//placeName | //rs[#type = 'place'])
return ($place/ancestor::p[1]/count((., preceding-sibling::p)) || ' : ' || string-length(string-join($place/preceding-sibling::node())))
for the simple child element case or
for $place in (//placeName | //rs[#type = 'place'])
return (
$place/ancestor::p[1]/count((., preceding-sibling::p))
|| ' : ' || string-length(string-join($place/preceding-sibling::node()))
|| ' : ' || string-length(string-join($place/(preceding::text() intersect ancestor::p[1]//text())))
)
as the descendant case (well, a comparison of both approaches, the last subexpression should work for the descendant case). An alternative to intersect could the use of the << operator: string-length(string-join($place/ancestor::p[1]//text()[. << $place])).
I want to create a post by using WP CLI's eval-file command on a file with the contents below. However, it fails to create a post, and furthermore doesn't return an error even though the second parameter of wp_insert_post() is set to true. Please can someone explain?
<?php
echo 'This file is being evaluated';
$new_post = Array(
'post-title' => 'New Post',
'post-content' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Efficiens dici potest. Videmus igitur ut conquiescere ne infantes quidem possint. Aliter enim nosmet ipsos nosse non possumus. An haec ab eo non dicuntur? Non enim iam stirpis bonum quaeret, sed animalis. Duo Reges: constructio interrete. Cave putes quicquam esse verius.'
);
wp_insert_post($new_post, true);
OK, so it was simple:
Use post_title etc., not post-title (underscore vs hyphen!).
I have been communicating back and forth with the Assemble team on this issue which they think seems to be an implementation issue rather than a bug.
Basically I have an assemble setup (not grunt-assemble) and am trying to pull in some data from a YAML file into a handlebars template via assembles YAML front-matter, passing it to a partial to render out.
What I'm getting when logging assembles data context is that the front matter variables are undefined.
Rather than go through everything I've done and code snippets here you can see this thread for a history of the conversation, code snippets and all things tried thus far: https://github.com/assemble/assemble/issues/758
I have also created a public demo repo on my github for anyone that wants to pull it down / fork for further investigation.
https://github.com/tgdev/assemble-yaml-demo
Why does the lodash template in the front matter return undefined, rather than the contents of the external YAML file?
UPDATE 11/09/16: Using json seems to work fine
Changing the data in the yml files to json (with .json extension) renders as expected with the front-matter middleware in tools/templates.js so it seems as though assemble and/or handlebars is having an issue parsing the yml files.
Here is a copy of the main yml file I'm testing with:
content: >
<h2>Page sub heading</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
<p>
<img src="http://placehold.it/1170x658" alt="" class="align-left" />
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>
<p><img src="http://placehold.it/1170x658" alt="" class="align-right" />
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>
So now, my question is why can't the lodash templates in the assemble front-matter of the handlebars template parse the yaml files (undefined in logs)?
Ok so I finally got to the bottom of it.
The problem was this...
Assemble doesn't support external yaml font files so I needed to use this snippet of code just before my middleware.
import yaml from 'js-yaml';
templates.dataLoader('yml', function(str) {
return yaml.safeLoad(str);
});
Then the expander middleware loads the yaml file contents via a lodash template from the front-matter, adding it to the data context.
So here's the whole assemble build task file for reference (I hope it helps someone in the future).
import assemble from 'assemble';
import expander from 'expander';
import merge from 'lodash.merge';
import yaml from 'js-yaml';
import through from 'through2';
import plumber from 'gulp-plumber';
import { projectName, templatesPathConfig as path } from '../project.config';
export default function templates() {
// Create assemble instance
let templates = assemble();
function expand(data) {
// `data` is front-matter
const ctx = merge({}, templates.cache.data, data);
return expander.process(ctx, data);
}
// #reference
// https://github.com/node-base/base-data#dataloader
//
// #notes
// Loading yaml files is not built in. Assemble uses
// base-data now. You can add yaml loading by using
// a custom dataLoader.
templates.dataLoader('yml', function(str) {
return yaml.safeLoad(str);
});
templates.data(path.data); // path.data = ./src/templates/data/**/*.yml
templates.preRender(/\.(hbs|html)$/, function (view, next) {
view.data = expand(view.data);
next();
});
templates.task('preload', function(cb) {
templates.partials(path.partials);
templates.layouts(path.layouts);
// Register helpers
templates.helpers(path.helpers);
// Add master pages and listing page
templates.pages(path.pages);
templates.pages(path.referencePages);
templates.pages(path.index);
// Add custom data
templates.data({
projectName: projectName
});
cb();
});
// Assemble task to build template files
templates.task('build', ['preload'], () => {
// Render out the template files to 'dist'
return templates.toStream('pages')
.pipe(plumber({
errorHandler: err => {
log.error(`${err.message}.`);
}
}))
.pipe(templates.renderFile())
.pipe(plumber.stop())
.pipe(renameExt())
.pipe(templates.dest('dist'));
});
// Run the Assemble build method
templates.build('build');
}
// Change the file extension through node stream
function renameExt() {
return through.obj( (file, enc, next) => {
file.extname = '.html';
next(null, file);
});
}