Leaderboard
Popular Content
Showing content with the highest reputation on 11/09/2021 in all areas
-
I have had a play and some of the implementation is literally, trivial ?. SSE SSE is so simple it is unbelievable at first. You only need two things: in our case PHP and a modern browser...batteries included! Example adapted from w3schools. More examples at MDN. server-side <?php namespace ProcessWire; header('Content-Type: text/event-stream'); header('Cache-Control: no-cache'); $time = date('r'); echo "data: The server time is: {$time}\n\n"; flush(); client if(typeof(EventSource) !== "undefined") { var source = new EventSource("/some-processwire/url/"); source.onmessage = function(event) { document.getElementById("result").innerHTML += event.data + "<br>"; }; } else { document.getElementById("result").innerHTML = "Sorry, your browser does not support server-sent events..."; } I tested it and it works fine but was getting errors about wrong mime type if I set the headers later, but that is to be expected. Live Preview Refresh - Partial and Full - ajax - htmx I have this working nicely (will try post the demo later today). The premise is: A change happens. The editor client sends the data to the server to save the change (autosave - was just mimicking this part). Server saves and sends back a response including the name of the field that has been saved and the page ID back to the editor client. The editor client communicates with the preview client (a separate window). It sets the page ID and the name of the changed field name in its message to the preview window. The preview window client (htmx) receives the page id and the name of the field that has changed and sends a request to the server to fetch the rendered contents of that field for that page. The server sends back the html for htmx to swap in the DOM. In this simple communication, we have the field name match the html id of the element we are swapping. E.g. <h2 id='headline'>My Headline</h2> This is simple and straightforward and easily achieves partial refresh. However, it will be difficult to handle the more complex cases I mentioned in my previous post. It will also be somewhat dictating how the developer should name their html attributes. In addition, it still leaves the developer's templates with htmx attributes that will not be needed in production. This got me thinking. Live Preview Wrapper - idea #1 In this case, the live preview window would add a wrapper html element around the rendered template file markup. The wrapper html element would have the htmx attributes instead of the template file markup. This idea didn't go very far. It would inevitably lead to broken / incorrect markup in the preview window as the outermost markup of the template file would be <html> tags. In addition, while it solves the problem of unrequired htmx attributes in production, it doesn't solve the problem of selective rendering. It still wouldn't' know what parts of the markup in the preview window to swap. Live Preview Config - idea #2 What if instead of live preview / ProcessWire suggesting things like naming of elements, standard markup, etc. that are needed in a template file in order to get selective markup rendering, we implemented it the other way round? What if the developer instead, via config file(s), passed properties to live preview to implement the htmx attributes needed for a template file / page? The default would be full page refreshes. However, if live preview detected that a partial refresh config was available, it would implement those. This would offer the granularity needed for partial refreshes. If live preview did not receive / detect any configs, the default would kick in. Live preview would remain as agnostic as possible. It could be something like this (assumes use of htmx): A developer places their live preview partial refresh configs in /site/templates/live-previews/basic-page-live-preview.php (or basic-page-live-preview.json). ProcessWire detects that the template file basic-page has a partial refresh config in .../live-previews/basic-page-live-preview.php When ProcessWire loads the preview window, it injects the htmx attributes for each named element in the live preview configs in the corresponding elements in the preview window DOM. Injection is done by a ProcessWire live preview near-agnostic JavaScript script that only runs in the preview window. When the autosave happens in the editor window and the preview window gets informed, htmx kicks in, responding to an event and fires htmx.trigger(elem, event). The element in the markup with the corresponding event listener (hx-trigger='this-element-event-fired') will pick up the event fired by htmx.trigger() and query the server for the changed markup. The attributes set in #3 will ensure only the markup that has a change will fire. Server sends the response back and the element's hx-swap (or even hx-oob) picks it up and swaps the DOM. #6 is still not straightforward though. What will live preview / ProcessWire send back? Here too the config at #1 can help. E.g. it can tell ProcessWire to send <p>My Field Value</p> or just send the value of the field back, i.e. 'My Field Value'. The former is still complicated. Instead of telling ProcessWire I want <p>My Field Value</p>, how about tell it, via the configs at #1 that if requested field is field A, send me back the raw value (e.g. a plain text) but if the request is for field B, use this defined function in basic-page-live-preview.php to send me the rendered partial markup for field B? All ProcessWire would do is pass the value of field B to the said function. This value could be a Pageimages object. The function would iterate the images and send back the 'gallery' markup back to htmx to swap. example live preview config <?php namespace ProcessWire; $livePreviewConfigs = [ [ 'field' => 'headline', 'element_id' => 'my_headline_element_id', 'htmx-attributes' => ['hx-swap' => 'outerHTML', 'hx-trigger' => 'headlineevent'] ], [ 'field' => 'images', 'element_id' => 'gallery', 'handler' => 'renderGallery','htmx-attributes' => ['hx-swap' => 'innerHTML', 'hx-trigger' => 'galleryevent'] ], ]; function renderGallery(Pageimages $images) { // we only need the <li> since we are using innerHTML swap // the target element is <ul id='gallery'></ul> // -------------- $out = ""; foreach ($images as $image) { $thumb = $image->size(200, 200); $out .= "<li><img src='{$thumb->url}' alt='{$image->url}'></li>"; } // --------- // sent back to htmx to swap return $out; } When the preview window loads, ProcessWire sends $livePreviewConfigs to the browser, e.g. ProcessWire.config.LivePreview. The preview window JavaScript would consume it like this: client set htmx attributes @note: JavaScript written in the browser, not tested, could have errors. // get live preview configs const elementsConfigs = ProcessWire.config.LivePreview // loop through configs, find the corresponding element and set their htmx attributes for (const elementConfig of elementsConfigs) { const element = document.getElementById(elementConfig.element_id) if (element) { const htmxAttributes = elementConfig['htmx-attributes'] for (const [prop, value] of Object.entries(htmxAttributes)) { element.setAttribute(prop,value); } } } htmx-ready DOM changes via live preview injection (runtime only in live preview window): <h2 id="my_headline_element_id" hx-swap='outerHTML' hx-trigger='headlineevent' hx-vals='{"field":"headline"}'>My Headline</h2> <!-- gallery --> <ul id="gallery" hx-swap='innerHTML' hx-trigger='galleryevent' hx-vals='{"field":"images"}'> <li><img src='image-1-src' alt='image-1-alt'></li> <li><img src='image-2-src' alt='image-2-alt'></li> </ul> I have partly implement the above (just hard-coded configs for now) and it works nicely. Takeaways ProcessWire's live preview remains as agnostic as possible. Developer give instructions to live preview (via configs) and not the other way round. Live preview ships with minimal defaults, including full page refresh. It seems like a lot of work on the developer's part if they want partial refresh. However, this partly matches the complexity of their template files. Besides, some of the functionality (e.g. renderGallery()) could be something they already use in their template, hence reusable. Developer does not need to have additional markup in their template files in order to see live previews when editing. This means no cleaning up afterwards. Their templates remain clean. Live preview only alters the DOM (of the rendered template) at runtime. Developer can name their markup attributes as they wish. Developer can decide what needs live preview and what doesn't, even on the same page (e.g. hx-select). ProcessWire stays out of the developer's way, as usual.7 points
-
@kongondo Thanks, great ideas! Your SSE examples are eye opening. I think I understand a lot of this but not yet all of it, since I'm not deep in htmx yet, so I'll have to return to this once I do. But part of what you covered was the external interface to it that the web developer implements, and I think that's a good place to start, so I'll focus just on that. I understand the benefits of a separate config file, but am not sure how many would go to the effort of maintaining it for live preview. Maybe if PW auto-generates it somehow with functional defaults, it might help. Pros/cons aside, when we need something outside of the defaults, personally I'd rather declare this with markup classes or attributes in a manner similar to how we do for Markup Regions. Maybe I'm lazy, but it's already easy and familiar, and not much new to remember. Like with Markup Regions, ProcessWire can remove the attributes when they aren't needed, so no need for any output downsides. But if I had to declare it separately from the markup, I'd at least like to be able to do it in Setup > Fields > field rather than having to edit a .json file (and maybe this is a good fallback either way). I agree that it's good if the default behavior is to swap the entire <body> / refresh the window. But when we want to declare what gets updated with a live preview, here's an example of one way we could do it. If I wanted a particular section of markup to update automatically when the field "images" changed, I'd like if I could just do something simple like this, where div#gallery is an existing bit of markup a already there, like this: <div id='gallery'> // markup for an image gallery </div> …and I just add a class to that existing markup tag... <div id='gallery' class='pw-field-images'> // markup for an image gallery </div> ...or maybe a custom attribute instead: <div id='gallery' pw-field='images'> <!-- markup for images gallery --> </div> I'm not suggesting to add additional markup, but just an additional class or attribute to existing markup. If that same images field was also used elsewhere in the page, then I could just use that class (or attribute) again, and it would update too: <div id='sidebar-photos' class='pw-field-images'> <!-- markup for the sidebar photos --> </div> One thing I noticed in coding ProDrafts live preview is that field-level granularity often makes little difference in the end result. Replacing larger blocks of markup often is just as effective and visually identical. So I'd want to be able to say "if any of these fields change, update this…": <div id='content' pw-field='title,body,images,sidebar'> ... </div> For fields that already contain markup (like CKEditor), or fields that get rendered from their own /site/templates/fields/* template file, ProcessWire could automatically add the appropriate class by wrapping the value with it (this is similar to what ProDrafts and PageFrontEdit do): <div class='pw-field-body'> <!-- value of $page->body, div wrapper added automatically when live preview --> </div> This enables it to work automatically, without the developer doing anything at all. The only minor downside is that occasionally that can interfere with a CSS/JS selector, like if you are trying to target h1 + p:first-child, where the <p> is part of the CKEditor output but the <h1> isn't. Live preview still works, but the CSS/JS selector no longer matches when live preview is on. It's simple to work around though, and of course only affects output when someone is live previewing. Whether using a separate config file or not, if we use htmx, I think it should be a silent player and the developer wouldn't even need to know anything about how to use it. I'd like to avoid an external config file where I'm having to add 'htmx-attributes' arrays. I don't think we need the level of granularity or features in live preview that would demand the user know this kind of stuff. (Though fine for advanced users if/when needed). The more automatic it is, the more likely one is to use it. I think we'd also like the flexibility of not being dependent upon any particular library in case we ever decide to replace it with something custom. Side note, but one issue I also noticed in coding ProDrafts live preview is that when you update an element that has JS events on it that were added in document.ready, then they no longer work. For instance, an images gallery might have events that make thumbnails open in a lightbox or something. When the images gallery live updates, the lightbox no longer works unless the events were added to the whole document. Does htmx have some magic for this kind of situation?6 points
-
Maybe I've said it somewhere before but this is such a nice timesaver, which is also transportable through projects. Great addition. Thank you @Macrura!2 points
-
This is the new topic for the Settings Factory module (formerly known as Settings Train). Repo: https://github.com/outflux3/SettingsFactory I'm not sure what versions this is compatible with, it has only been tested on 3.x branch; it is not namespaced, and i'm not sure if namespacing is necessary or a benefit for this module; if any namespace or module gurus can weigh in on this, let me know. I'm also not sure if there needs to be a minimum php version; I have one live site using this now and it's working great; But before submitting to mods directory, would be better if there was some additional testing by other users.1 point
-
Hi! I am trying to build a new version (open source) of the recurring dates module released a few years ago. And I've found myself with a couple doubts. I am building it as a single value Fieldtype module where I want to save the rrule string (to render the UI) but I want to save the actual occurrences of the rule in a second table, so I can use mysql to query the actual dates. Does this make sense? Is there another field around that uses a secondary table to save data? If this is sensible, where should I do it?? sleepValue()? savePageField()? @adrian I think I saw you suffering with the bugs a lot around the support thread haha, so any input on how you would approach the development/refactors of this would be appreciated. Right now on the "big picture" I want to use Alpine.js to redo de Inputfield and looking for advice here on how to do the actual Fieldtype. Also it's the plan to completely rely on rlavin/php-rrule . And this module will only involve the fieldtype/inputfield not the whole rendering modules that came along the original one.1 point
-
Hello boys and girls, here's my newest: https://drinkbodegabay.com/ Bodega Bay is a Hard Seltzer drink that's delivered sustainably and contributes to projects to move climate solutions forward and help those less fortunate than ourselves. The website was built with my customary repeater based content blocks solution. The frontend is based on webcomponents built with StencilJS, and as always, it was a pleasure to build. An interesting detail was the processing we had to make on the locations listed in the Where to Buy section. Basically we were handed an Excel list of locations with irregular addresses that had to be geographically located on google maps. Well, maps doesn't really agree with doing that much processing at once, so we had to convert each location's address into coordinates. We built a script that imported everything to pages, then another one that made requests to grab the coordinates from google maps, store them and move on. As Maps defends itself against multiple requests, this script had to wait, pick out the locations that were already georeffed, and repeat a few times until all were processed. In the end, only a couple of locations had to be manually referenced, as Google wasn't able to find them from the way the address was formed. Another detail was the interstitial we have here. Being an alcoholic drink, this must have an age verification popup. It also needed a cookies popup and language selection. So the solution here was to combine all that in an interstitial (languages are disabled now until they translate everything), where the user is redirected on the first time to get that out of the way, but with user agent testing it lets scrapers pass. I'm a bit worried this might cause problems down the line, so we're keeping an eye on it and will reconsider this solution if need be. Hope you like it. I'll have an even better one to show real soon ?1 point
-
Hello, my Processwire family. Just minutes ago I flipped on the switch for my agency's new website. Check it out here: https://supertiny.agency/ This is a year of changes. I've finished my collab with a major studio, focused back on Supertiny, Liliana joined me for marketing, campaigns and strategy services, and over the last few months I've been working on the agency's website overhaul. Curiosities: Threejs based intro: After learning a few new tricks from the Awwwards winner Bruno Simon and his incredible course, I went oldschool and made an intro for this website. The intro runs once, the session remembers it's been played and turns to a discreet mode if users return to the homepage, allowing a replay. Using my Tiny Cookie Consent as a web component, I made an approach that I'll probably use a lot going forward. I have a text field in the CMS for all the scripts, and with some PHP find/replace that's undone on the client side, trackers and stats are being turned on only when the user allows it. As usual, repeaters and content blocks allow mix and match when building content. The design and overall vibe takes cues from 80's sci-fi. When I started designing this I was playing Control on my PS4 and overloaded with nostalgia, and attempted to convey some of that in this website. Now, there's a bit of an experiment here. All UX bibles are saying that it's a sin make an intro, lock scroll and whatnot. But this is a site users are probably only going to see once. So my approach here is more like a Yield sign. There's an intro, the scroll is locked, but you have a skip button. I'm basically trying to make the user slow down a bit and take in the concept, but let them through if they're really in a rush. Also, as a communication strategy, I'm placing my bets on case studies that will be promoted in social networks, and also new content on the Insights which will hopefully become more interesting with video content. Users coming from there will only see the intro if after seeing that content they get curious and have a lookaround. This is making sense in my head, but it's a bet. I'm curious to see how it performs and if I have to change the approach. Let me know what you think.1 point
-
I tried and wanted to build something similar a while back with just a click-through-path with some entry questions and possible solutions further down the path. Or a "funnel" if you will but the client decided no to establish such a guided "FAQ" like this because he couldn't provide the data that was needed. Which means the cases and questions or "stories". They didn't want to use something like Analytics or Matomo to track the most visited pages or sub-sections within their FAQ so guessing would have been the only way to go. In your case... if your client can tell, measure, analyse, collect and somehow get a bunch of those "stories" I'd say it's totally possible. In addition to that search queries from Google Search Console would be nice to what questions people had. If it's not something from the financial, health or similar niche it should be doable I guess. Only ever built a small prototype.1 point
-
This is a cool idea. And with a JS frontend onto the PW API it would still have a pretty chatesque UX. Plus using PW operators for the page selectors you could probably fuzzy match free-typed enquiries quite well too.1 point
-
I don't know much about datetime libraries, just know that tabulator.info recently replaced momentjs by luxon. http://tabulator.info/docs/5.0/release#dependencies https://github.com/you-dont-need/You-Dont-Need-Momentjs https://www.htmlgoodies.com/javascript/luxon-vs-moment-javascript/ Good luck with your new challenge ??1 point
-
1 point
-
I am redoing a Wordpress website in Processwire. Would like to try it out.1 point
-
@Ivan Gretsky That part I know I can handle. It's the front-end JS side of it that I don't know a lot about, yet. I mean this just to suggest the possibility of establishing some sort of guidelines for how a developer might implement selective rendering. I don't know what those guidelines would be, and just use that GET var as an example of how a request might indicate a change for a particular field (named "body"). Trying to communicate that in the simplest example possible. The developer would decide what that meant in terms of output that should be produced. But perhaps this example is too far from the context, I'm still learning how this SSE stuff works. PW can't generate partial markup for anything (outside of the admin at least), so it would be up to the site developer to implement whatever methodology we suggest for this, at least if they want higher performance live updates. From what I can tell, htmx can handle both cases... replacing partial markup, or selecting a part (hx-select) from a larger portion of markup, like the full document. @kongondo Yes you are right, it wouldn't be very practical. But the benefits of selective markup rendering may be enough that it's worthwhile for us to establish some guidelines for supporting selective rendering. Like that ?change=body example earlier, that's what I was trying to communicate there even if it's likely not a good example. The guidelines could be as simple as just letting the site developer know when they can skip over expensive things to render, like primary navigation or footer, etc. Something like if($config->livePreview) { // skip rendering unnecessary primary navigation } else { // render primary navigation } I was actually thinking of "body" as a PW field name, and the developer would decide what that means to the output in terms of what should be replaced. But you guys have a lot more experience with htmx than me so I think my example is likely flying a little blind, as my focus so far as only been on the auto-save part that identifies field changes. I need to start playing with htmx. ? The way that ProDrafts live preview works is by having the editor window communicate to the preview window and notify it when a field changes (JS based window-to-window communication). The preview window does pull a fresh/updated copy of the rendered page, but it attempts to only replace the markup that changed. It does this by having formatted values for fields like "body" include an extra <div> around them that identifies what it is, and then ProDrafts can replace just that element with JS. You can see things as you type in the editor, with a short delay, unless your page is very slow to render. This works well for formatted text fields (which is usually most of what one wants to see in live preview), but it does have to refresh the full document for other things where the site developer may be responsible for more runtime markup related to the field output. @monollonom ProDrafts only reloads the whole frame as a last resort. For most cases it can update just what changed. This is helpful for avoiding a very visible and distracting page refresh. Such page refreshes kind of kill the live-preview effect and can more easily draw your attention away from the content you are working on. I don't know if it'll be necessary to introduce a 3rd party dependency or not (htmx), but it does seem like a good place to start, even if we end up rolling our own later. That's more the domain of ProDrafts, and PagesSnapshots (another module in development). I think any kind of core autosave and live preview will focus on just those features (autosave and live updated view) and making them as good as they can be. It might be that one only wants to use these features for unpublished pages, but I think that's where it is most useful in the first place. If you wanted autosave/live preview on a published page, you could always clone it and then replace the original (or not) when you were done. This is essentially how PagesSnapshots manages drafts.1 point
-
I think I am scarred for life from my time battling that module. Thank you for taking this on - it will be a very welcome addition. A few initial thoughts: rlavin/php-rrule is very useful - I ended up using it to parse some info from the output of that module If you make use of momentjs, please ensure you use the version with timezone support - the lack of this was a huge part of the issues I was having and the module relied on an old version which made upgrading and switching to the timezone version quite difficult Please follow the rrule spec properly - the way it was implement did not follow the standard (I don't recall the exact problem, but a read through my comments should help you find it) so it resulted in errors when using php-rrule and other means of parsing it. Really focus on getting the API methods for querying events by date working properly and performantly - it was a nightmare in the other module. PS - I know I am coming across harshly regarding the old module. I don't want to sound unappreciative of the work that was put into it because recurring dates really are a difficult thing to do well and I have produced my own fair share of poor stuff and I am not saying I could have done any better. Best of luck with it and let me know if you have any other questions.1 point
-
I don't really do routing here. Still need to learn that. My use of Stencil is more based on the benefits in organization and workflow. It's super easy to bring a component from one project to the next, and it's easier to maintain when everything is neat and tidy. Using my boilerplate I basically start with a StencilJS + Storybook base, to develop individual components and demo them. If it's a super simple project, storybook already generates a static site nicely. See this one here: https://papoes.iniciativaliberal.pt/, where I just made the basic components and combined them in simple HTML pages. And from there, if we have an HTML page you know how easy it is to make it dynamic with PW ? My suggestion is just to try out the getting started on Stencil's page. It's really not that much of a stretch going from knowing HTML+CSS+JS to webcomponents. Once there, when you feel comfortable, look into the routing bit. You're right. Still plenty of room to improve that bit.1 point
-
Tabs 4 Life, brah! There is no more definitive authority than Richard Hendricks: (I've posted that before but could not resist) But seriously, the PW code style gets things so right. The braces style too. Some reasoned argument: https://github.com/php-fig-rectified/fig-rectified-standards/blob/master/Reasoning-Tabs-vs-Spaces.md https://github.com/php-fig-rectified/fig-rectified-standards/blob/master/Reasoning-Brace-Styles.md1 point
-
Under your template's Advanced tab, you can add your field names under "List of fields to display in the admin Page List" Update: There also this (not sure on 2.5 compatibility, if that's what you're using)1 point