f227203cca1ffaa2eb8bc11ce47d7b9dc2ce4729
[catagits/Reaction.git] / lib / Reaction / UI / ViewPort / Field / TimeRange.pm
1 package Reaction::UI::ViewPort::Field::TimeRange;
2
3 use Reaction::Class;
4 use Reaction::Types::DateTime qw(SpanSet);
5 use DateTime;
6 use DateTime::SpanSet;
7 use Time::ParseDate ();
8
9 use namespace::clean -except => [ qw(meta) ];
10 extends 'Reaction::UI::ViewPort::Field';
11
12
13
14 has '+value' => (isa => SpanSet);
15
16 #has '+layout' => (default => 'timerange');
17
18 has value_string =>
19   (isa => 'Str',  is => 'rw', lazy_fail => 1, trigger_adopt('value_string'));
20
21 has delete_label => (
22   isa => 'Str', is => 'rw', required => 1, default => sub { 'Delete' },
23 );
24
25 has parent => (
26   isa => 'Reaction::UI::ViewPort::TimeRangeCollection',
27   is => 'ro',
28   required => 1,
29   is_weak_ref => 1
30 );
31 sub _build_value_string {
32   my $self = shift;
33   #return '' unless $self->has_value;
34   #return $self->value_string;
35 };
36 sub value_array {
37   my $self = shift;
38   return split(',', $self->value_string);
39 };
40 sub adopt_value_string {
41   my ($self) = @_;
42   my @values = $self->value_array;
43   for my $idx (0 .. 3) { # last value is repeat
44     if (length $values[$idx]) {
45       my ($epoch) = Time::ParseDate::parsedate($values[$idx], UK => 1);
46       $values[$idx] = DateTime->from_epoch( epoch => $epoch );
47     }
48   }
49   $self->value($self->range_to_spanset(@values));
50 };
51 sub range_to_spanset {
52   my ($self, $time_from, $time_to, $repeat_from, $repeat_to, $pattern) = @_;
53   my $spanset = DateTime::SpanSet->empty_set;
54   if (!$pattern || $pattern eq 'none') {
55     my $span = DateTime::Span->from_datetimes(
56                  start => $time_from, end => $time_to
57                );
58     $spanset = $spanset->union( $span );
59   } else {
60     my $duration = $time_to - $time_from;
61     my %args = ( days => $time_from->day + 2,
62                 hours => $time_from->hour,
63               minutes => $time_from->minute,
64               seconds => $time_from->second );
65
66     delete $args{'days'} if ($pattern eq 'daily');
67     delete @args{qw/hours days/} if ($pattern eq 'hourly');
68     $args{'days'} = $time_from->day if ($pattern eq 'monthly');
69     my $start_set = DateTime::Event::Recurrence->$pattern( %args );
70     my $iter = $start_set->iterator( start => $repeat_from, end => $repeat_to );
71     while ( my $dt = $iter->next ) {
72       my $endtime = $dt + $duration;
73       my $new_span = DateTime::Span->from_datetimes(
74                        start => $dt,
75                        end => $endtime
76                      );
77       $spanset = $spanset->union( $new_span );
78     }
79   }
80   return $spanset;
81 };
82 sub delete {
83   my ($self) = @_;
84   $self->parent->remove_range_vp($self);
85 };
86
87 override accept_events => sub { ('value_string', 'delete', super()) };
88
89 __PACKAGE__->meta->make_immutable;
90
91
92 1;
93
94 =head1 NAME
95
96 Reaction::UI::ViewPort::Field::TimeRange
97
98 =head1 SYNOPSIS
99
100 =head1 DESCRIPTION
101
102 =head1 METHODS
103
104 =head2 value
105
106   Accessor for a L<DateTime::SpanSet> object.
107
108 =head2 value_string
109
110   Returns: Encoded range string representing the value.
111
112 =head2 value_array
113
114   Returns: Arrayref of the elements of C<value_string>.
115
116 =head2 parent
117
118   L<Reaction::UI::ViewPort::TimeRangeCollection> object.
119
120 =head2 range_to_spanset
121
122   Arguments: $self, $time_from, $time_to, $repeat_from, $repeat_to, $pattern
123   where $time_from, $time_to, $repeat_from, $repeat_to are L<DateTime>
124   objects, and $pattern is a L<DateTime::Event::Recurrence> method name
125
126   Returns: $spanset
127
128 =head2 delete
129
130   Removes TimeRange from C<parent> collection.
131
132 =head2 delete_label
133
134   Label for the delete option. Default: 'Delete'.
135
136 =head1 SEE ALSO
137
138 =head2 L<Reaction::UI::ViewPort::Field>
139
140 =head2 L<Reaction::UI::ViewPort::TimeRangeCollection>
141
142 =head1 AUTHORS
143
144 See L<Reaction::Class> for authors.
145
146 =head1 LICENSE
147
148 See L<Reaction::Class> for the license.
149
150 =cut