Skip to content

Commit

Permalink
- add mode with following mode supported:
Browse files Browse the repository at this point in the history
   - `in-place`: default value. widget is inserted after `host`.
   - `out-place`: widget is inserted under `document.body`.
   - `fixed`: widget is inserted under `document.body`, with fixed position style.
 - support `container` option for customized modal window.
  • Loading branch information
zbryikt committed Jun 30, 2022
1 parent 1bdbf5e commit fa4ad16
Show file tree
Hide file tree
Showing 12 changed files with 396 additions and 132 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
# Change Logs

## v0.0.8 (upcoming)
## v0.0.8

- proper handle invalid input
- add `mode` with following mode supported:
- `in-place`: default value. widget is inserted after `host`.
- `out-place`: widget is inserted under `document.body`.
- `fixed`: widget is inserted under `document.body`, with fixed position style.
- support `container` option for customized modal window.


## v0.0.7
Expand Down
22 changes: 19 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,25 @@ Constructor options:

- `host`: host element ( should be an input element, if provided )
- `time`: default true. hide and disable time picker if false.
- `fixed`: default false. true to enabled fixed mode, which:
- when toggled, the datetime widget pops up in screen center, and covers the whole screen.
- will be forced to true if `host` is not provided.
- `fixed`: (deprecated) default false. true to enabled fixed mode.
- `mode`: either `in-place`, `out-place` or `fixed`.
- `fixed`:
- when toggled, the datetime widget pops up in screen center, and covers the whole screen.
- will be forced to true if `host` and `container` are not provided.
- `out-place`: widget is inserted under body.
- `in-place`: widget is inserted after `host`.
- `container`: optional. if provided, `lddatetimepicker` are rendered barely in this container.
- this should be an object with following fields:
- `node`: required. container element.
- `toggle(v)`: required. function for toggling container on / off.
- `isOn()`: required. return true if a container is toggled on, otherwise false.
- `position({x,y,ix,iy})`: optional. use this function to correctly position container.
- `x`: suggested x position
- `y`: suggested y position
- `ix`: suggested x position if mode is `in-place`.
- `iy`: suggested y position if mode is `in-place`.
- by default, mode will be `out-place` with `container` option set.
- Set `mode` to `fixed` if the container is a fixed element (such as dialog)


## API
Expand Down
9 changes: 9 additions & 0 deletions dist/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@
box-shadow: 0 3px 5px rgba(0,0,0,0.1);
cursor: pointer;
}
.lddtp.bare {
border: 0;
box-shadow: none;
border-radius: 0;
}
.lddtp.static {
position: relative;
display: block;
}
.lddtp.active {
display: block;
}
Expand Down
82 changes: 68 additions & 14 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,33 @@
var html, lddatetimepicker;
html = '<div class="lddtp"><div>\n <div class="lddtp-h">\n <div class="lddtp-a" data-action="-"></div>\n <div class="lddtp-f"><select class="lddtp-month-sel"></select></div>\n <div class="lddtp-f"><input class="lddtp-year-sel" type="number"/></div>\n <div class="lddtp-a" data-action="+"></div>\n </div>\n <div class="lddtp-ds">\n </div>\n <div class="lddtp-t">\n <div class="lddtp-f"><select class="lddtp-hour-sel"></select></div>\n <div><b>:</b></div>\n <div class="lddtp-f"><select class="lddtp-minute-sel"></select></div>\n </div>\n</div></div>';
lddatetimepicker = function(opt){
var div, r, ref$, x$, _handler, e, this$ = this;
var ref$, _c, div, r, x$, _handler, e, this$ = this;
opt == null && (opt = {});
this.opt = opt;
this._enabled = {
time: !(opt.time != null) || opt.time
};
this._fixed = opt.fixed;
this._mode = (ref$ = opt.mode) === 'in-place' || ref$ === 'out-place' || ref$ === 'fixed'
? opt.mode
: opt.fixed
? 'fixed'
: opt.container ? 'out-place' : 'in-place';
this.evthdr = {};
if (_c = opt.container) {
if (!(typeof _c === 'object' && _c.isOn && _c.toggle && _c.node)) {
throw new Error("[lddatetimepicker] `isOn`, `toggle` and `node` are all required within `container` option.");
}
this._toggle = _c.toggle;
this.isOn = _c.isOn;
this._container = _c.node;
this._pos = _c.position;
}
this.hdr = {
mouseup: function(evt){
if (evt.target === this$.host) {
return;
}
this$.root.classList.toggle('active', false);
this$._toggle(false);
document.removeEventListener('mouseup', this$.hdr.mouseup);
return document.removeEventListener('keydown', this$.hdr.keydown);
},
Expand Down Expand Up @@ -58,7 +71,12 @@
}
div.innerHTML = html;
this.root = r = div.querySelector('.lddtp');
if (this._fixed || !this.host) {
if (this._container) {
this._container.appendChild(div);
this.root.classList.add('static', 'bare');
} else if (this._mode === 'out-place') {
document.body.appendChild(div);
} else if (this._mode === 'fixed' || !this.host) {
document.body.appendChild(div);
this.root.classList.toggle('fixed');
} else if (this.host) {
Expand Down Expand Up @@ -106,7 +124,7 @@
}).join('');
this.root.addEventListener('click', function(evt){
var n;
if (this$._fixed && evt.target.classList.contains('fixed')) {
if (this$._mode === 'fixed' && evt.target.classList.contains('fixed')) {
return this$.toggle(false);
}
n = evt.target;
Expand Down Expand Up @@ -189,13 +207,16 @@
isOn: function(){
return this.root.classList.contains('active');
},
_toggle: function(v){
return this.root.classList.toggle('active', v);
},
toggle: function(v){
var c, h, n, hb, cb, ref$, x, y, nscroll, nstack, countScroll, s, stackb, scrollb, scroll;
var c, h, n, hb, cb, ref$, x, y, nscroll, nstack, countScroll, s, stackb, scrollb, scroll, rscroll, _cb, vy, vx, iy, ix;
if (arguments.length === 0) {
v = !this.root.classList.contains('active');
v = !this.isOn();
}
if (!v) {
this.root.classList.toggle('active', false);
this._toggle(false);
document.removeEventListener('mouseup', this.hdr.mouseup);
document.removeEventListener('keydown', this.hdr.keydown);
return;
Expand All @@ -204,8 +225,8 @@
document.addEventListener('mouseup', this.hdr.mouseup);
document.addEventListener('keydown', this.hdr.keydown);
}
this.root.classList.toggle('active', true);
if (this._fixed) {
this._toggle(true);
if (this._mode === 'fixed') {
return;
}
c = this.root;
Expand Down Expand Up @@ -247,16 +268,49 @@
left: nscroll.scrollLeft,
top: nscroll.scrollTop
};
rscroll = this._mode === 'fixed'
? {
left: 0,
top: 0
}
: {
left: document.scrollingElement.scrollLeft,
top: document.scrollingElement.scrollTop
};
_cb = this._container ? this._container.getBoundingClientRect() : cb;
if (hb.y + hb.height + _cb.height > window.innerHeight + rscroll.top) {
vy = hb.y - _cb.height + rscroll.top - 2;
} else {
vy = hb.y + hb.height + rscroll.top + 2;
}
if (hb.x + _cb.width > window.innerWidth + rscroll.left) {
vx = hb.x + hb.width - +rscroll.left + _cb.width;
} else {
vx = hb.x + rscroll.left;
}
if (hb.y + hb.height + cb.height > scrollb.y + scrollb.height + scroll.top) {
y = hb.y - stackb.y - cb.height + (countScroll ? scroll.top : 0) - 2;
iy = hb.y - stackb.y - cb.height + (countScroll ? scroll.top : 0) - 2;
} else {
y = hb.y - stackb.y + hb.height + (countScroll ? scroll.top : 0) + 2;
iy = hb.y - stackb.y + hb.height + (countScroll ? scroll.top : 0) + 2;
}
if (hb.x + cb.width > scrollb.x + scrollb.width + scroll.left) {
x = hb.x - stackb.x + hb.width - cb.width + (countScroll ? scroll.left : 0);
ix = hb.x - stackb.x + hb.width - cb.width + (countScroll ? scroll.left : 0);
} else {
x = hb.x - stackb.x + (countScroll ? scroll.left : 0);
ix = hb.x - stackb.x + (countScroll ? scroll.left : 0);
}
if (this._pos) {
return this._pos({
x: vx,
y: vy,
vx: vx,
vy: vy,
ix: ix,
iy: iy
});
}
ref$ = this._mode === 'out-place'
? (ref$ = [vx, vy], x = ref$[0], y = ref$[1], ref$)
: [ix, iy], x = ref$[0], y = ref$[1];
c.style.transform = "translate(" + x + "px, " + y + "px)";
return ref$ = c.style, ref$.top = 0, ref$.left = 0, ref$;
function fn$(it){
Expand Down
2 changes: 1 addition & 1 deletion dist/index.min.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/index.min.js

Large diffs are not rendered by default.

57 changes: 44 additions & 13 deletions src/index.ls
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,19 @@ html = '''
lddatetimepicker = (opt = {})->
@opt = opt
@_enabled = time: !(opt.time?) or opt.time
@_fixed = opt.fixed
@_mode = if opt.mode in <[in-place out-place fixed]> => opt.mode
else if opt.fixed => \fixed #legacy
else if opt.container => \out-place
else \in-place
@evthdr = {}
if (_c = opt.container) =>
if !(typeof(_c) == \object and _c.is-on and _c.toggle and _c.node) =>
throw new Error("[lddatetimepicker] `isOn`, `toggle` and `node` are all required within `container` option.")
@ <<< {_toggle: _c.toggle, is-on: _c.is-on, _container: _c.node, _pos: _c.position}
@hdr =
mouseup: (evt) ~>
if evt.target == @host => return
@root.classList.toggle \active, false
@_toggle false
document.removeEventListener \mouseup, @hdr.mouseup
document.removeEventListener \keydown, @hdr.keydown
keydown: (evt) ~>
Expand All @@ -48,7 +55,12 @@ lddatetimepicker = (opt = {})->
@host.addEventListener \mouseup, (evt) ~> @toggle!
div.innerHTML = html
@root = r = div.querySelector '.lddtp'
if @_fixed or !@host =>
if @_container =>
@_container.appendChild div
@root.classList.add \static, \bare
else if @_mode == \out-place =>
document.body.appendChild div
else if @_mode == \fixed or !@host =>
document.body.appendChild div
@root.classList.toggle \fixed
else if @host => @host.parentNode.insertBefore div, opt.host.nextSibling
Expand Down Expand Up @@ -88,7 +100,7 @@ lddatetimepicker = (opt = {})->
@n.sel.minute.innerHTML = [0 to 59].map((m) -> """<option value="#m">#{(''+m).padStart(2,"0")}</option>""").join('')

@root.addEventListener \click, (evt) ~>
if @_fixed and evt.target.classList.contains(\fixed) => return @toggle false
if @_mode == \fixed and evt.target.classList.contains(\fixed) => return @toggle false
n = evt.target
if n.classList.contains \lddtp-d =>
@sel = dayjs new Date(n.date.year, n.date.month, n.date.date, @sel.hour!, @sel.minute!)
Expand Down Expand Up @@ -135,19 +147,20 @@ lddatetimepicker.prototype = Object.create(Object.prototype) <<< do
on: (n, cb) -> (if Array.isArray(n) => n else [n]).map (n) ~> @evthdr.[][n].push cb
fire: (n, ...v) -> for cb in (@evthdr[n] or []) => cb.apply @, v
is-on: -> @root.classList.contains \active
_toggle: (v) -> @root.classList.toggle \active, v
toggle: (v) ->
if arguments.length == 0 => v = !(@root.classList.contains \active)
if arguments.length == 0 => v = !@is-on!
if !v =>
@root.classList.toggle \active, false
@_toggle false
document.removeEventListener \mouseup, @hdr.mouseup
document.removeEventListener \keydown, @hdr.keydown
return
if !@is-on! =>
document.addEventListener \mouseup, @hdr.mouseup
document.addEventListener \keydown, @hdr.keydown
@root.classList.toggle \active, true

if @_fixed => return
@_toggle true
# in fixed mode, caller should position container.
if @_mode == \fixed => return

c = @root
h = @host
Expand Down Expand Up @@ -177,14 +190,32 @@ lddatetimepicker.prototype = Object.create(Object.prototype) <<< do
stackb = nstack.getBoundingClientRect!
scrollb = nscroll.getBoundingClientRect!
scroll = {left: nscroll.scrollLeft, top: nscroll.scrollTop}
# root scrolling. we dont have to consider it in fixed mode
rscroll = if @_mode == \fixed => {left: 0, top: 0}
else {left: document.scrollingElement.scrollLeft, top: document.scrollingElement.scrollTop}

_cb = if @_container => @_container.getBoundingClientRect! else cb
# vx / vy: viewport x and y. for fixed element to correctly position
if hb.y + hb.height + _cb.height > window.innerHeight + rscroll.top =>
vy = hb.y - _cb.height + rscroll.top - 2
else vy = hb.y + hb.height + rscroll.top + 2
if hb.x + _cb.width > window.innerWidth + rscroll.left =>
vx = hb.x + hb.width - + rscroll.left + _cb.width
else vx = hb.x + rscroll.left

# ix / iy: inside stacking context x and y.
if hb.y + hb.height + cb.height > scrollb.y + scrollb.height + scroll.top =>
y = hb.y - stackb.y - cb.height + (if count-scroll => scroll.top else 0) - 2
iy = hb.y - stackb.y - cb.height + (if count-scroll => scroll.top else 0) - 2
else
y = hb.y - stackb.y + hb.height + (if count-scroll => scroll.top else 0) + 2
iy = hb.y - stackb.y + hb.height + (if count-scroll => scroll.top else 0) + 2
if hb.x + cb.width > scrollb.x + scrollb.width + scroll.left =>
x = hb.x - stackb.x + hb.width - cb.width + (if count-scroll => scroll.left else 0)
ix = hb.x - stackb.x + hb.width - cb.width + (if count-scroll => scroll.left else 0)
else
x = hb.x - stackb.x + (if count-scroll => scroll.left else 0)
ix = hb.x - stackb.x + (if count-scroll => scroll.left else 0)
# _pos usually are for fixed element, so we by default use `x` and `y` for viewport x and y.
if @_pos => return @_pos {x: vx, y: vy, vx, vy, ix, iy}
[x, y] = if @_mode == \out-place => [x, y] = [vx, vy]
else [ix, iy]
c.style.transform = "translate(#{x}px, #{y}px)"
c.style <<< top: 0, left: 0

Expand Down
7 changes: 7 additions & 0 deletions src/index.styl
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@
background: #fff
box-shadow: 0 3px 5px rgba(0,0,0,.1)
cursor: pointer
.lddtp.bare
border: 0
box-shadow: none
border-radius: 0
.lddtp.static
position: relative
display: block
.lddtp.active
display: block
.lddtp.active.fixed
Expand Down
Loading

0 comments on commit fa4ad16

Please sign in to comment.