How to DRY coffeescript - coffeescript

How would one adhere to DRY in this case (the number of accordions could be large):
$ ->
$("#accordion-1").accordion
autoHeight: false
navigation: true
$ ->
$("#accordion-2").accordion
autoHeight: false
navigation: true

It seems you can create a function which generate another function by a selector:
create = (selector) ->
$ ->
$(selector).accordion
autoHeight: false
navigation: true
then:
create "#accordion1"
create "#accordion2"

Don't know CoffeeScript, but in JS with jQuery, you could say like $("#accordion-1,#accordion-2").accordion(...) to affect both elements at once.
Or, you could apply a class to the accordions in your HTML and then say like $(".accordion").accordion(...). That seems to me the way least likely to cause future grief; you want an accordion, you just give it the appropriate class. You remove it from the HTML, it's already removed from the list of elements to work with without you having to edit a separate script.

The easiest/shortest solution:
$ ->
$("#accordion-1, #accordion-2").accordion
autoHeight: no
navigation: yes

Related

How to return a default Intent configuration for Widget

I want to return a
default INIntent for my widget. In the automatically generated file, there is an optional method of the INIntentHandlerProviding protocol that allows you to specify the default configuration for your widgets.
For me, it looks something like:
defaultIntentParameter(for intent: ConfigurationIntent) -> MyCustomINObject
I manage to make this work, but it only works once. I can't change this dynamically based on date or any other parameters.
Has anyone else had experience with this and managed to get around it?
/*! #abstract Default values for parameters with dynamic options
#discussion Called to query the parameter default value.
*/
#available(iOS 14.0, macOS 10.16, watchOS 7.0, *)
#objc(defaultChartTypeForConfiguration:)
optional func defaultChartType(for intent: ConfigurationIntent) -> Chart?
In your IntentHandler file, add the default function so will display default as well.
Change the Task to be the parameter from intent
TaskIntent is the name of the Type
class IntentHandler: INExtension, DynamicTaskIntentHandling {
func defaultTask(for _: DynamicTaskIntent) -> TaskIntent? {
TaskIntent(identifier: "default", display: "Test")
}

setting initial facet/refinement values when using instantsearch.js

I'm using instantsearch.js and a combination of widgets to display my search results (pretty much modeled exactly after the demos).
I need to set some initial values for facets so certain items are filtered on by default. How do I do this? I know the AlgoliaSearchHelper (helper) object has a method toggleRefinement that should allow me to do this but I can't seem to access this helper prior to calling search.start() which does the initial query.
Any advice or insight on how to set some default refinements would be appreciated. Thanks.
Update: This isn't a duplicate - my issue seems to have been with the instantsearch.widget.toggle. It looks like this widget sets default values behind the scenes before the initial search. I've adjusted my code to not use this widget and to just set the searchParameters.tagFilters property instead. It was the toggle widget throwing things off for me as I couldn't figure out how to override its default filtering.
The easiest way to add initial filters to your instantsearch.js instance is to use an extra custom widget:
var search = instantsearch({
appId: 'YourApplicationID',
apiKey: 'YourSearchOnlyAPIKey',
indexName: 'YourIndexName'
});
search.addWidget(
instantsearch.widgets.searchBox({
container: '#search-box',
placeholder: 'Search for FIXME...'
})
);
search.addWidget(
instantsearch.widgets.hits({
container: '#hits-container',
templates: {
item: 'Hit {{objectID}}: FIXME'
}
})
);
// setup initial filters
search.addWidget({
init: function(options) {
// use options.helper (https://github.com/algolia/algoliasearch-helper-js) to initialize the search with custom filters & parameters
options.helper.addFacetRefinement('MyFacet', 'my value');
}
});
search.start();
This is what worked for us:
search.addWidgets([{
init: function(options) {
options.helper.toggleRefinement('attribute', 'value');
}
}]);
My issue seems to have been with the instantsearch.widget.toggle. It looks like this widget sets default values behind the scenes before the initial search. I've adjusted my code to not use this widget and to just set the searchParameters.tagFilters property instead. It was the toggle widget throwing things off for me as I couldn't figure out how to override its default filtering.
You are indeed right, under the hood the toggle widget uses the off value if provided:
if (userValues.off === undefined) {
return;
}
// Add filtering on the 'off' value if set
let isRefined = state.isFacetRefined(attributeName, userValues.on);
if (!isRefined) {
helper.addFacetRefinement(attributeName, userValues.off);
}
To avoid this incomprehension from other users, there is now a PR on instantsearch.js with the following update:
Note that if you provide an "off" option, it will be refined at initialization.

Dragula Copy and removeOnSpill

I'm trying to use the Dragula Drag & Drop library to Clone elements into a target container AND allow the user to remove cloned elements from the Target Container by drag & dropping them outside of the target container (spilling).
Using the examples provided I have this:
dragula([$('left-copy-1tomany'), $('right-copy-1tomany')], {
copy: function (el, source) {
return source === $('left-copy-1tomany');
},
accepts: function (el, target) {
return target !== $('left-copy-1tomany');
}
});
dragula([$('right-copy-1tomany')], { removeOnSpill: true });
Which does not work - it seems that 'removeOnSpill' simply doesn't work if the container accepts a copy.
Does anybody know what I am not doing, doing wrong or if there is a work-around?
TIA!
I came here after looking for a while for a solution to a similar issue using the ng2-dragula for angular2.
dragulaService.setOptions('wallet-bag', {
removeOnSpill: (el: Element, source: Element): boolean => {
return source.id === 'wallet';
},
copySortSource: false,
copy: (el: Element, source: Element): boolean => {
return source.id !== 'wallet';
},
accepts: (el: Element, target: Element, source: Element, sibling: Element): boolean => {
return !el.contains(target) && target.id === 'wallet';
}
});
I've got 4 divs that can all drag into one which has the id of wallet
They are all part of the wallet-bag
using this code, they can all copy into the wallet, not copy between each other, and you can remove them from the wallet using the spill but not from the others.
I'm posting my solution as it may also help someone.
Ok, so the general answer I came trough is that:
you can have 'removeOnSpill' working - even with 'copy' option set to true - , only if you set the 'copy' option applying ONLY when the 'source' container IS NOT the one you are trying to remove elements from.
In my case I had 3 containers from which I can drag in another one called 'to_drop_to'.
Those container have all id starting with 'drag'.
So I set:
var containers = [document.querySelector('#drag1'),
document.querySelector('#drag2'),
document.querySelector('#drag3'),
document.querySelector('#to_drop_to')];
dragula(containers, {
accepts: function (el, target, source, sibling) {
return $(target).attr('id')=="gadget_drop"; // elements can be dropped only in 'to_drop_to' container
},
copy: function(el,source){
return $(source).attr('id').match('drag'); //elements are copied only if they are not already copied ones. That enables the 'removeOnSpill' to work
},
removeOnSpill: true
}
and this worked for me.
Hope it helps.
From the dragula documentation
options.removeOnSpill
By default, spilling an element outside of any containers will move
the element back to the drop position previewed by the feedback
shadow. Setting removeOnSpill to true will ensure elements dropped
outside of any approved containers are removed from the DOM. Note that
remove events won't fire if copy is set to true.

React Animations with VelocityJS

I'm having a really hard time getting animations to work in React. Perhaps there is something I'm fundamentally missing.
I'm doing this in coffeescript -- I hope you don't mind.
I've created a very simple UI. Theres a div with a title in it. When you click the title, the title is changed, and I want to animate a fade in/out transition using VelocityJS.
ReactTransitionGroup = React.createFactory(React.addons.CSSTransitionGroup)
{div} = React.DOM
TitleClass = React.createClass
displayName: "Title"
render: ->
(div {onClick:#props.changeTitle}, #props.title)
componentWillEnter: (done) ->
node = #getDOMNode()
console.log "willEnter"
Velocity(node, 'transition.fadeIn', {complete: done})
componentWillLeave: (done) ->
node = #getDOMNode()
console.log "willLeave"
Velocity(node, 'transition.fadeOut', {complete: done})
Title = React.createFactory(TitleClass)
MainClass = React.createClass
displayName: "Main"
getInitialState: ->
title: 'Main'
changeTitle: ->
if #state.title is 'Home'
#setState {title: 'Main'}
else
#setState {title: 'Home'}
render: ->
(div {style:{width: '100%', fontSize:'25px', textAlign:'center', marginTop:'20px'}},
(ReactTransitionGroup {transitionName: 'fade'},
(Title {changeTitle:#changeTitle, title:#state.title})
)
)
Main = React.createFactory(MainClass)
React.render(Main({}), document.body)
So thats it. Pretty self explanatory. This ReactTransitionGroup is still quite a mystery to me. It is my understanding that any of its children should get calls to componentWillEnter and componentWillLeave but that doesn't end up happening. According to the docs it seems that I should see the console.log "willEnter" but I don't.
I've been hung up on this for hours. Any ideas?
There are two problems I can see in your code above.
The first one is that you are using React.addons.CSSTransitionGroup instead of React.addons.TransitionGroup.
The second problem is that you are expecting componentWillEnter to get triggered on mount when in fact componentWillAppear is the method being triggered.
CSSTransitionGroup watches for actual uses of the CSS transition property written onto the elements. I'm not sure exactly how, but it sounds like Velocity isn't doing its work in a way that CSSTransitionGroup is twigging to. You may have to call componentWillEnter and componentWillLeave manually. In this situation, it doesn't sound like that'll be hard.
EDIT: oops, I missed that you don't have key attributes on your child components in the group. From what I can tell, componentWillEnter and componentWillLeave should get called, irrespective of anything else, if you get keys on those kids.
The solution ends up being to use React.addons.TransitionGroup. The CSSTransitionGroup is just a wrapper that does CSS stuff.
The other issue is that to get the animation callbacks, the children must have a key!
ReactTransitionGroup = React.createFactory(React.addons.TransitionGroup)
# {clip}
(ReactTransitionGroup {transitionName: 'fade'},
(Title {changeTitle:#changeTitle, title:#state.title, key:'title'})
)

Backbone.Marionette overriding methods

In 0.9.3, the region manager has changed with the following entry in the changelog:
BREAKING Changed the implementation of Region to allow easier
overriding of how the new view is added to the DOM
What is the best way of overriding the open method?
Currently I am doing the following which does work but I am curious to know what the recommended way is:
_.extend(Backbone.Marionette.Region.prototype, {
open: (view) ->
#$el.after(view.el)
})
This change has also broken some code for me because in some cases I was calling show like this:
region.show(documentsView, 'after')
And in others I was calling it like this:
region.show unitsView
How can I override open to take both these instances into account or do I need to override show?
This works:
_.extend(Backbone.Marionette.Region.prototype, {
show: (view, appendMethod) ->
#ensureEl()
#close()
view.render()
#open(view, appendMethod)
#currentView = view
open: (view, appendMethod) ->
appendMethod = appendMethod || "html"
#$el[appendMethod](view.el)
})