Browse Source
Merge pull request #706 from LasLabs/feature/10.0/SMD-263-mig-web_widget_slick
Merge pull request #706 from LasLabs/feature/10.0/SMD-263-mig-web_widget_slick
[ADD] web_widget_slick: Add modulepull/729/head
Dave Lasley
7 years ago
committed by
GitHub
26 changed files with 4292 additions and 0 deletions
-
114web_widget_slick/README.rst
-
3web_widget_slick/__init__.py
-
25web_widget_slick/__manifest__.py
-
BINweb_widget_slick/static/description/icon.png
-
79web_widget_slick/static/description/icon.svg
-
BINweb_widget_slick/static/lib/slick/ajax-loader.gif
-
172web_widget_slick/static/lib/slick/slick-theme.less
-
2996web_widget_slick/static/lib/slick/slick.js
-
104web_widget_slick/static/lib/slick/slick.less
-
172web_widget_slick/static/src/js/web_widget_slick.js
-
74web_widget_slick/static/src/less/slick.less
-
9web_widget_slick/static/src/xml/web_widget_slick.xml
-
225web_widget_slick/static/tests/js/web_widget_slick.js
-
36web_widget_slick/templates/assets.xml
-
4web_widget_slick/tests/__init__.py
-
19web_widget_slick/tests/test_ui.py
-
48web_widget_slick_example/README.rst
-
5web_widget_slick_example/__init__.py
-
25web_widget_slick_example/__manifest__.py
-
34web_widget_slick_example/demo/slick_example_data.xml
-
5web_widget_slick_example/models/__init__.py
-
16web_widget_slick_example/models/slick_example.py
-
2web_widget_slick_example/security/ir.model.access.csv
-
BINweb_widget_slick_example/static/description/icon.png
-
79web_widget_slick_example/static/description/icon.svg
-
46web_widget_slick_example/views/slick_example_view.xml
@ -0,0 +1,114 @@ |
|||
.. image:: https://img.shields.io/badge/license-LGPL--3-blue.svg |
|||
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html |
|||
:alt: License: LGPL-3 |
|||
|
|||
===================== |
|||
Slick Carousel Widget |
|||
===================== |
|||
|
|||
This module provides a Slick Carousel widget for use in the Odoo backend web interface. |
|||
|
|||
Usage |
|||
===== |
|||
|
|||
Default usage is on a One2many attachment field, as defined below:: |
|||
|
|||
class SlickExample(models.Model): |
|||
_name = 'slick.example' |
|||
_description = 'Slick Example Model' |
|||
image_ids = fields.One2many( |
|||
name='Images', |
|||
comodel_name='ir.attachment', |
|||
inverse_name='res_id', |
|||
) |
|||
|
|||
Assuming the above model, you would add a Slick Carousel on the |
|||
``image_ids`` column by using the following field definition in the |
|||
model's form view:: |
|||
|
|||
<field name="image_ids" widget="one2many_slick_images" options="{}"/> |
|||
|
|||
Options |
|||
------- |
|||
|
|||
The widget passes options directly through to Slick, so any `setting |
|||
available to Slick`_ is available to the widget. Additional options |
|||
specific to Odoo are: |
|||
|
|||
+-----------------+--------------+---------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ |
|||
| Name | Type | Default | Description | |
|||
+=================+==============+=====================+=============================================================================================================================================================================+ |
|||
| ``fieldName`` | ``String`` | ``datas`` | Field to lookup on relation table. Defaults to ``datas``, which is the data field used in ``ir.attachment`` table. This would be used to define a custom attachment model | |
|||
+-----------------+--------------+---------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ |
|||
| ``modelName`` | ``String`` | ``ir.attachment`` | Model of attachment relation. This would be used to define a custom attachment model instead of default ``ir.attachment`` | |
|||
+-----------------+--------------+---------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ |
|||
|
|||
.. _setting available to Slick: http://kenwheeler.github.io/slick/#settings |
|||
|
|||
Example Module |
|||
-------------- |
|||
|
|||
An example implementation, for instructional purposes as well as convenient |
|||
functional testing, is provided in the `web_widget_slick_example` module. |
|||
|
|||
* Install `web_widget_slick_example`. |
|||
* Activate Developer Mode. |
|||
* Go to Settings / Technical / Slick, and open the record to view the widget. |
|||
|
|||
To try out different Slick settings: |
|||
|
|||
* Go to Settings/User Interface/Views and search for 'slick.example.view.form'. |
|||
* Open the form view record. |
|||
* Click the Edit button. |
|||
* In the Architecture editor, find `options="{'slidesToShow': 2}`, and add |
|||
any desired settings (separated by commas) inside the curly braces. |
|||
* Save the changes and browse to the widget, as described above, to see the |
|||
widget with the new settings in effect. |
|||
|
|||
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas |
|||
:alt: Try me on Runbot |
|||
:target: https://runbot.odoo-community.org/runbot/162/10.0 |
|||
|
|||
Known issues / Roadmap |
|||
====================== |
|||
|
|||
* Adding / Deleting images from a carousel is not currently supported. |
|||
|
|||
Bug Tracker |
|||
=========== |
|||
|
|||
Bugs are tracked on `GitHub Issues |
|||
<https://github.com/OCA/web/issues>`_. In case of trouble, please |
|||
check there if your issue has already been reported. If you spotted it first, |
|||
help us smash it by providing detailed and welcomed feedback. |
|||
|
|||
Credits |
|||
======= |
|||
|
|||
Images |
|||
------ |
|||
|
|||
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_. |
|||
|
|||
Contributors |
|||
------------ |
|||
|
|||
* Dave Lasley <dave@laslabs.com> |
|||
* Brent Hughes <brent.hughes@laslabs.com> |
|||
|
|||
Do not contact contributors directly about support or help with technical issues. |
|||
|
|||
Maintainer |
|||
---------- |
|||
|
|||
.. image:: https://odoo-community.org/logo.png |
|||
:alt: Odoo Community Association |
|||
:target: https://odoo-community.org |
|||
|
|||
This module is maintained by the OCA. |
|||
|
|||
OCA, or the Odoo Community Association, is a nonprofit organization whose |
|||
mission is to support the collaborative development of Odoo features and |
|||
promote its widespread use. |
|||
|
|||
To contribute to this module, please visit https://odoo-community.org. |
@ -0,0 +1,3 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2016-2017 LasLabs Inc. |
|||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). |
@ -0,0 +1,25 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2016-2017 LasLabs Inc. |
|||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). |
|||
|
|||
{ |
|||
"name": "Slick Carousel Widget", |
|||
"summary": "Adds SlickJS slider widget for use as a carousel on Many2one" |
|||
" attachment fields in backend form views.", |
|||
"version": "10.0.1.0.0", |
|||
"category": "Web", |
|||
"website": "https://laslabs.com/", |
|||
"author": "LasLabs, Odoo Community Association (OCA)", |
|||
"license": "LGPL-3", |
|||
"application": False, |
|||
"installable": True, |
|||
"depends": [ |
|||
"web", |
|||
], |
|||
"data": [ |
|||
"templates/assets.xml", |
|||
], |
|||
"qweb": [ |
|||
"static/src/xml/web_widget_slick.xml", |
|||
], |
|||
} |
After Width: 128 | Height: 128 | Size: 9.2 KiB |
79
web_widget_slick/static/description/icon.svg
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
After Width: 32 | Height: 32 | Size: 4.1 KiB |
@ -0,0 +1,172 @@ |
|||
/* Copyright 2013-2016 Ken Wheeler |
|||
* Version 1.7.1 |
|||
* License MIT (https://opensource.org/licenses/MIT) */ |
|||
|
|||
@charset "UTF-8"; |
|||
|
|||
// Default Variables |
|||
|
|||
@slick-font-path: "./fonts/"; |
|||
@slick-font-family: "slick"; |
|||
@slick-loader-path: "./"; |
|||
@slick-arrow-color: white; |
|||
@slick-dot-color: black; |
|||
@slick-dot-color-active: @slick-dot-color; |
|||
@slick-prev-character: "←"; |
|||
@slick-next-character: "→"; |
|||
@slick-dot-character: "•"; |
|||
@slick-dot-size: 6px; |
|||
@slick-opacity-default: 0.75; |
|||
@slick-opacity-on-hover: 1; |
|||
@slick-opacity-not-active: 0.25; |
|||
|
|||
/* Slider */ |
|||
.slick-loading .slick-list{ |
|||
background: #fff url('@{slick-loader-path}ajax-loader.gif') center center no-repeat; |
|||
} |
|||
|
|||
/* Arrows */ |
|||
.slick-prev, |
|||
.slick-next { |
|||
position: absolute; |
|||
display: block; |
|||
height: 20px; |
|||
width: 20px; |
|||
line-height: 0px; |
|||
font-size: 0px; |
|||
cursor: pointer; |
|||
background: transparent; |
|||
color: transparent; |
|||
top: 50%; |
|||
-webkit-transform: translate(0, -50%); |
|||
-ms-transform: translate(0, -50%); |
|||
transform: translate(0, -50%); |
|||
padding: 0; |
|||
border: none; |
|||
outline: none; |
|||
&:hover, &:focus { |
|||
outline: none; |
|||
background: transparent; |
|||
color: transparent; |
|||
&:before { |
|||
opacity: @slick-opacity-on-hover; |
|||
} |
|||
} |
|||
&.slick-disabled:before { |
|||
opacity: @slick-opacity-not-active; |
|||
} |
|||
} |
|||
|
|||
.slick-prev:before, .slick-next:before { |
|||
font-family: @slick-font-family; |
|||
font-size: 20px; |
|||
line-height: 1; |
|||
color: @slick-arrow-color; |
|||
opacity: @slick-opacity-default; |
|||
-webkit-font-smoothing: antialiased; |
|||
-moz-osx-font-smoothing: grayscale; |
|||
|
|||
& when ( @slick-font-family = 'slick' ) { |
|||
/* Icons */ |
|||
@font-face { |
|||
font-family: 'slick'; |
|||
font-weight: normal; |
|||
font-style: normal; |
|||
src: url('@{slick-font-path}slick.eot'); |
|||
src: url('@{slick-font-path}slick.eot?#iefix') format('embedded-opentype'), url('@{slick-font-path}slick.woff') format('woff'), url('@{slick-font-path}slick.ttf') format('truetype'), url('@{slick-font-path}slick.svg#slick') format('svg'); |
|||
} |
|||
} |
|||
} |
|||
|
|||
.slick-prev { |
|||
left: -25px; |
|||
[dir="rtl"] & { |
|||
left: auto; |
|||
right: -25px; |
|||
} |
|||
&:before { |
|||
content: @slick-prev-character; |
|||
[dir="rtl"] & { |
|||
content: @slick-next-character; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.slick-next { |
|||
right: -25px; |
|||
[dir="rtl"] & { |
|||
left: -25px; |
|||
right: auto; |
|||
} |
|||
&:before { |
|||
content: @slick-next-character; |
|||
[dir="rtl"] & { |
|||
content: @slick-prev-character; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Dots */ |
|||
|
|||
.slick-dotted .slick-slider { |
|||
margin-bottom: 30px; |
|||
} |
|||
|
|||
.slick-dots { |
|||
position: absolute; |
|||
bottom: -25px; |
|||
list-style: none; |
|||
display: block; |
|||
text-align: center; |
|||
padding: 0; |
|||
margin: 0; |
|||
width: 100%; |
|||
li { |
|||
position: relative; |
|||
display: inline-block; |
|||
height: 20px; |
|||
width: 20px; |
|||
margin: 0 5px; |
|||
padding: 0; |
|||
cursor: pointer; |
|||
button { |
|||
border: 0; |
|||
background: transparent; |
|||
display: block; |
|||
height: 20px; |
|||
width: 20px; |
|||
outline: none; |
|||
line-height: 0px; |
|||
font-size: 0px; |
|||
color: transparent; |
|||
padding: 5px; |
|||
cursor: pointer; |
|||
&:hover, &:focus { |
|||
outline: none; |
|||
&:before { |
|||
opacity: @slick-opacity-on-hover; |
|||
} |
|||
} |
|||
&:before { |
|||
position: absolute; |
|||
top: 0; |
|||
left: 0; |
|||
content: @slick-dot-character; |
|||
width: 20px; |
|||
height: 20px; |
|||
font-family: @slick-font-family; |
|||
font-size: @slick-dot-size; |
|||
line-height: 20px; |
|||
text-align: center; |
|||
color: @slick-dot-color; |
|||
opacity: @slick-opacity-not-active; |
|||
-webkit-font-smoothing: antialiased; |
|||
-moz-osx-font-smoothing: grayscale; |
|||
} |
|||
} |
|||
&.slick-active button:before { |
|||
color: @slick-dot-color-active; |
|||
opacity: @slick-opacity-default; |
|||
} |
|||
} |
|||
} |
2996
web_widget_slick/static/lib/slick/slick.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,104 @@ |
|||
/* Copyright 2013-2016 Ken Wheeler |
|||
* Version 1.7.1 |
|||
* License MIT (https://opensource.org/licenses/MIT) */ |
|||
|
|||
/* Slider */ |
|||
|
|||
.slick-slider { |
|||
position: relative; |
|||
display: block; |
|||
box-sizing: border-box; |
|||
-webkit-touch-callout: none; |
|||
-webkit-user-select: none; |
|||
-khtml-user-select: none; |
|||
-moz-user-select: none; |
|||
-ms-user-select: none; |
|||
user-select: none; |
|||
-ms-touch-action: pan-y; |
|||
touch-action: pan-y; |
|||
-webkit-tap-highlight-color: transparent; |
|||
} |
|||
.slick-list { |
|||
position: relative; |
|||
overflow: hidden; |
|||
display: block; |
|||
margin: 0; |
|||
padding: 0; |
|||
|
|||
&:focus { |
|||
outline: none; |
|||
} |
|||
|
|||
&.dragging { |
|||
cursor: pointer; |
|||
cursor: hand; |
|||
} |
|||
} |
|||
.slick-slider .slick-track, |
|||
.slick-slider .slick-list { |
|||
-webkit-transform: translate3d(0, 0, 0); |
|||
-moz-transform: translate3d(0, 0, 0); |
|||
-ms-transform: translate3d(0, 0, 0); |
|||
-o-transform: translate3d(0, 0, 0); |
|||
transform: translate3d(0, 0, 0); |
|||
} |
|||
|
|||
.slick-track { |
|||
position: relative; |
|||
left: 0; |
|||
top: 0; |
|||
display: block; |
|||
margin-left: auto; |
|||
margin-right: auto; |
|||
|
|||
&:before, |
|||
&:after { |
|||
content: ""; |
|||
display: table; |
|||
} |
|||
|
|||
&:after { |
|||
clear: both; |
|||
} |
|||
|
|||
.slick-loading & { |
|||
visibility: hidden; |
|||
} |
|||
} |
|||
.slick-slide { |
|||
float: left; |
|||
height: 100%; |
|||
min-height: 1px; |
|||
[dir="rtl"] & { |
|||
float: right; |
|||
} |
|||
img { |
|||
display: block; |
|||
} |
|||
&.slick-loading img { |
|||
display: none; |
|||
} |
|||
|
|||
display: none; |
|||
|
|||
&.dragging img { |
|||
pointer-events: none; |
|||
} |
|||
|
|||
.slick-initialized & { |
|||
display: block; |
|||
} |
|||
|
|||
.slick-loading & { |
|||
visibility: hidden; |
|||
} |
|||
|
|||
.slick-vertical & { |
|||
display: block; |
|||
height: auto; |
|||
border: 1px solid transparent; |
|||
} |
|||
} |
|||
.slick-arrow.slick-hidden { |
|||
display: none; |
|||
} |
@ -0,0 +1,172 @@ |
|||
/* Copyright 2016-2017 LasLabs Inc. |
|||
* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). */
|
|||
|
|||
odoo.define('web_widget_slick', function(require) { |
|||
"use strict"; |
|||
|
|||
var core = require('web.core'); |
|||
var AbstractManyField = require('web.form_relational').AbstractManyField; |
|||
|
|||
var FieldSlickImages = AbstractManyField.extend({ |
|||
|
|||
widget_class: 'o_slick', |
|||
template: 'FieldSlickImages', |
|||
$slick: null, |
|||
no_rerender: true, |
|||
loading: [], |
|||
loaded: 0, |
|||
|
|||
events: { |
|||
'mousedown img': function(ev) { |
|||
ev.preventDefault(); |
|||
}, |
|||
'touchstart img': function(ev) { |
|||
ev.preventDefault(); |
|||
}, |
|||
// Triggering a resize on the lazyLoaded event prevents the carousel
|
|||
// from appearing empty when page loads
|
|||
'lazyLoaded': function(ev) { |
|||
$(ev.target).trigger('resize'); |
|||
} |
|||
}, |
|||
|
|||
defaults: { |
|||
lazyLoad: 'ondemand', |
|||
fieldName: 'datas', |
|||
modelName: 'ir.attachment', |
|||
slidesToShow: 3, |
|||
slidesToScroll: 1, |
|||
swipeToSlide: true, |
|||
dots: true, |
|||
infinite: true, |
|||
speed: 500, |
|||
arrows: true, |
|||
responsive: [ |
|||
{ |
|||
breakpoint: 1024, |
|||
settings: { |
|||
slidesToShow: 2, |
|||
slidesToScroll: 1 |
|||
} |
|||
}, |
|||
{ |
|||
breakpoint: 600, |
|||
settings: { |
|||
slidesToShow: 1, |
|||
slidesToScroll: 1 |
|||
} |
|||
} |
|||
// You can unslick at a given breakpoint now by adding:
|
|||
// settings: "unslick"
|
|||
// instead of a settings object
|
|||
] |
|||
}, |
|||
|
|||
init: function(field_manager, node) { |
|||
this._super(field_manager, node); |
|||
this.options = _.defaults(this.options, this.defaults); |
|||
}, |
|||
|
|||
destroy_content: function() { |
|||
if (this.$slick) { |
|||
var $imgs = this.$el.find('img'); |
|||
// Unslicking removes the carousel but re-appends any images,
|
|||
// so removal of images is also required
|
|||
$imgs.each($.proxy(this._slickRemove, this)); |
|||
this.$slick.slick('unslick'); |
|||
} |
|||
}, |
|||
|
|||
render_value: function() { |
|||
this._super(); |
|||
this.destroy_content(); |
|||
|
|||
this.$el.parent('td').addClass('o_slick_cell'); |
|||
this.$slick = $('<div class="slick-container"></div>'); |
|||
if (this.options.arrows) { |
|||
this.$slick.addClass('slick-arrowed'); |
|||
} |
|||
this.$el.append(this.$slick); |
|||
|
|||
var baseUrl = '/web/image/' + this.options.modelName + '/'; |
|||
var value = this.get('value'); |
|||
this.loading.push.apply(value); |
|||
_.each(value, $.proxy(this._slickRender, this, [baseUrl])); |
|||
|
|||
this.$slick.slick(this.options); |
|||
core.bus.on('resize', this, this._resizeCarousel); |
|||
}, |
|||
|
|||
_resizeCarousel: function () { |
|||
var maxWidth = this._resizeMaxWidth(); |
|||
var containerWidth = maxWidth; |
|||
|
|||
var $parentCell = this.$el.parent('td'); |
|||
if ($parentCell.length) { |
|||
var scaledWidth = this._resizeScaledWidth($parentCell, maxWidth); |
|||
var labelWidth = this._resizeLabelWidth($parentCell); |
|||
containerWidth = scaledWidth - labelWidth; |
|||
} |
|||
|
|||
var marginWidth = this._resizeMarginWidth(this.$slick); |
|||
var carouselWidth = containerWidth - marginWidth; |
|||
|
|||
// Set outerWidth of carousel, with minimum size. Minimum size can cause
|
|||
// overflow in some cases but prevents displaying with zero width
|
|||
this.$slick.outerWidth(Math.max(carouselWidth, 150)); |
|||
}, |
|||
|
|||
_resizeLabelWidth: function ($parentCell) { |
|||
// If the widget has a label, subtract label cell's width, plus the extra
|
|||
// padding applied to the parent cell, from container width
|
|||
var $labelCell = $parentCell.prev('.o_td_label'); |
|||
if ($labelCell.length) { |
|||
var parentPadding = $parentCell.outerWidth() - $parentCell.width(); |
|||
return $labelCell.outerWidth() + parentPadding; |
|||
} |
|||
|
|||
return 0; |
|||
}, |
|||
|
|||
_resizeMarginWidth: function ($element) { |
|||
// Subtract container's margins so outerWidth can be set properly
|
|||
return $element.outerWidth(true) - $element.outerWidth(); |
|||
}, |
|||
|
|||
_resizeMaxWidth: function () { |
|||
// Determine the maximum possible width the widget container can occupy
|
|||
var parentSelectors = ['.o_form_sheet', '.o_form_nosheet']; |
|||
var containerWidth = parentSelectors.map(function (selector) { |
|||
return this.$el.closest(selector).width(); |
|||
}, this).filter(function (width) { |
|||
return width !== null; |
|||
})[0]; |
|||
|
|||
return containerWidth; |
|||
}, |
|||
|
|||
_resizeScaledWidth: function ($parentCell, maxWidth) { |
|||
// If the widget is inside a group tag, scale carousel size based on
|
|||
// intended % width of parent cell
|
|||
return maxWidth * parseInt($parentCell[0].style.width, 10) / 100; |
|||
}, |
|||
|
|||
_slickRemove: function (idx, val) { |
|||
this.$slick.slick('slickRemove', idx); |
|||
}, |
|||
|
|||
_slickRender: function (baseUrl, id) { |
|||
var $img = $('<img class="img img-responsive"></img>'); |
|||
var $div = $('<div></div>'); |
|||
$img.attr('data-lazy', baseUrl + id + '/' + this.options.fieldName); |
|||
$div.append($img); |
|||
this.$slick.append($div); |
|||
} |
|||
|
|||
}); |
|||
|
|||
core.form_widget_registry.add("one2many_slick_images", FieldSlickImages); |
|||
|
|||
return {FieldSlickImages: FieldSlickImages}; |
|||
|
|||
}); |
@ -0,0 +1,74 @@ |
|||
/* Copyright 2016-2017 LasLabs Inc. |
|||
* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). */ |
|||
|
|||
@slick-arrow-margin: 40px; |
|||
@slick-arrow-offset: calc(-(@slick-arrow-margin - 5px)); |
|||
@slick-font-family: "FontAwesome"; |
|||
@slick-dot-character: "\f10c"; |
|||
@slick-dot-character-active: "\f111"; |
|||
@slick-dot-color: @odoo-brand-primary; |
|||
@slick-dot-offset: 35px; |
|||
@slick-dot-size: 12px; |
|||
@slick-next-character: "\f054"; |
|||
@slick-prev-character: "\f053"; |
|||
@slick-opacity-default: 1; |
|||
@slick-opacity-not-active: 0.5; |
|||
|
|||
/* Odoo field */ |
|||
|
|||
.o_slick { |
|||
display: block !important; |
|||
} |
|||
|
|||
/* Container */ |
|||
|
|||
.slick-container { |
|||
&.slick-dotted { |
|||
margin-bottom: @slick-dot-offset; |
|||
} |
|||
&.slick-arrowed { |
|||
margin-left: @slick-arrow-margin; |
|||
margin-right: @slick-arrow-margin; |
|||
.slick-prev { |
|||
left: @slick-arrow-offset; |
|||
} |
|||
.slick-next { |
|||
right: @slick-arrow-offset; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Images */ |
|||
|
|||
.slick-slide { |
|||
margin-left: 2px; |
|||
margin-right: 2px; |
|||
outline: none; |
|||
img { |
|||
margin-left: auto; |
|||
margin-right: auto; |
|||
} |
|||
} |
|||
|
|||
/* Arrows */ |
|||
|
|||
.slick-arrow { |
|||
height: 35px; |
|||
width: 30px; |
|||
font-size: 0 !important; |
|||
padding: 0 !important; |
|||
.btn(); |
|||
.btn-sm(); |
|||
.btn-primary(); |
|||
} |
|||
|
|||
/* Dots */ |
|||
|
|||
.slick-dots { |
|||
bottom: calc(-(@slick-dot-offset)); |
|||
li { |
|||
&.slick-active button:before { |
|||
content: @slick-dot-character-active; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,9 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<!-- Copyright 2016-2017 LasLabs Inc. |
|||
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). --> |
|||
|
|||
<template> |
|||
<t t-name="FieldSlickImages"> |
|||
<div t-attf-class="o_form_field {{ widget.widget_class }}"></div> |
|||
</t> |
|||
</template> |
@ -0,0 +1,225 @@ |
|||
/* Copyright 2017 LasLabs Inc. |
|||
* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). */
|
|||
|
|||
odoo.define_section('web_widget_slick', ['web.core', 'web.form_common'], function(test) { |
|||
"use strict"; |
|||
|
|||
function appendWidget (core, formCommon, $fix) { |
|||
var fieldManager = new formCommon.DefaultFieldManager(null, {}); |
|||
var node = {'attrs': {}}; |
|||
var FieldSlickImages = core.form_widget_registry.get('one2many_slick_images'); |
|||
var widget = new FieldSlickImages(fieldManager, node); |
|||
widget.appendTo($fix); |
|||
return widget; |
|||
} |
|||
|
|||
function imageUrl (modelName, fieldName, id) { |
|||
return '/web/image/' + modelName + '/' + id + '/' + fieldName; |
|||
} |
|||
|
|||
test('It should add a slick widget', |
|||
function(assert, core, formCommon) { |
|||
var $fix = $('#qunit-fixture'); |
|||
var widget = appendWidget(core, formCommon, $fix); |
|||
|
|||
var slickContainerCount = $fix.find('.slick-container').length; |
|||
assert.strictEqual(slickContainerCount, 1); |
|||
} |
|||
); |
|||
|
|||
test('.init() should add defaults to options', |
|||
function(assert, core, formCommon) { |
|||
var $fix = $('#qunit-fixture'); |
|||
var fieldManager = new formCommon.DefaultFieldManager(null, {}); |
|||
var node = {'attrs': {}}; |
|||
var FieldSlickImages = core.form_widget_registry.get('one2many_slick_images'); |
|||
var widget = new FieldSlickImages(fieldManager, node); |
|||
widget.appendTo($fix); |
|||
|
|||
widget.defaults.testing = 'tested'; |
|||
widget.init(fieldManager, node); |
|||
assert.strictEqual(widget.options.testing, 'tested'); |
|||
} |
|||
); |
|||
|
|||
test('.destroy_content() should remove images', |
|||
function(assert, core, formCommon) { |
|||
var $fix = $('#qunit-fixture'); |
|||
var widget = appendWidget(core, formCommon, $fix); |
|||
|
|||
var $slickImage = $('<div><img></div>'); |
|||
widget.$slick.slick('slickAdd', $slickImage); |
|||
widget.destroy_content(); |
|||
|
|||
var slickImageCount = widget.$slick.find('img').length; |
|||
assert.strictEqual(slickImageCount, 0); |
|||
} |
|||
); |
|||
|
|||
test('.destroy_content() should remove carousel', |
|||
function (assert, core, formCommon) { |
|||
var $fix = $('#qunit-fixture'); |
|||
var widget = appendWidget(core, formCommon, $fix); |
|||
|
|||
widget.destroy_content(); |
|||
|
|||
var slickChildren = widget.$slick.children().length; |
|||
assert.strictEqual(slickChildren, 0); |
|||
} |
|||
); |
|||
|
|||
test('.render_value() should add images corresponding to field value', |
|||
function(assert, core, formCommon) { |
|||
var $fix = $('#qunit-fixture'); |
|||
var widget = appendWidget(core, formCommon, $fix); |
|||
|
|||
var fieldValues = [1, 2]; |
|||
widget.set({'value': fieldValues}); |
|||
widget.render_value(); |
|||
|
|||
var slickImages = widget.$slick.find('img'); |
|||
var slickImageUrls = slickImages.map(function() { |
|||
return $(this).data('lazy'); |
|||
}).get(); |
|||
|
|||
var modelName = widget.options.modelName; |
|||
var fieldName = widget.options.fieldName; |
|||
var expectedUrls = fieldValues.map(function(id) { |
|||
return '/web/image/' + modelName + '/' + id + '/' + fieldName; |
|||
}); |
|||
|
|||
assert.deepEqual(slickImageUrls, expectedUrls); |
|||
} |
|||
); |
|||
|
|||
test('._resizeCarousel() should resize the widget', |
|||
function (assert, core, formCommon) { |
|||
var $fix = $('#qunit-fixture'); |
|||
var $nosheet = $('<div class="o_form_nosheet"></div>'); |
|||
$fix.append($nosheet); |
|||
|
|||
var widget = appendWidget(core, formCommon, $nosheet); |
|||
var setWidth = 50; |
|||
widget.$slick.outerWidth(setWidth); |
|||
|
|||
widget._resizeCarousel(); |
|||
|
|||
assert.notStrictEqual(widget.$slick.outerWidth(), setWidth); |
|||
} |
|||
); |
|||
|
|||
test('._resizeCarousel() should be called when container is resized', |
|||
function (assert, core, formCommon) { |
|||
var $fix = $('#qunit-fixture'); |
|||
var $nosheet = $('<div class="o_form_nosheet"></div>'); |
|||
$fix.append($nosheet); |
|||
|
|||
var widget = appendWidget(core, formCommon, $nosheet); |
|||
var setWidth = 50; |
|||
widget.$slick.outerWidth(setWidth); |
|||
|
|||
core.bus.trigger('resize'); |
|||
|
|||
assert.notStrictEqual(widget.$slick.outerWidth(), setWidth); |
|||
} |
|||
); |
|||
|
|||
test('._resizeLabelWidth() should return the width of the preceding ' + |
|||
'sibling label cell if it exists', |
|||
function (assert, core, formCommon) { |
|||
var $fix = $('#qunit-fixture'); |
|||
var widget = appendWidget(core, formCommon, $fix); |
|||
|
|||
var width = 100; |
|||
var $cell = $('<td style="width:10px;"></td>'); |
|||
var $labelCell = $('<td class="o_td_label"></td>'); |
|||
$labelCell.outerWidth(width); |
|||
|
|||
widget.$slick.append($labelCell); |
|||
widget.$slick.append($cell); |
|||
|
|||
assert.strictEqual(widget._resizeLabelWidth($cell), width); |
|||
} |
|||
); |
|||
|
|||
test('._resizeLabelWidth() should return 0 if the previous sibling cell ' + |
|||
' of the provided element is not a label cell', |
|||
function (assert, core, formCommon) { |
|||
var $fix = $('#qunit-fixture'); |
|||
var widget = appendWidget(core, formCommon, $fix); |
|||
|
|||
var width = '100px'; |
|||
var $cell = $('<td></td>'); |
|||
widget.$slick.append($('<td style="width:' + width + ';"></td>')); |
|||
widget.$slick.append($cell); |
|||
|
|||
assert.strictEqual(widget._resizeLabelWidth($cell), 0); |
|||
} |
|||
); |
|||
|
|||
test('._resizeMarginWidth() should return the total left and right ' + |
|||
' margins of the provided element', |
|||
function (assert, core, formCommon) { |
|||
var $fix = $('#qunit-fixture'); |
|||
var widget = appendWidget(core, formCommon, $fix); |
|||
|
|||
var elementStyle = 'margin-left: 12px; margin-right: 7px;'; |
|||
var marginTotal = 19; |
|||
var $element = $('<div style="' + elementStyle + '"></div>'); |
|||
widget.$slick.append($element); |
|||
|
|||
assert.strictEqual(widget._resizeMarginWidth($element), marginTotal); |
|||
} |
|||
); |
|||
|
|||
test('._resizeMaxWidth() should return the width of the closest sheet element', |
|||
function (assert, core, formCommon) { |
|||
var $fix = $('#qunit-fixture'); |
|||
var $sheet1 = $('<div class="o_form_sheet"></div>'); |
|||
var $sheet2 = $('<div class="o_form_sheet"></div>'); |
|||
var expectedWidth = 266; |
|||
|
|||
$sheet1.width(700); |
|||
$sheet2.width(expectedWidth); |
|||
$sheet1.append($sheet2); |
|||
$fix.append($sheet1); |
|||
|
|||
var widget = appendWidget(core, formCommon, $sheet2); |
|||
|
|||
assert.strictEqual(widget._resizeMaxWidth(), expectedWidth); |
|||
} |
|||
); |
|||
|
|||
test('._resizeMaxWidth() should return the width of the closest nosheet element', |
|||
function (assert, core, formCommon) { |
|||
var $fix = $('#qunit-fixture'); |
|||
var $nosheet1 = $('<div class="o_form_nosheet"></div>'); |
|||
var $nosheet2 = $('<div class="o_form_nosheet"></div>'); |
|||
var expectedWidth = 266; |
|||
|
|||
$nosheet1.width(700); |
|||
$nosheet2.width(expectedWidth); |
|||
$nosheet1.append($nosheet2); |
|||
$fix.append($nosheet1); |
|||
|
|||
var widget = appendWidget(core, formCommon, $nosheet2); |
|||
|
|||
assert.strictEqual(widget._resizeMaxWidth(), expectedWidth); |
|||
} |
|||
); |
|||
|
|||
test('._resizeScaledWidth() should return the provided integer, scaled' + |
|||
'to the % width in the provided element style attribute', |
|||
function (assert, core, formCommon) { |
|||
var $fix = $('#qunit-fixture'); |
|||
var widget = appendWidget(core, formCommon, $fix); |
|||
|
|||
var givenWidth = 100; |
|||
var widthPercent = 54; |
|||
var expectedWidth = widthPercent; |
|||
var $cell = $('<td style="width:' + widthPercent + '%;"></td>'); |
|||
|
|||
assert.strictEqual(widget._resizeScaledWidth($cell, givenWidth), expectedWidth); |
|||
} |
|||
); |
|||
}); |
@ -0,0 +1,36 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<!-- Copyright 2016-2017 LasLabs Inc. |
|||
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). --> |
|||
|
|||
<odoo> |
|||
<template id="assets_slick" inherit_id="web.assets_backend"> |
|||
<xpath expr="//script[last()]" position="after"> |
|||
<link rel="stylesheet" |
|||
type="text/less" |
|||
href="/web_widget_slick/static/lib/slick/slick.less" |
|||
/> |
|||
<link rel="stylesheet" |
|||
type="text/less" |
|||
href="/web_widget_slick/static/lib/slick/slick-theme.less" |
|||
/> |
|||
<link rel="stylesheet" |
|||
type="text/less" |
|||
href="/web_widget_slick/static/src/less/slick.less" |
|||
/> |
|||
<script type="application/javascript" |
|||
src="/web_widget_slick/static/lib/slick/slick.js" |
|||
/> |
|||
<script type="application/javascript" |
|||
src="/web_widget_slick/static/src/js/web_widget_slick.js" |
|||
/> |
|||
</xpath> |
|||
</template> |
|||
|
|||
<template id="qunit_suite" inherit_id="web.qunit_suite"> |
|||
<xpath expr="//t[@t-set='head']" position="inside"> |
|||
<script type="application/javascript" |
|||
src="/web_widget_slick/static/tests/js/web_widget_slick.js" |
|||
/> |
|||
</xpath> |
|||
</template> |
|||
</odoo> |
@ -0,0 +1,4 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
|||
|
|||
from . import test_ui |
@ -0,0 +1,19 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2017 LasLabs Inc. |
|||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). |
|||
|
|||
from odoo.tests.common import HttpCase |
|||
|
|||
|
|||
class UICase(HttpCase): |
|||
|
|||
post_install = True |
|||
at_install = False |
|||
|
|||
def test_ui_web(self): |
|||
"""Test backend tests.""" |
|||
self.phantom_js( |
|||
"/web/tests?debug=assets&module=web_widget_slick", |
|||
"", |
|||
login="admin", |
|||
) |
@ -0,0 +1,48 @@ |
|||
.. image:: https://img.shields.io/badge/license-LGPL--3-blue.svg |
|||
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html |
|||
:alt: License: LGPL-3 |
|||
|
|||
============================= |
|||
Slick Carousel Widget Example |
|||
============================= |
|||
|
|||
This module provides an example of how to implement the web_widget_slick module. |
|||
|
|||
Bug Tracker |
|||
=========== |
|||
|
|||
Bugs are tracked on `GitHub Issues |
|||
<https://github.com/OCA/web/issues>`_. In case of trouble, please |
|||
check there if your issue has already been reported. If you spotted it first, |
|||
help us smash it by providing detailed and welcomed feedback. |
|||
|
|||
Credits |
|||
======= |
|||
|
|||
Images |
|||
------ |
|||
|
|||
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_. |
|||
|
|||
Contributors |
|||
------------ |
|||
|
|||
* Dave Lasley <dave@laslabs.com> |
|||
* Brent Hughes <brent.hughes@laslabs.com> |
|||
|
|||
Do not contact contributors directly about support or help with technical issues. |
|||
|
|||
Maintainer |
|||
---------- |
|||
|
|||
.. image:: https://odoo-community.org/logo.png |
|||
:alt: Odoo Community Association |
|||
:target: https://odoo-community.org |
|||
|
|||
This module is maintained by the OCA. |
|||
|
|||
OCA, or the Odoo Community Association, is a nonprofit organization whose |
|||
mission is to support the collaborative development of Odoo features and |
|||
promote its widespread use. |
|||
|
|||
To contribute to this module, please visit https://odoo-community.org. |
@ -0,0 +1,5 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2016-2017 LasLabs Inc. |
|||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). |
|||
|
|||
from . import models |
@ -0,0 +1,25 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2016-2017 LasLabs Inc. |
|||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). |
|||
|
|||
{ |
|||
"name": "Slick Carousel Widget Example", |
|||
"summary": "Example usage of the web_widget_slick module", |
|||
"version": "10.0.1.0.0", |
|||
"category": "Hidden", |
|||
"website": "https://laslabs.com/", |
|||
"author": "LasLabs, Odoo Community Association (OCA)", |
|||
"license": "LGPL-3", |
|||
"application": False, |
|||
"installable": True, |
|||
"depends": [ |
|||
"web_widget_slick", |
|||
], |
|||
"data": [ |
|||
'views/slick_example_view.xml', |
|||
'security/ir.model.access.csv', |
|||
], |
|||
"demo": [ |
|||
'demo/slick_example_data.xml', |
|||
], |
|||
} |
34
web_widget_slick_example/demo/slick_example_data.xml
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,5 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2016-2017 LasLabs Inc. |
|||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). |
|||
|
|||
from . import slick_example |
@ -0,0 +1,16 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2016-2017 LasLabs Inc. |
|||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). |
|||
|
|||
from odoo import models, fields |
|||
|
|||
|
|||
class SlickExample(models.Model): |
|||
_name = 'slick.example' |
|||
_description = 'Slick Example Model' |
|||
|
|||
image_ids = fields.One2many( |
|||
name='Images', |
|||
comodel_name='ir.attachment', |
|||
inverse_name='res_id', |
|||
) |
@ -0,0 +1,2 @@ |
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink |
|||
slick_example_manager,slick.example.manager,web_widget_slick_example.model_slick_example,base.group_no_one,1,1,1,1 |
After Width: 128 | Height: 128 | Size: 9.2 KiB |
79
web_widget_slick_example/static/description/icon.svg
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,46 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<!-- Copyright 2016-2017 LasLabs Inc. |
|||
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). --> |
|||
|
|||
<odoo> |
|||
<record id="slick_example_view_form" model="ir.ui.view"> |
|||
<field name="name">slick.example.view.form</field> |
|||
<field name="model">slick.example</field> |
|||
<field name="arch" type="xml"> |
|||
<form string="Slick Example"> |
|||
<header /> |
|||
<sheet> |
|||
<group> |
|||
<field name="image_ids" widget="one2many_slick_images" |
|||
options="{'slidesToShow': 2}" /> |
|||
</group> |
|||
</sheet> |
|||
<footer /> |
|||
</form> |
|||
</field> |
|||
</record> |
|||
|
|||
<record id="slick_example_view_tree" model="ir.ui.view"> |
|||
<field name="name">slick.example.view.tree</field> |
|||
<field name="model">slick.example</field> |
|||
<field name="arch" type="xml"> |
|||
<tree string="Slick Example"> |
|||
<field name="id" /> |
|||
</tree> |
|||
</field> |
|||
</record> |
|||
|
|||
<record id="slick_example_action" model="ir.actions.act_window"> |
|||
<field name="name">Slick Examples</field> |
|||
<field name="res_model">slick.example</field> |
|||
<field name="type">ir.actions.act_window</field> |
|||
<field name="view_type">form</field> |
|||
<field name="view_mode">tree,form</field> |
|||
</record> |
|||
|
|||
<menuitem id="slick_example_menu" |
|||
name="Slick" |
|||
parent="base.menu_custom" |
|||
action="slick_example_action" |
|||
sequence="1" /> |
|||
</odoo> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue